本帖最后由 艾琪 于 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;
- }
- }
复制代码 |
|