前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何自定义 JPA 的数据库命名策略

如何自定义 JPA 的数据库命名策略

作者头像
happyJared
修改2021-05-20 14:50:32
1.1K0
修改2021-05-20 14:50:32
举报
文章被收录于专栏:happyJared

本次示例的项目中,定义了如下两个实体:

代码语言:txt
复制
@Data
@Entity
public class Teacher {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "Name")
    private String name;

}
代码语言:txt
复制
@Data
@Entity
@Table(name = "teacher_class")
public class TeacherClass {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private Integer teacherId;

    @Column(name = "ClassName")
    private String className;

}

先来看看 JPA 默认的命名策略,我们可以显示配置如下:

代码语言:txt
复制
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false
  jpa:
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    show-sql: true
    hibernate:
      ddl-auto: create
      naming:
         # 1. 表名及字段全小写下划线分隔命名策略(默认)
         physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
代码语言:txt
复制
Hibernate: create table teacher (id integer not null auto_increment, name varchar(255), primary key (id)) engine=InnoDB
Hibernate: create table teacher_class (id integer not null auto_increment, class_name varchar(255), teacher_id integer, primary key (id)) engine=InnoDB

运行项目,通过数据库或输出的SQL语句,默认策略的表现为:表名及字段全小写,并以下划线分隔

此外,引入的 Hibernate 还提供了另外一种物理命名策略,先进行如下配置,再来观察结果:

代码语言:txt
复制
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false
  jpa:
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    show-sql: true
    hibernate:
      ddl-auto: create
      naming:
		 # 2. 物理命名策略,未定义 @Table 和 @Column 将以实体名和属性名作为表名及字段名
         physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
代码语言:txt
复制
Hibernate: create table Teacher (id integer not null auto_increment, Name varchar(255), primary key (id)) engine=InnoDB
Hibernate: create table teacher_class (id integer not null auto_increment, ClassName varchar(255), teacherId integer, primary key (id)) engine=InnoDB

以上表明,Hibernate 提供的命名策略是以实体名和属性名分别作为表名及字段名,但如果有定义 @Table@Column ,则以该属性值进行映射命名

当然,某些场景下,可以通过自定义命名策略来简化操作,或实现自身特定的业务,例如:假设需要为未定义 @Table 实体加上表前缀 tb_,或是以大写字母下划线来分隔定义字段(简单起见,以下配置并不直接实现 PhysicalNamingStrategy):

代码语言:txt
复制
public class CustomNamingStrategyConfig extends SpringPhysicalNamingStrategy {

    /**
     * 配置映射的数据表名
     *
     * @param name
     * @param jdbcEnvironment
     * @return
     */
    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        if (name == null) {
            return null;
        }
        // 实体名 or 自定义的@Tabel name属性值
        String text = name.getText();

        // 首字母大写(类名),实体未定义@Table, 为表名加上tb_前缀
        if (Character.isUpperCase(text.charAt(0))) {
            final String tb = "tb_";
            if (!text.contains(tb)) {
                text = "tb" + text;
            }

            StringBuilder builder = new StringBuilder(text.replace('.', '_'));
            for (int i = 1, maxLength = builder.length() - 1; i < maxLength; i++) {
                if (this.isUnderscoreRequired(builder.charAt(i - 1), builder.charAt(i), builder.charAt(i + 1))) {
                    builder.insert(i++, '_');
                }
            }
            return super.getIdentifier(builder.toString(), name.isQuoted(), jdbcEnvironment);
        } else {
            // 实体定义了@Table(name="xxx"),以name属性值为表名
            return super.getIdentifier(text, name.isQuoted(), jdbcEnvironment);
        }
    }

    /**
     * 判断是否前一个字符为小写字母,当前字符为大写字母,下一个字符为小写字母
     *
     * @param before
     * @param current
     * @param after
     * @return
     */
    private boolean isUnderscoreRequired(char before, char current, char after) {
        return Character.isLowerCase(before) && Character.isUpperCase(current) && Character.isLowerCase(after);
    }

    /**
     * 配置映射的字段名
     *
     * @param name
     * @param jdbcEnvironment
     * @return
     */
    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        // 实体的属性名 or 自定义的@Column name属性值
        String text = name.getText();

        if (Character.isUpperCase(text.charAt(0))) {
            // 大写字母下划线分隔命名策略,有在实体字段上自定义@Column(name="Xx_Xxx")
            return new Identifier(text, name.isQuoted());
        } else {
            // 常见的小写驼峰式命名策略
            return super.toPhysicalColumnName(name, jdbcEnvironment);
        }
    }

}

application.yml 中修改如下配置:

代码语言:txt
复制
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false
  jpa:
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    show-sql: true
    hibernate:
      ddl-auto: create
      naming:
        # 3. 自定义的命名策略
        physical-strategy: cn.mariojd.jpa.naming.config.CustomNamingStrategyConfig

启动项目,查看结果,符合以上预设的实现要求

代码语言:txt
复制
Hibernate: create table tb_teacher (id integer not null auto_increment, Name varchar(255), primary key (id)) engine=InnoDB
Hibernate: create table teacher_class (id integer not null auto_increment, ClassName varchar(255), teacher_id integer, primary key (id)) engine=InnoDB

示例源码

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档