打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Hibernate扩展_自定义OID生成器
本帖最后由 艾琪 于 2009-12-18 15:27 编辑
前言
背景:Hibernate是靠对象标识OID来区分对象的,Hibernate自带的标识(OID)生成器不足以满足需求,用户需要定义自己的对象标识生成器。
Hibernate(3.0)提供的标识生成器扩展相关接口:org.hibernate.id.IdentifierGenerator和org.hibernate.id.Configurable
参考资料:Hibernate官方网站的NativeHiloGenerator的实现。
方法与原理
用户实现指定标识生成器扩展接口IdentifierGenerator。org.hibernate.id.IdentifierGenerator是Hibernate提供的对象标识生成器的扩展接口,另外,如果需要在配置文件中加载一些用户定义的参数,则需要同时实现接口org.hibernate.id.Configurable。
在ORMapping文件中设定使用自定义的OID生成器。<id name="uid" column="id" type="long">
<generator class="gocom.identifier.DateSeqGenerator">
<param name="length">4</param>
</generator>
</id>
复制代码
Hibernate根据2中的配置,从ClassPath下装载扩展的Java Class,然后实例化该类,并调用其接口方法生成ID。Hibernate的主键生成器扩展是通过一个工厂类(设计模式)IdentifierGeneratorFactory实现的。其实现原理可以参看如下部分代码,public final class IdentifierGeneratorFactory {
private static final HashMap GENERATORS = new HashMap();
public static final String SHORT_CIRCUIT_INDICATOR = new String();
public static final String POST_INSERT_INDICATOR = new String();
static {
GENERATORS.put("uuid", UUIDHexGenerator.class);
GENERATORS.put("hilo", TableHiLoGenerator.class);
GENERATORS.put("assigned", Assigned.class);
GENERATORS.put("identity", IdentityGenerator.class);
GENERATORS.put("select", SelectGenerator.class);
GENERATORS.put("sequence", SequenceGenerator.class);
GENERATORS.put("seqhilo", SequenceHiLoGenerator.class);
GENERATORS.put("increment", IncrementGenerator.class);
GENERATORS.put("foreign", ForeignGenerator.class);
GENERATORS.put("guid", GUIDGenerator.class);
GENERATORS.put("uuid.hex", UUIDHexGenerator.class); //uuid.hex is deprecated
}
public static IdentifierGenerator create(String strategy, Type type, Properties params, Dialect dialect)
throws MappingException {
try {
Class clazz = getIdentifierGeneratorClass( strategy, dialect );
IdentifierGenerator idgen = (IdentifierGenerator) clazz.newInstance();
if (idgen instanceof Configurable) ( (Configurable) idgen).configure(type, params, dialect);
return idgen;
}
catch (Exception e) {
throw new MappingException("could not instantiate id generator", e);
}
}
public static Class getIdentifierGeneratorClass(String strategy, Dialect dialect) {
Class clazz = (Class) GENERATORS.get(strategy);
if ( "native".equals(strategy) ) clazz = dialect.getNativeIdentifierGeneratorClass();
try {
if (clazz==null) clazz = ReflectHelper.classForName(strategy);
}
catch (ClassNotFoundException e) {
throw new MappingException("could not interpret id generator strategy: " + strategy);
}
return clazz;
}
private IdentifierGeneratorFactory() {} //cannot be instantiated
}
复制代码
相关的接口
这里我们只介绍最基本的两个接口org.hibernate.id.IdentifierGenerator和org.hibernate.id.Configurable,其接口定义如下,
IdentifierGenerator接口定义:package org.hibernate.id;
import org.hibernate.HibernateException;
import org.hibernate.engine.SessionImplementor;
import java.io.Serializable;
public interface IdentifierGenerator {
/**
* The configuration parameter holding the entity name
*/
public static final String ENTITY_NAME = "entity_name";
/**
* Generate a new identifier.
* @param session
* @param object the entity or toplevel collection for which the id is being generated
*
* @return a new identifier
* @throws HibernateException
*/
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException;
}
复制代码
Configurable接口定义如下:package org.hibernate.id;
import java.util.Properties;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.type.Type;
public interface Configurable {
/**
* Configure this instance, given the value of parameters
* specified by the user as <param> elements.
* This method is called just once, following instantiation.
*
* @param params param values, keyed by parameter name
*/
public void configure(Type type, Properties params, Dialect d) throws MappingException;
}
复制代码
代码示例
这个示例里我们扩展的ID标识的规则是日期加当天的流水号,流水号的位数由输入的参数决定,本例中限定流水号的位数是4位。目标生成的OID例如:200603150099,代码如下
扩展标识生成器类DateSeqGenerator:package gocom.identifier;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.Properties;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.MappingException;
import net.sf.hibernate.dialect.Dialect;
import net.sf.hibernate.engine.SessionImplementor;
import net.sf.hibernate.id.Configurable;
import net.sf.hibernate.id.IdentifierGenerator;
import net.sf.hibernate.type.Type;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class DateSeqGenerator
implements IdentifierGenerator, Configurable {// logger
private static final Log log = LogFactory.getLog(DateSeqGenerator.class);      // reference to the underlying generator to which we delegate the work
private String currentDate = "";      //上次生成标识的日期前缀
private long day;      //下一个Sequence值
private long next;      //末位序号的位数限制
private int length = 4;      //从数据库获取当前最大标识的Sql语句
private String sql;   //标识返回值的JAVA类别
private Class returnClass;
/**
* Construct a new DateSeqGenerator
*/
public DateSeqGenerator() {
super();
}
/* (non-Javadoc)
* @see org.hibernate.id.IdentifierGenerator#generate(org.hibernate.engine.SessionImplementor, java.lang.Object)
*/
public Serializable generate(SessionImplementor session, Object object)            throws SQLException, HibernateException {
if (sql!=null) {
getNext( session );
}
long currentDay = getCurrentDay();
if(currentDay != day){
day = currentDay;
next = 1l;
}
return IdentifierGeneratorFactory.createNumber(day * Math.pow(10l,length) + (next++), returnClass);
}
/* (non-Javadoc)
* @see org.hibernate.id.Configurable#configure(org.hibernate.type.Type, java.util.Properties, org.hibernate.dialect.Dialect)
*/
public void configure(Type type, Properties params, Dialect dialect)throws MappingException     {
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 numLength = params.getProperty("length");
if(numLength == null)
length = 4;
else
length = Integer.parseInt(numLength);
String schema = params.getProperty(PersistentIdentifierGenerator.SCHEMA);
String catalog = params.getProperty(PersistentIdentifierGenerator.CATALOG);
returnClass = type.getReturnedClass();
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], d.getSchemaSeparator() ) );
if ( i<tables.length-1) buf.append(" union ");
}
if (tables.length>1) {
buf.insert(0, "( ").append(" ) ids_");
column = "ids_." + column;
}
sql = "select max(" + column + ") from " + buf.toString();
}
/**
* 从数据库检索已有数据标识的最大值
* @param session
*
* @return
*/
//从数据库获取已有数据的最大ID标识,确定下一标识的值
private void getNext( SessionImplementor session ) {
Connection conn = session.connection();
log.debug("fetching initial value: " + sql);
try {
PersistentIdentifierGenerator.SQL.debug(sql);
PreparedStatement st = conn.prepareStatement(sql);
ResultSet rs = null;
try {
rs = st.executeQuery();
if ( rs.next() ) {
next = rs.getLong(1);
if ( rs.wasNull())
next = 1l;
else{
day = Long.parseLong(next.substring(0,8)) + 1;
}
next = Long.parseLong((next + "").substring(8));
}
else {
next = 1l;
}
sql=null;
log.debug("first free id: " + next);
}
finally {
if (rs!=null) rs.close();
st.close();
}
}
catch (SQLException sqle) {
throw JDBCExceptionHelper.convert(
session.getFactory().getSQLExceptionConverter(),sqle,  "could not fetch initial value", sql);
}
}
/**
* 从数据库服务器获取当前日期
*
* @return long 数据库当前日期,日期格式为"yyyymmdd"
*/
private long getCurrentDay(){
String cDate = null;
/**此部分代码省略**/
/** 从数据库获取当前日期,返回格式为"yyyymmdd",“20060316”**/
return cDate;
}
}
复制代码
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Hibernate中关于ID配置
hibernate.dialect' must be set when no Connection available错误
Hibernate使用jdbc只能获取单字符问题
Hibernate入门之自己写的小例子的总结-JB--IT之博客
常用设计模式的应用场景
hibernate 初学
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服