分享

Hibernate扩展_自定义OID生成器

 hehffyy 2011-01-05
本帖最后由 艾琪 于 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生成器。
  1. <id name="uid" column="id" type="long">
  2.    <generator class="gocom.identifier.DateSeqGenerator">
  3.       <param name="length">4</param>
  4.    </generator>
  5. </id>
复制代码
Hibernate根据2中的配置,从ClassPath下装载扩展的Java Class,然后实例化该类,并调用其接口方法生成ID。Hibernate的主键生成器扩展是通过一个工厂类(设计模式)IdentifierGeneratorFactory实现的。其实现原理可以参看如下部分代码,
  1. public final class IdentifierGeneratorFactory {
  2.         private static final HashMap GENERATORS = new HashMap();

  3.         public static final String SHORT_CIRCUIT_INDICATOR = new String();
  4.         public static final String POST_INSERT_INDICATOR = new String();

  5.         static {
  6.                 GENERATORS.put("uuid", UUIDHexGenerator.class);
  7.                 GENERATORS.put("hilo", TableHiLoGenerator.class);
  8.                 GENERATORS.put("assigned", Assigned.class);
  9.                 GENERATORS.put("identity", IdentityGenerator.class);
  10.                 GENERATORS.put("select", SelectGenerator.class);
  11.                 GENERATORS.put("sequence", SequenceGenerator.class);
  12.                 GENERATORS.put("seqhilo", SequenceHiLoGenerator.class);
  13.                 GENERATORS.put("increment", IncrementGenerator.class);
  14.                 GENERATORS.put("foreign", ForeignGenerator.class);
  15.                 GENERATORS.put("guid", GUIDGenerator.class);
  16.                 GENERATORS.put("uuid.hex", UUIDHexGenerator.class); //uuid.hex is deprecated
  17.         }

  18.         public static IdentifierGenerator create(String strategy, Type type, Properties params, Dialect dialect) 
  19.         throws MappingException {
  20.                 try {
  21.                         Class clazz = getIdentifierGeneratorClass( strategy, dialect );
  22.                         IdentifierGenerator idgen = (IdentifierGenerator) clazz.newInstance();
  23.                         if (idgen instanceof Configurable) ( (Configurable) idgen).configure(type, params, dialect);
  24.                         return idgen;
  25.                 }
  26.                 catch (Exception e) {
  27.                         throw new MappingException("could not instantiate id generator", e);
  28.                 }
  29.         }

  30.         public static Class getIdentifierGeneratorClass(String strategy, Dialect dialect) {
  31.                 Class clazz = (Class) GENERATORS.get(strategy);
  32.                 if ( "native".equals(strategy) ) clazz = dialect.getNativeIdentifierGeneratorClass();
  33.                 try {
  34.                         if (clazz==null) clazz = ReflectHelper.classForName(strategy);
  35.                 }
  36.                 catch (ClassNotFoundException e) {
  37.                         throw new MappingException("could not interpret id generator strategy: " + strategy);
  38.                 }
  39.                 return clazz;
  40.         }

  41.         private IdentifierGeneratorFactory() {} //cannot be instantiated

  42. }
复制代码
相关的接口
这里我们只介绍最基本的两个接口org.hibernate.id.IdentifierGenerator和org.hibernate.id.Configurable,其接口定义如下,
IdentifierGenerator接口定义:
  1. package org.hibernate.id;
  2. import org.hibernate.HibernateException;
  3. import org.hibernate.engine.SessionImplementor;
  4. import java.io.Serializable;

  5. public interface IdentifierGenerator {

  6.     /**
  7.      * The configuration parameter holding the entity name
  8.      */
  9.     public static final String ENTITY_NAME = "entity_name";
  10.     
  11.         /**
  12.          * Generate a new identifier.
  13.          * @param session
  14.          * @param object the entity or toplevel collection for which the id is being generated
  15.          *
  16.          * @return a new identifier
  17.          * @throws HibernateException
  18.          */
  19.         public Serializable generate(SessionImplementor session, Object object) 
  20.         throws HibernateException;

  21. }
复制代码
Configurable接口定义如下:
  1. package org.hibernate.id;
  2. import java.util.Properties;
  3. import org.hibernate.MappingException;
  4. import org.hibernate.dialect.Dialect;
  5. import org.hibernate.type.Type;

  6. public interface Configurable {
  7.         /**
  8.          * Configure this instance, given the value of parameters
  9.          * specified by the user as <param> elements.
  10.          * This method is called just once, following instantiation.
  11.          *
  12.          * @param params param values, keyed by parameter name
  13.          */
  14.         public void configure(Type type, Properties params, Dialect d) throws MappingException;
  15. }
