Naming Strategy
Naming Strategy Interface
In previous chapters, we have introduced simple entity mapping and association mapping (one-to-one, many-to-one, one-to-many, many-to-many). From these contents, we understand that
-
@Table(name = "...")
can be used to explicitly specify the table name for an entity. -
@GeneratedValue(..., sequenceName = "...")
can be used to explicitly specify the sequence name required to generate the id (if using sequence generation strategy). -
@Column(name = "...")
can be used to explicitly specify the column name for ordinary columns. -
@JoinColumn(name = "...")
can be used to explicitly specify the column name for foreign key columns. -
@JoinTable(name = "...")
can be used to explicitly specify the join table name and all its column names for association properties based on join tables.
However, in order to improve development efficiency, it is impossible to use these annotations too much. In most cases, the default name deduction behavior should work, and these annotations should only be used in code in a few cases.
For a given class or property, how to automatically determine the identifier name in the database if the user does not use such annotations is called the naming strategy, which is a customizable Java interface:
package org.babyfish.jimmer.sql.meta;
import org.babyfish.jimmer.meta.ImmutableProp;
import org.babyfish.jimmer.meta.ImmutableType;
public interface DatabaseNamingStrategy {
String tableName(ImmutableType type);
String sequenceName(ImmutableType type);
String columnName(ImmutableProp prop);
String foreignKeyColumnName(ImmutableProp prop);
String middleTableName(ImmutableProp prop);
String middleTableBackRefColumnName(ImmutableProp prop);
String middleTableTargetRefColumnName(ImmutableProp prop);
}
Among them, ImmutableType
and ImmutableProp
are the types used by Jimmer metadata and can be intuitively understood by analogy with Class
and Field
in JVM reflection API.
The role of each method is:
-
tableName: Given an entity type, what is its table name?
-
sequenceName: Given an entity type whose ID growth strategy is sequence, what is its sequence name?
-
columnName: Given a non-associative property, what is its column name?
-
foreignKeyColumnName: Given an association property based on foreign key, what is its column name?
-
middleTableName: Given an association property based on join table, what is its join table name?
-
middleTableBackRefColumnName: Given an association property based on join table, what is the column name of the foreign key pointing to the current entity in the join table?
-
middleTableTargetRefColumnName: Given an association property based on join table, what is the column name of the foreign key pointing to the associated entity in the join table?
Default Naming Strategy
In most cases, developers do not need to implement this interface directly. Jimmer's built-in org.babyfish.jimmer.sql.runtime.DefaultDatabaseNamingStrategy
class already implements this interface.
The DefaultDatabaseNamingStrategy class has two static fields:
-
UPPER_CASE:
The generated database identifier names are all uppercase.
infoIf the user does not do any configuration, this is the default naming strategy for Jimmer.
-
LOWER_CASE:
The generated database identifier names are all lowercase.
Some databases, such as MySQL, can be configured for case sensitivity. So it is very likely that you will be handling a MySQL database that is configured to be case sensitive with most table and column names in lowercase. In this case, you need to override the default strategy with this strategy.
Even if neither UPPER_CASE
nor LOWER_CASE
meets your requirements and you need to implement your own strategy, you can also consider inheriting this default strategy instead of implementing from scratch.
Before introducing the behavior of the default strategy, let's first introduce a character transformation rule: snake case.
The so-called snake case refers to converting alternating case text into underscore-concatenated text, e.g. the snake case of class name BookStore
is BOOK_STORE
, and the snake case of property name firstName
is FIRST_NAME
.
Considering case issues, we define two functions u_snake
and l_snake
with the following behaviors:
u_snake("BookStore")
-> "BOOK_STORE"l_snake("BookStore")
-> "book_store"u_snake("firstName")
-> "FIRST_NAME"l_snake("firstName")
-> "first_name"
With the conventions of u_snake
and l_snake
, it is easy to describe the behavior of DefaultDatabaseNamingStrategy
:
ClassName below refers to the SimpleName of the Java class, not the QualifiedName.
UPPER_CASE
-
tableName
Rule:
u_snake(ClassName)
Example: BookStore -> BOOK_STORE
-
sequenceName
Rule:
u_snake(ClassName)
_ID_SEQExample: BookStore -> BOOK_STORE_ID_SEQ
-
columnName
Rule:
u_snake(ClassName)
Example: firstName -> FIRST_NAME
-
foreignKeyColumnName
Rule:
u_snake(ClassName)
_IDExample: parentNode -> PARENT_NODE_ID
-
middleTableName
Rule:
u_snake(SourceClassName)
_u_snake(TargetClassName)
_MAPPINGExample: Book::authors -> BOOK_AUTHOR_MAPPING
-
middleTableBackRefColumnName
Rule:
u_snake(SourceClassName)
_IDExample: Book::authors -> BOOK_ID
-
middleTableTargetRefColumnName
Rule:
u_snake(TargetClassName)
_IDExample: Book::authors -> AUTHOR_ID
LOWER_CASE
-
tableName
Rule:
l_snake(ClassName)
Example: BookStore -> book_store
-
sequenceName
Rule:
l_snake(ClassName)
_id_seqExample: BookStore -> book_store_id_seq
-
columnName
Rule:
l_snake(ClassName)
Example: firstName -> first_name
-
foreignKeyColumnName
Rule:
l_snake(ClassName)
_idExample: parentNode -> parent_node_id
-
middleTableName
Rule:
l_snake(SourceClassName)
_l_snake(TargetClassName)
_mappingExample: Book::authors -> book_author_mapping
-
middleTableBackRefColumnName
Rule:
l_snake(SourceClassName)
_idExample: Book::authors -> book_id
-
middleTableTargetRefColumnName
Rule:
l_snake(TargetClassName)
_idExample: Book::authors -> author_id
Override Strategy
Now let's demonstrate how to override the default DefaultDatabaseNamingStrategy.UPPER_CASE
with DefaultDatabaseNamingStrategy.LOWER_CASE
.
When using SpringBoot
- Java
- Kotlin
@Bean
public DatabaseNamingStrategy databaseNamingStrategy() {
return DefaultDatabaseNamingStrategy.LOWER_CASE;
}
@Bean
fun databaseNamingStrategy(): DatabaseNamingStrategy =
DefaultDatabaseNamingStrategy.LOWER_CASE
When not using SpringBoot
- Java
- Kotlin
JSqlClient sqlClient = JSqlClient
.newBuilder()
.setDatabaseNamingStrategy(
DefaultDatabaseNamingStrategy.LOWER_CASE
)
...Omit other configurations...
.build();
val sqlClient = newKSqlClient {
setDatabaseNamingStrategy(
DefaultDatabaseNamingStrategy.LOWER_CASE
)
...Omit other configurations...
}