场景描述:因老产品安全测试不能通过,要求第三方依赖组件升级。其中一项是hibernate3.2.7.ga 升级到hibernate5.2.18.Final,产品支持Oracle,Sqlserver,Mysql数据库,此文只记录主键生成策略的调整:从hilo调整为enhanced-table。
在hibernate-core-5.2.18.Final.jar
包org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory.DefaultIdentifierGeneratorFactory()
方法可看到hibernate5.2默认支持的所有主键生成策略
/**
* Constructs a new DefaultIdentifierGeneratorFactory.
*/
@SuppressWarnings("deprecation")
public DefaultIdentifierGeneratorFactory() {
register( "uuid2", UUIDGenerator.class );
register( "guid", GUIDGenerator.class ); // can be done with UUIDGenerator + strategy
register( "uuid", UUIDHexGenerator.class ); // "deprecated" for new use
register( "uuid.hex", UUIDHexGenerator.class ); // uuid.hex is deprecated
register( "assigned", Assigned.class );
register( "identity", IdentityGenerator.class );
register( "select", SelectGenerator.class );
register( "sequence", SequenceStyleGenerator.class );
register( "seqhilo", SequenceHiLoGenerator.class );
register( "increment", IncrementGenerator.class );
register( "foreign", ForeignGenerator.class );
register( "sequence-identity", SequenceIdentityGenerator.class );
register( "enhanced-sequence", SequenceStyleGenerator.class );
register( "enhanced-table", TableGenerator.class );
}
可以看到hibernate5删掉了对hilo主键生成策略的支持。seqhilo和hilo不一样,需要数据库支持序列,mysql不适用。
产品特性要求主键生成策略不能依赖数据库,所以想使用hibernate默认主键生成策略的可选项很窄。hibernate3的时候hilo基本满足此条件,升级到hibernate5之后需要找到一个平滑过渡的主键生成策略替代方案。
以下先回顾hibernate3的时候使用hilo高低位的情况,
hilo的hbm.xml配置:
<hibernate-mapping>
<class name="com.example.test.className" table="TEST_TABLE">
<id name="id" type="java.lang.Long">
<column name="ID" precision="10" scale="0" />
<generator class="hilo">
<param name="table">HIBERNATE_UNIQUE_KEY_TEST</param>
<param name="column">TEST_TABLE</param>
<param name="max_lo">1000</param>
</generator>
</id>
...
</class>
</hibernate-mapping>
同时需要在数据库中需要创建一张名叫HIBERNATE_UNIQUE_KEY_TEST的表,sqlserver 表结构语句如下
CREATE TABLE HIBERNATE_UNIQUE_KEY_TEST (
ID NUMERIC (10) NOT NULL,
TEST_TABLE NUMERIC (10) NULL,
TEST_TABLE1 NUMERIC (10) NULL,
TEST_TABLE2 NUMERIC (10) NULL,
TEST_TABLE3 NUMERIC (10) NULL,
PRIMARY KEY (ID)
)
hibernate3的hilo生成策略在sqlserver、mysql数据库下有个问题在此指出:
1、这个hi表(HIBERNATE_UNIQUE_KEY_TEST表)中只放一条数据,按列来记录各表的hi键
2、当多个hbm.xml文件使用同一个表(HIBERNATE_UNIQUE_KEY_TEST)来记录hi值时(如以上表结构中的TEST_TABLE1、TEST_TABLE2列)时,当其中一个hbm.xml对应的表(如TEST_TABLE表)在做频繁的写入大量数据操作时,会使用行级锁锁定行数据修改hi键,这样会影响到别的业务表操作hi键(如TEST_TABLE1、TEST_TABLE2表相关的insert、update语句过来后就会挂起),因为只有一行数据嘛。
避免这种问题出现的办法就是给这种频繁写数据的表单独创建hi表,表与表彼此之间获取主键互不影响。因产品中使用hilo策略的表过多,外加产品开发人员使用hilo时约定不是很清晰,数据库中会出现一堆为其提供hi值的只有一条数据的主键生成策略表,混在业务表中很不优雅。
升级hibernate到5以后发现官方把hilo这种id生成策略给直接摒弃掉了,一方面是惊讶hibernate版本之间兼容性的确较差;另一方面也暗自猜测,hilo生成策略的摒弃可能也和上面给出的原因有关,所以他们设计了新的主键生成策略enhanced-table来弥补这种缺陷。
enhanced-table的使用场景和hilo类似,下面是hibernate5中table主键生成策略的hbm.xml配置事例
<class name="com.example.test.className" table="TEST_TABLE">
<id name="id" type="java.lang.Long">
<column name="ID" precision="10" scale="0" />
<generator class="org.hibernate.id.enhanced.TableGenerator">
<param name="table_name">HIBERNATE_PK_TABLE</param>
<param name="value_column_name">GEN_VAL</param>
<param name="segment_column_name">GEN_PK</param>
<param name="segment_value">TEST_TABLE</param>
<param name="increment_size">1</param>
<param name="optimizer">pooled-lo</param>
</generator>
</id>
...
</class>
</hibernate-mapping>
hibernate5中enhanced-table主键生成策略也需要配合数据库表来使用,以下是表结构
CREATE TABLE HIBERNATE_PK_TABLE (
GEN_PK varchar(60) not NULL ,
GEN_VAL numeric(20,0) NULL
PRIMARY KEY (GEN_PK)
)
和hilo对比table解决了hilo存在的问题:一张HIBERNATE_PK_TABLE表可以按行记录n张表的主键,而不是一行数据按列记录多张表的hi键,所以行级锁就不会影响别的表获取主键了,所以也就不需要设计多张表来存储hi键了
以上简单记录,
后期在实战中使用enhanced-table主键生成策略有新认识的时候再做说明,希望对看到文章,想升级老项目老产品的hibernate的人有所帮助
参考:
https://www.cnblogs.com/linnuo/p/7085688.html
https://blog.csdn.net/qq_35551089/article/details/54861002
https://blog.csdn.net/sun_ting_chuan/article/details/84849053
https://www.cnblogs.com/zhi-leaf/p/6245677.html