hibernate中自定义主键生成器
自定义 hibernate 主键生成机制 http://walle1027.iteye.com/blog/1114824
org.hibernate.id.MultipleHiLoPerTableGenerator主键生成器
http://suzefeng8806.iteye.com/blog/923511
http://zhongrf.iteye.com/blog/972303
生成器@ TableGenerator
http://zpfadmin.iteye.com/blog/670319
将当前主键的值单独保存到一个数据库的表中,主键的值每次都是从指定的表中查询来获得,这种生成主键的方式也是很常用的。这种方法生成主键的策略可以适用于任何的数据库,不必担心不同数据库不兼容造成的问题。
使用以下SQL脚本创建一个表“tb_generator”,并插入两条数据,SQL脚本如下所示。
|
1 2 3 4 5 6 7 8 9 |
Sql代码 CREATE TABLE tb_generator ( id int(20) unsigned NOT NULL auto_increment, gen_name varchar(255) NOT NULL, gen_value int(20) NOT NULL, PRIMARY KEY (id) ) INSERT INTO tb_generator ( gen_name ,gen_value ) VALUES ( 'CUSTOMER_PK',1); INSERT INTO tb_generator ( gen_name ,gen_value ) VALUES ( 'CONTACT_PK',100); |
执行SQL语句后,表中的数据如图5.1所示。
图5.1 自动生成主键表tb_generator
现在有另外两个表customer和contact,它们每次新建记录时生成主键的值分别“CUSTOMER_PK”所对应的value值加 1,“CONTACT_PK”所对应的value值加1。
下面就来具体看一下如何来配置主键的生成策略,以配置“customer”表为例,步骤如下。
(1)在Entity标记主键的位置,指定主键生成策略为“GenerationType.TABLE”,具体设置如下。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Java代码 @Entity @Table(name = "customer") public final class CustomerEO implements java.io.Serializable { private Integer id; @Id @GeneratedValue(strategy = GenerationType.TABLE) public Integer getId() { return this.id; } public void setId(Integer id) { this.id = id; } } |
(2)指定生成主键策略的名称,例如这里命名为“customer_gen”。
|
1 2 3 4 5 6 |
Java代码 @Id @GeneratedValue(strategy = GenerationType.TABLE,generator="customer_gen") public Integer getId() { return this.id; } |
(3)使用@ TableGenerator标记定义表生成策略的具体设置,代码如下所示。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Java代码 @Id @GeneratedValue(strategy = GenerationType.TABLE,generator="customer_gen") @TableGenerator(name = "customer_gen", table="tb_generator", pkColumnName="gen_name", valueColumnName="gen_value", pkColumnValue="CUSTOMER_PK", allocationSize=1 ) public Integer getId() { return this.id; } |
这样,当通过以下代码创建新的实体后,表tb_generator中“CUSTOMER_PK”的value的值将自动加1,如图5.2所示。
|
1 2 3 4 5 |
Java代码 CustomerEO customer = new CustomerEO(); customer.setName("Janet"); customer.setShortName("Jane"); entityManager.persist(customer); |
图5.2 添加新数据后表tb_generator
(4)@TableGenerator标记用于设置主键使用数据表生成主键的策略,它的定义如下所示。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Java代码 @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface TableGenerator { String name(); String table() default ""; String catalog() default ""; String schema() default ""; String pkColumnName() default ""; String valueColumnName() default ""; String pkColumnValue() default ""; int initialValue() default 0; int allocationSize() default 50; UniqueConstraint[] uniqueConstraints() default {}; } |
在使用此@ TableGenerator标记时,需要注意以下几个问题。
l 该标记可以在类名、方法名、和属性名前。并且一旦在实体中标记,它不仅可以在本实体中使用,在其他的实体中也可以引用。它的作用范围是整个persist unit配置的实体类中。
例如以上的定义也可以写成:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Java代码 @Entity @Table(name = "customer") @TableGenerator(name = "customer_gen", table="tb_generator", pkColumnName="gen_name", valueColumnName="gen_value", pkColumnValue="CUSTOMER_PK", allocationSize=1 ) public class CustomerEO implements java.io.Serializable { …… } |
或者将其标注在ContactEO中,也是可以的。但建议标注在所作用的实体中,这样有助于方便查看。
l name属性表示该表主键生成策略的名称,它被引用在@GeneratedValue中设置的“generator”值中。
l table属性表示表生成策略所持久化的表名,例如,这里表使用的是数据库中的“tb_generator”。
l catalog属性和schema具体指定表所在的目录名或是数据库名。
l pkColumnName属性的值表示在持久化表中,该主键生成策略所对应键值的名称。例如在“tb_generator”中将“gen_name”作为主键的键值
l valueColumnName属性的值表示在持久化表中,该主键当前所生成的值,它的值将会随着每次创建累加。例如,在“tb_generator”中将“gen_value”作为主键的值
l pkColumnValue属性的值表示在持久化表中,该生成策略所对应的主键。例如在“tb_generator”表中,将“gen_name”的值为 “CUSTOMER_PK”。
l initialValue表示主键初识值,默认为0。
l allocationSize表示每次主键值增加的大小,例如设置成1,则表示每次创建新记录后自动加1,默认为50。为了降低标识生成时频繁操作数据库造成 的性能上的影响,实体标识生成的时候会一次性的获取多个实体标识,该属性设置的就是一次性获取实体标识的数目。该属性并不是必须设置的属性,如果开发者没有为该属性设置值,OpenJPA 容器将会使用默认值 50 。
l UniqueConstraint与@Table标记中的用法类似,请读者参阅5.2.1小节。
用一个简单示意图表示持久化主键表和表生成策略,如图所示。

