介绍
在我以前的文章中,我谈到了不同的数据库标识符策略。 这篇文章将比较最常见的替代主要关键策略:
- 身份
- 序列
- 表(序列)
身份
IDENTITY类型(包括在SQL:2003标准中)受以下支持:
- SQL服务器
- MySQL(AUTO_INCREMENT)
- DB2
- 数据库
IDENTITY生成器允许按需自动增加integer / bigint列。 增量过程发生在当前正在运行的事务之外,因此回滚可能最终丢弃已分配的值(可能会发生值差距)。
增量过程非常有效,因为它使用了数据库内部的轻量级锁定机制,而不是重量级的事务性过程粒度锁定。
唯一的缺点是我们无法在执行INSERT语句之前知道新分配的值。 这种限制阻碍了Hibernate采用的“事务后写”刷新策略。 因此,Hibernates使用IDENTITY生成器禁用对实体的JDBC批处理支持。
对于以下示例,我们将启用会话工厂JDBC批处理:
properties.put("hibernate.order_inserts", "true");
properties.put("hibernate.order_updates", "true");
properties.put("hibernate.jdbc.batch_size", "2");
让我们使用IDENTITY生成策略定义一个实体:
@Entity(name = "identityIdentifier")
public static class IdentityIdentifier {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;
}
持续存在5个实体:
doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session session) {for (int i = 0; i < 5; i++) {session.persist(new IdentityIdentifier());}session.flush();return null;}
});
将在另一个查询之后执行一个查询(不涉及JDBC批处理):
Query:{[insert into identityIdentifier (id) values (default)][]}
Query:{[insert into identityIdentifier (id) values (default)][]}
Query:{[insert into identityIdentifier (id) values (default)][]}
Query:{[insert into identityIdentifier (id) values (default)][]}
Query:{[insert into identityIdentifier (id) values (default)][]}
除了禁用JDBC批处理之外,IDENTITY生成器策略不适用于每个具体类继承模型的Table ,因为可能存在多个具有相同标识符的子类实体,并且基类查询最终将检索具有相同标识符的实体(甚至(如果属于不同类型)。
序列
SEQUENCE生成器(在SQL:2003标准中定义)受以下支持:
- 甲骨文
- SQL服务器
- PostgreSQL的
- DB2
- 数据库
SEQUENCE是一个数据库对象,它在每个连续的请求上生成增量整数。 SEQUENCES比IDENTIFIER列灵活得多,因为:
- SEQUENCE是无表的,并且可以将同一序列分配给多个列或表
- SEQUENCE可以预分配值以提高性能
- SEQUENCE可以定义一个增量步骤,使我们可以受益于“池化” Hilo算法
- SEQUENCE不会限制Hibernate JDBC批处理
- SEQUENCE不会限制Hibernate继承模型
让我们使用SEQUENCE生成策略定义一个实体:
@Entity(name = "sequenceIdentifier")
public static class SequenceIdentifier {@Id@GenericGenerator(name = "sequence", strategy = "sequence", parameters = {@org.hibernate.annotations.Parameter(name = "sequenceName", value = "sequence"),@org.hibernate.annotations.Parameter(name = "allocationSize", value = "1"),})@GeneratedValue(generator = "sequence", strategy=GenerationType.SEQUENCE)private Long id;
}
我使用了“序列”生成器,因为我不希望Hibernate代表我们选择SequenceHiLoGenerator或SequenceStyleGeneGenerator 。
添加5个实体:
doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session session) {for (int i = 0; i < 5; i++) {session.persist(new SequenceIdentifier());}session.flush();return null;}
});
生成以下查询:
Query:{[call next value for hibernate_sequence][]}
Query:{[call next value for hibernate_sequence][]}
Query:{[call next value for hibernate_sequence][]}
Query:{[call next value for hibernate_sequence][]}
Query:{[call next value for hibernate_sequence][]}
Query:{[insert into sequenceIdentifier (id) values (?)][1]} {[insert into sequenceIdentifier (id) values (?)][2]}
Query:{[insert into sequenceIdentifier (id) values (?)][3]} {[insert into sequenceIdentifier (id) values (?)][4]}
Query:{[insert into sequenceIdentifier (id) values (?)][5]}
该表中的插入是批量处理的,但是我们知道在插入实体之前有5个序列调用。 这可以通过使用HILO算法进行优化。
表(序列)
生成序列还有另一种与数据库无关的替代方法。 一个或多个表可用于保存标识符序列计数器。 但这意味着要牺牲写入性能来实现数据库的可移植性。
尽管IDENTITY和SEQUENCES没有事务,但是使用数据库表授权ACID来同步多个并发id生成请求。
通过使用行级锁定,这比IDENTITY或SEQUENCE生成器要高得多的成本,使之成为可能。
必须在单独的数据库事务中计算序列,这需要IsolationDelegate机制,该机制同时支持本地(JDBC)和全局(JTA)事务。
- 对于本地事务,它必须打开一个新的JDBC连接,因此对当前的连接池机制施加了更大的压力。
- 对于全局事务,它需要挂起当前正在运行的事务。 在生成序列值之后,必须恢复实际事务。 此过程有其自己的成本,因此可能会影响整体应用程序性能。
让我们使用TABLE生成策略定义一个Entity:
@Entity(name = "tableIdentifier")
public static class TableSequenceIdentifier {@Id@GenericGenerator(name = "table", strategy = "enhanced-table", parameters = {@org.hibernate.annotations.Parameter(name = "table_name", value = "sequence_table")})@GeneratedValue(generator = "table", strategy=GenerationType.TABLE)private Long id;
}
我使用了较新的“增强表”生成器,因为旧式“表”生成器已被弃用。
添加5个实体:
doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session session) {for (int i = 0; i < 5; i++) {session.persist(new TableSequenceIdentifier());}session.flush();return null;}
});
生成以下查询:
Query:{[select tbl.next_val from sequence_table tbl where tbl.sequence_name=? for update][default]}
Query:{[insert into sequence_table (sequence_name, next_val) values (?,?)][default,1]}
Query:{[update sequence_table set next_val=? where next_val=? and sequence_name=?][2,1,default]}
Query:{[select tbl.next_val from sequence_table tbl where tbl.sequence_name=? for update][default]}
Query:{[update sequence_table set next_val=? where next_val=? and sequence_name=?][3,2,default]}
Query:{[select tbl.next_val from sequence_table tbl where tbl.sequence_name=? for update][default]}
Query:{[update sequence_table set next_val=? where next_val=? and sequence_name=?][4,3,default]}
Query:{[select tbl.next_val from sequence_table tbl where tbl.sequence_name=? for update][default]}
Query:{[update sequence_table set next_val=? where next_val=? and sequence_name=?][5,4,default]}
Query:{[select tbl.next_val from sequence_table tbl where tbl.sequence_name=? for update][default]}
Query:{[update sequence_table set next_val=? where next_val=? and sequence_name=?][6,5,default]}
Query:{[insert into tableIdentifier (id) values (?)][1]} {[insert into tableIdentifier (id) values (?)][2]}
Query:{[insert into tableIdentifier (id) values (?)][3]} {[insert into tableIdentifier (id) values (?)][4]}
Query:{[insert into tableIdentifier (id) values (?)][5]}
表生成器允许JDBC批处理,但它依靠SELECT FOR UPDATE查询。 行级别锁定绝对比使用本机IDENTITY或SEQUENCE效率低。
因此,根据您的应用程序要求,您可以选择多个选项。 没有一个单一的获胜策略,每一个都有优点和缺点。
- 代码可在GitHub上获得 。
翻译自: https://www.javacodegeeks.com/2014/07/hibernate-identity-sequence-and-table-sequence-generator.html