hibernate中自定义主键生成器

自定义 hibernate 主键生成机制 [url]http://walle1027.iteye.com/blog/1114824[/url]
org.hibernate.id.MultipleHiLoPerTableGenerator主键生成器
[url]http://suzefeng8806.iteye.com/blog/923511[/url]
[url]http://zhongrf.iteye.com/blog/972303[/url]

[b][color=red]生成器@ TableGenerator[/color][/b]
[url]http://zpfadmin.iteye.com/blog/670319[/url]
将当前主键的值单独保存到一个数据库的表中,主键的值每次都是从指定的表中查询来获得,这种生成主键的方式也是很常用的。这种方法生成主键的策略可以适用于任何的数据库,不必担心不同数据库不兼容造成的问题。
使用以下SQL脚本创建一个表“tb_generator”,并插入两条数据,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”表为例,步骤如下。
[color=darkblue](1)在Entity标记主键的位置[/color],指定主键生成策略为“GenerationType.TABLE”,具体设置如下。
@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;
}
}

[color=darkblue](2)指定生成主键策略的名称[/color],例如这里命名为“customer_gen”。
         @Id
@GeneratedValue(strategy = GenerationType.TABLE,generator="customer_gen")
public Integer getId() {
return this.id;
}

[color=darkblue](3)使用@ TableGenerator标记[/color]定义表生成策略的具体设置,代码如下所示。
         @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所示。
             CustomerEO customer = new CustomerEO();
customer.setName("Janet");
customer.setShortName("Jane");
entityManager.persist(customer);


图5.2 添加新数据后表tb_generator
[color=darkblue](4)@TableGenerator标记用于设置主键使用数据表生成主键的策略[/color],它的定义如下所示。
@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 [color=red]该标记可以在类名、方法名、和属性名前。并且一旦在实体中标记,它不仅可以在本实体中使用,在其他的实体中也可以引用。它的作用范围是整个persist unit配置的实体类中[/color]。
例如以上的定义也可以写成:
@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 [b]name[/b]属性表示该表主键生成策略的名称,它被引用在@GeneratedValue中设置的“generator”值中。
l [b]table[/b]属性表示表生成策略所持久化的表名,例如,这里表使用的是数据库中的“tb_generator”。
l [b]catalog[/b]属性和schema具体指定表所在的目录名或是数据库名。
l [b]pkColumnName[/b]属性的值表示在持久化表中,该主键生成策略所对应键值的名称。例如在“tb_generator”中将“gen_name”作为主键的键值
l [b]valueColumnName[/b]属性的值表示在持久化表中,该主键当前所生成的值,它的值将会随着每次创建累加。例如,在“tb_generator”中将“gen_value”作为主键的值
l [b]pkColumnValue[/b]属性的值表示在持久化表中,该生成策略所对应的主键。例如在“tb_generator”表中,将“gen_name”的值为 “CUSTOMER_PK”。
l [b]initialValue[/b]表示主键初识值,默认为0。
l [b]allocationSize[/b]表示每次主键值增加的大小,例如设置成1,则表示每次创建新记录后自动加1,默认为50。[b][color=red]为了降低标识生成时频繁操作数据库造成 的性能上的影响,实体标识生成的时候会一次性的获取多个实体标识,该属性设置的就是一次性获取实体标识的数目。[/color][/b]该属性并不是必须设置的属性,如果开发者没有为该属性设置值,OpenJPA 容器将会使用默认值 50 。
l [b]UniqueConstraint[/b]与@Table标记中的用法类似,请读者参阅5.2.1小节。
用一个简单示意图表示持久化主键表和表生成策略,如图所示。
[img]https://p-blog.csdn.net/images/p_blog_csdn_net/EJB_JPA/5.3.jpg[/img]


[color=red][b]hibernate中自定义主键生成器[/b][/color] 原文:[url]http://lin23871.iteye.com/blog/363062[/url]
背景:
Hibernate(目前使用的版本是3.2)中提供了多种生成主键的方式.在下面的文章中有列出来
[hibernate]Hibernate主键生成方式 Key Generator

然而当前的这么多种生成方式未必能满足我们的要求.
比如increment,可以在一个hibernate实例的应用上很方便的时候,但是在集群的时候就不行了.
再如 identity ,sequence ,native 是数据局提供的主键生成方式,往往也不是我们需要,而且在程序跨数据库方面也体现出不足.
还有基于算法的生成方式生成出来的主键基本都是字符串的.

我们现在需要一种生成方式:使用Long作为主键类型,自动增,支持集群.
那么我们需要自定义一个我们的主键生成器才能实现了.

实现代码:
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的配置如下:
<id name="id" type="long" column="id" >  
<generator class="hibernate.IncrementGenerator" />
</id>



另一个主键生成方式的例子
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();
}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值