复制代码
代码示例
这个示例里我们扩展的ID标识的规则是日期加当天的流水号,流水号的位数由输入的参数决定,本例中限定流水号的位数是4位。目标生成的OID例如:200603150099,代码如下
扩展标识生成器类DateSeqGenerator:
  1. package gocom.identifier;

  2. import java.io.Serializable;
  3. import java.sql.SQLException;
  4. import java.util.Properties;

  5. import net.sf.hibernate.HibernateException;
  6. import net.sf.hibernate.MappingException;
  7. import net.sf.hibernate.dialect.Dialect;
  8. import net.sf.hibernate.engine.SessionImplementor;
  9. import net.sf.hibernate.id.Configurable;
  10. import net.sf.hibernate.id.IdentifierGenerator;
  11. import net.sf.hibernate.type.Type;

  12. import org.apache.commons.logging.Log;
  13. import org.apache.commons.logging.LogFactory;

  14. public class DateSeqGenerator 
  15. implements IdentifierGenerator, Configurable {// logger  
  16. private static final Log log = LogFactory.getLog(DateSeqGenerator.class);      // reference to the underlying generator to which we delegate the work  
  17. private String currentDate = "";      //上次生成标识的日期前缀  
  18. private long day;      //下一个Sequence值  
  19. private long next;      //末位序号的位数限制  
  20. private int length = 4;      //从数据获取当前最大标识的Sql语句  
  21. private String sql;   //标识返回值的JAVA类别  
  22. private Class returnClass;      
  23. /**
  24.    * Construct a new DateSeqGenerator
  25. */
  26.   public DateSeqGenerator() {
  27.         super();
  28.   }
  29.       /* (non-Javadoc)
  30.      * @see org.hibernate.id.IdentifierGenerator#generate(org.hibernate.engine.SessionImplementor, java.lang.Object)
  31.    */
  32.   public Serializable generate(SessionImplementor session, Object object)            throws SQLException, HibernateException {
  33.     if (sql!=null) {
  34.       getNext( session );
  35.     } 
  36.     long currentDay = getCurrentDay();
  37.     if(currentDay != day){
  38.       day = currentDay;
  39.       next = 1l;
  40.     }
  41.     return IdentifierGeneratorFactory.createNumber(day * Math.pow(10l,length) + (next++), returnClass);
  42.   }
  43.     /* (non-Javadoc)
  44.      * @see org.hibernate.id.Configurable#configure(org.hibernate.type.Type, java.util.Properties, org.hibernate.dialect.Dialect)
  45.      */
  46.     public void configure(Type type, Properties params, Dialect dialect)throws MappingException     {
  47.     String tableList = params.getProperty("tables");
  48.     if (tableList==null) tableList = params.getProperty(PersistentIdentifierGenerator.TABLES);
  49.     String[] tables = StringHelper.split(", ", tableList);
  50.     String column = params.getProperty("column");
  51.     if (column==null) column = params.getProperty(PersistentIdentifierGenerator.PK);
  52.     String numLength = params.getProperty("length");
  53.     if(numLength == null)
  54.       length = 4;
  55.     else
  56.       length = Integer.parseInt(numLength);
  57.      String schema = params.getProperty(PersistentIdentifierGenerator.SCHEMA);
  58.     String catalog = params.getProperty(PersistentIdentifierGenerator.CATALOG);
  59.     returnClass = type.getReturnedClass();
  60.       StringBuffer buf = new StringBuffer();
  61.     for ( int i=0; i<tables.length; i++ ) {
  62.       if (tables.length>1) {
  63.         buf.append("select ").append(column).append(" from ");
  64.       }
  65.       buf.append( Table.qualify(catalog, schema, tables[i], d.getSchemaSeparator() ) );
  66.       if ( i<tables.length-1) buf.append(" union ");
  67.     }
  68.     if (tables.length>1) {
  69.       buf.insert(0, "( ").append(" ) ids_");
  70.       column = "ids_." + column;
  71.     }
  72.       sql = "select max(" + column + ") from " + buf.toString();
  73.   }
  74.     /**
  75.    * 从数据库检索已有数据标识的最大值
  76.     * @param session
  77.    *
  78.    * @return 
  79.   */
  80.   //从数据库获取已有数据的最大ID标识,确定下一标识的值
  81.   private void getNext( SessionImplementor session ) {
  82.   Connection conn = session.connection();
  83.   log.debug("fetching initial value: " + sql);
  84.     try {
  85.     PersistentIdentifierGenerator.SQL.debug(sql);
  86.     PreparedStatement st = conn.prepareStatement(sql);
  87.     ResultSet rs = null;
  88.     try {
  89.       rs = st.executeQuery();
  90.       if ( rs.next() ) {
  91.         next = rs.getLong(1);
  92.         if ( rs.wasNull()) 
  93.           next = 1l;
  94.         else{
  95.           day = Long.parseLong(next.substring(0,8)) + 1;
  96.                               }
  97.           next = Long.parseLong((next + "").substring(8));
  98.         }
  99.       else {
  100.         next = 1l;
  101.       }
  102.       sql=null;
  103.       log.debug("first free id: " + next);
  104.     }
  105.     finally {
  106.       if (rs!=null) rs.close();
  107.         st.close();
  108.     }
  109.   }
  110.   catch (SQLException sqle) {
  111.     throw JDBCExceptionHelper.convert(
  112.            session.getFactory().getSQLExceptionConverter(),sqle,  "could not fetch initial value", sql);
  113.     }
  114.   }
  115.   /**
  116.    * 从数据库服务器获取当前日期
  117.    *
  118.    * @return long 数据库当前日期,日期格式为"yyyymmdd"
  119.   */
  120.   private long getCurrentDay(){
  121.     String cDate = null;
  122.     /**此部分代码省略**/
  123.     /** 从数据库获取当前日期,返回格式为"yyyymmdd",“20060316”**/
  124.     return cDate;
  125.   }
  126. }
复制代码

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多