Hibernate 自定义表名映射
问题描述
Hibernate
映射介绍
Hibernate
中,默认的生成关系是将我们驼峰命名的实体进行拼接下划线同时转小写。
这种情况我们可以接受,默认的设置很规范。
但是这样,我们在实体之上声明了@Table
注解,并说我们的表名是Mandatory_Instrument_Apply
,但是Hibernate
还是将我们的数据表映射为小写加下划线的形式。这种情况看起来就有些不合理了。
业务需求
因为需要兼容老项目,老项目的数据表命名不很很规范,所以需要用最小的成本实现数据库的兼容。
所以设计的表名映射格式为,如果不加@Table
注解,则将实体名按照Hibernate
默认的生成规则进行生成,如果加了@Table
注解,则填写的name
就作为表名映射,不进行任何处理。
功能实现
入门
抛出来一个问题,无从下手。
打开浏览器,看看有没有前人的经验,Google
来Google
去发现找不着啥有价值的信息。但是在StackOverflow
上找到一篇引人思索的问题。
Spring boot JPA insert in TABLE with uppercase name with Hibernate - StackOverflow
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
将这个配置声明为org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
,就能实现大写的转换。
试试再说:
将配置按该问题的回答进行修改:
测试一番:
加了@Table
注解的,是我们想要的配置,直接映射。那不加注解的呢?
这个又不是我们想要的了,这个注解应该是直接将注解中的名或实体名映射到数据表,不做任何修改。
发现新大陆
正当一筹莫展之时,再去看一下配置,有了新的领悟。
org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
,这个配置的不就是一个实现类吗?我是不是也可以写一个类然后将我写的类配置上呢?
点进去,发现不过是一个实现了PhysicalNamingStrategy
与Serializable
两个接口的类。
看实现的toPhysicalTableName
方法,应该就是生成数据表名的方法。直接将name
返回,这就和我们之前猜想的一致,这个配置是直接将@Table
或实体名映射到数据表。
YunzhiNamingStrategy
建立配置类YunzhiNamingStrategy.java
,云智命名策略,分别实现上述PhysicalNamingStrategyStandardImpl
实现的两个接口。同时实现接口中声明的方法。
package com.mengyunzhi.demo.config; import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.PhysicalNamingStrategy; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import java.io.Serializable; /** * @author zhangxishuo on 2018/6/15 * 云智命名策略 * 实体与数据表名的关系配置 */ public class YunzhiNamingStrategy implements PhysicalNamingStrategy, Serializable { @Override public Identifier toPhysicalCatalogName(Identifier identifier, JdbcEnvironment jdbcEnvironment) { return null; } @Override public Identifier toPhysicalSchemaName(Identifier identifier, JdbcEnvironment jdbcEnvironment) { return null; } @Override public Identifier toPhysicalTableName(Identifier identifier, JdbcEnvironment jdbcEnvironment) { return null; } @Override public Identifier toPhysicalSequenceName(Identifier identifier, JdbcEnvironment jdbcEnvironment) { return null; } @Override public Identifier toPhysicalColumnName(Identifier identifier, JdbcEnvironment jdbcEnvironment) { return null; } }
然后将该项配置修改为我们自己建立的实现类。
兼容原配置项
因为该接口中有多个配置项,如:数据库名、字段名等,我们只想修改实体到数据表的命名策略,所以我们找到了另一个实现PhysicalNamingStrategy
命名策略的实现类:SpringPhysicalNamingStrategy
。
这就是我们第一次演示的策略,无论添不添加@Table
注解,都会映射到小写的加下划线的表名。
同时这里的字段映射为小写下划线我们是需要保留的,为了代码的复用,我们用到了面向对象的继承大法。
package com.mengyunzhi.demo.config; import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy; /** * @author zhangxishuo on 2018/6/15 * 云智命名策略 * 实体与数据表名的关系配置 */ public class YunzhiNamingStrategy extends SpringPhysicalNamingStrategy { }
我们对父类中的toPhysicalTableName
方法不满意,Command + N
,重写父类方法。
package com.mengyunzhi.demo.config; import org.hibernate.boot.model.naming.Identifier; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy; /** * @author zhangxishuo on 2018/6/15 * 云智命名策略 * 实体与数据表名的关系配置 */ public class YunzhiNamingStrategy extends SpringPhysicalNamingStrategy { @Override public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) { return super.toPhysicalTableName(name, jdbcEnvironment); } }
注解判断
我们可以开始我们的逻辑了。
有@Table
注解的,就按@Table
中的名称中,否则就按Spring
默认的父类走。
但是问题出现了,Identifier
中给我们的名称就是已经处理好的名称。
Identifier
假如这么写:
@Entity public class MandatoryInstrumentApply { }
那我们的Identifier
中的text
值就是MandatoryInstrumentApply
。
@Entity @Table(name = "Mandatory_Instrument_Apply") public class MandatoryInstrumentApply { }
那我们的Identifier
中的text
值就是Mandatory_Instrument_Apply
。
Hibernate
是把应该处理好的名称告诉我们,但是不会告诉我们这个名称是实体的名还是在注解上获取的。
解决方案
package com.mengyunzhi.demo.config; import org.hibernate.boot.model.naming.Identifier; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy; import javax.persistence.Table; /** * @author zhangxishuo on 2018/6/15 * 云智命名策略 * 实体与数据表名的关系配置 */ public class YunzhiNamingStrategy extends SpringPhysicalNamingStrategy { // 定义包名 private static final String packageName = "com.mengyunzhi.demo.entity."; /** * 重写父类生成表名的方法 */ @Override public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) { try { // 获取实体类 Class entityClass = Class.forName(packageName + name.getText()); // 判断类上是否有Table注解 Boolean hasAnnotation = entityClass.isAnnotationPresent(Table.class); // 存在Table注解 if (hasAnnotation) { // 获取Table注解实例 Table table = (Table) entityClass.getAnnotation(Table.class); // 如果注解中的name字段不为空 if (!table.name().equals("")) { // 不对名称进行处理 return name; } } // 表示这是一个类名,按父类操作进行处理 return super.toPhysicalTableName(name, jdbcEnvironment); } catch (ClassNotFoundException e) { // 找不到实体类,说明肯定是@Table注解中的名称 return name; } } }
测试