問題描述
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;
}
}
}
測試