hibernate中自定义主键生成器 原文:http://lin23871.iteye.com/blog/363062
背景:
Hibernate(目前使用的版本是3.2)中提供了多种生成主键的方式.在下面的文章中有列出来
[hibernate]Hibernate主键生成方式 Key Generator
然而当前的这么多种生成方式未必能满足我们的要求.
比如increment,可以在一个hibernate实例的应用上很方便的时候,但是在集群的时候就不行了.
再如 identity ,sequence ,native 是数据局提供的主键生成方式,往往也不是我们需要,而且在程序跨数据库方面也体现出不足.
还有基于算法的生成方式生成出来的主键基本都是字符串的.
我们现在需要一种生成方式:使用Long作为主键类型,自动增,支持集群.
那么我们需要自定义一个我们的主键生成器才能实现了.
实现代码:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
Java代码 package hibernate; import java.io.Serializable; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.dialect.Dialect; import org.hibernate.engine.SessionImplementor; import org.hibernate.id.Configurable; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.PersistentIdentifierGenerator; import org.hibernate.type.Type; public class IncrementGenerator implements IdentifierGenerator, Configurable { private static final Log log = LogFactory.getLog(IncrementGenerator.class); private Long next; private String sql; public Serializable generate(SessionImplementor session, Object object) throws HibernateException { if (sql!=null) { getNext( session.connection() ); } return next; } public void configure(Type type, Properties params, Dialect d) throws MappingException { String table = params.getProperty("table"); if (table==null) table = params.getProperty(PersistentIdentifierGenerator.TABLE); String column = params.getProperty("column"); if (column==null) column = params.getProperty(PersistentIdentifierGenerator.PK); String schema = params.getProperty(PersistentIdentifierGenerator.SCHEMA); sql = "select max("+column +") from " + ( schema==null ? table : schema + '.' + table ); log.info(sql); } private void getNext(Connection conn) throws HibernateException { try { PreparedStatement st = conn.prepareStatement(sql); ResultSet rs = st.executeQuery(); if ( rs.next() ) { next = rs.getLong(1) + 1; } else { next = 1l; } }catch(SQLException e) { throw new HibernateException(e); } finally { try{ conn.close(); }catch(SQLException e) { throw new HibernateException(e); } } } } |
配置:
在对应的hbm文件里面将id的配置如下:
|
1 2 3 4 |
Xml代码 <id name="id" type="long" column="id" > <generator class="hibernate.IncrementGenerator" /> </id> |
另一个主键生成方式的例子
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
Java代码 package com.mc.framework.hibernate.id; import java.io.Serializable; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.text.MessageFormat; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.dialect.Dialect; import org.hibernate.engine.SessionImplementor; import org.hibernate.id.Configurable; import org.hibernate.id.IdentifierGeneratorFactory; import org.hibernate.id.MultipleHiLoPerTableGenerator; import org.hibernate.id.PersistentIdentifierGenerator; import org.hibernate.mapping.Table; import org.hibernate.type.Type; import org.hibernate.util.PropertiesHelper; import org.hibernate.util.StringHelper; import com.htf.framework.util.ClassUtil; public class AutoadjustMultipleHiLoGenerator extends MultipleHiLoPerTableGenerator implements PersistentIdentifierGenerator,Configurable { private static final Log _log = LogFactory.getLog(AutoadjustMultipleHiLoGenerator.class); public static final int DEFAULT_START_VALUE = 0; private static final String START_VALUE = "start_value"; public static final int DEFAULT_MAX_LO = 10000; protected int startValue; protected String _query = null; protected String _insert = null; protected String _update = null; protected String _tableName = null; protected int _maxLo; protected long _hi = 0; protected int _lo; protected Class<?> _returnClass; private String selectMaxSQL = null; public Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException { // 这个结果是高位,保存在数据库中的值. int result = 0; int updateRows = 0; int nextInTable; // 表中ID的最大值 do { // SQL.debug(_query); PreparedStatement qps = conn.prepareStatement(_query); PreparedStatement ips = null; try { ResultSet rs = qps.executeQuery(); boolean isInitialized = rs.next(); // 没有初始化,则代表ID表中,没有该表主键的记录 if (!isInitialized) { nextInTable = this.getNext(conn); _log.debug("表中记录最大值为的下一个值 : " + nextInTable); int curVal = nextInTable - 1;// 当前值 if (curVal == 0) { return 0; } int tmp = curVal / (_maxLo + 1); result = tmp + 1; ips = conn.prepareStatement(_insert); ips.setInt(1, result); ips.execute(); } else { result = rs.getInt(1); } rs.close(); } catch (SQLException sqle) { _log.error("could not read or init a hi value", sqle); throw sqle; } finally { if (ips != null) { ips.close(); } qps.close(); } // sql = update; PreparedStatement ups = conn.prepareStatement(_update); try { ups.setInt(1, result + 1);// 新值 ups.setInt(2, result);// 旧值 updateRows = ups.executeUpdate(); } catch (SQLException sqle) { _log.error("could not update hi value in: " + _tableName, sqle); throw sqle; } finally { ups.close(); } } while (updateRows == 0); return new Integer(result); } public synchronized Serializable generate(SessionImplementor session, Object obj) throws HibernateException { // 低位会不断在内存中加一,如果当低位大于 "最大的低位" 时, //则从数据库中将高位加一,低位归零,再一次循环 if (_lo > _maxLo) { // 高位 int hival = ((Integer) doWorkInNewTransaction(session)).intValue(); _lo = (hival == 0) ? 1 : 0; _hi = hival * (_maxLo + 1); String msg = "高低位生成的ID的信息 : new hiValue=[{0}],maxLo=[{1}],lo=[{2}],hi=[{3}]"; _log.debug(MessageFormat.format(msg, hival, _maxLo, _lo, _hi)); } Number genedId = IdentifierGeneratorFactory.createNumber(_hi + _lo, _returnClass); _lo++; _log.debug("根据高低位生成的ID为: " + genedId); return genedId; } @Override public void configure(Type type, Properties params, Dialect dialect) throws MappingException { startValue = PropertiesHelper.getInt(START_VALUE, params, DEFAULT_START_VALUE); super.configure(type, params, dialect); _query = ClassUtil.getFieldValue(this, "query", String.class); _insert = ClassUtil.getFieldValue(this, "insert", String.class); _update = ClassUtil.getFieldValue(this, "update", String.class); _tableName = ClassUtil.getFieldValue(this, "tableName", String.class); // _maxLo = ClassUtil.getFieldValue(this, "maxLo", Integer.class); _maxLo = PropertiesHelper.getInt(MAX_LO, params, DEFAULT_MAX_LO); _lo = ClassUtil.getFieldValue(this, "lo", Integer.class); _returnClass = ClassUtil .getFieldValue(this, "returnClass", Class.class); configureForIncrement(type, params, dialect); } private void configureForIncrement(Type type, Properties params, Dialect dialect) { String tableList = params.getProperty("tables"); if (tableList == null) tableList = params.getProperty(PersistentIdentifierGenerator.TABLES); String[] tables = StringHelper.split(", ", tableList); String column = params.getProperty("column"); if (column == null) column = params.getProperty(PersistentIdentifierGenerator.PK); String schema = params.getProperty(PersistentIdentifierGenerator.SCHEMA); String catalog = params.getProperty(PersistentIdentifierGenerator.CATALOG); StringBuffer buf = new StringBuffer(); for (int i = 0; i < tables.length; i++) { if (tables.length > 1) { buf.append("select ").append(column).append(" from "); } buf.append(Table.qualify(catalog, schema, tables[i])); if (i < tables.length - 1) buf.append(" union "); } if (tables.length > 1) { buf.insert(0, "( ").append(" ) ids_"); column = "ids_." + column; } selectMaxSQL = "select max(" + column + ") from " + buf.toString(); } private int getNext(Connection conn) { long next; PreparedStatement st = null; ResultSet rs = null; try { st = conn.prepareStatement(selectMaxSQL); rs = st.executeQuery(); try { if (rs.next()) { next = rs.getLong(1) + 1; if (rs.wasNull()) next = 1; } else { next = 1; } selectMaxSQL = null; } finally { if (rs != null) { rs.close(); } if (st != null) { st.close(); } } } catch (SQLException sqle) { throw new RuntimeException(sqle); } return new Long(next).intValue(); } } |
