扩展hibernate的主键生成策略 good

 

关键字: hibernate id uuid
Hibernate

的主键生成策略有好几种:

1) assigned

2) hilo

3) seqhilo

4) increment

5) identity

6) sequence

7) native

8) uuid.hex

9) uuid.string

10) foreign

一般而言,利用uuid.hex方式生成主键将提供最好的性能和数据库平台适

应性。另外由于常用的数据库,如Oracle、DB2、SQLServer、MySql 等,都提

供了易用的主键生成机制(Auto-Increase 字段或者Sequence)。我们可以在数

据库提供的主键生成机制上,采用generator-class=native的主键生成方式。

不过值得注意的是,一些数据库提供的主键生成机制在效率上未必最佳,

大量并发insert数据时可能会引起表之间的互锁。

数据库提供的主键生成机制,往往是通过在一个内部表中保存当前主键状

态(如对于自增型主键而言,此内部表中就维护着当前的最大值和递增量),

之后每次插入数据会读取这个最大值,然后加上递增量作为新记录的主键,之

后再把这个新的最大值更新回内部表中,这样,一次Insert操作可能导致数据

库内部多次表读写操作,同时伴随的还有数据的加锁解锁操作,这对性能产生

了较大影响。

因此,对于并发Insert要求较高的系统,推荐采用uuid.hex 作为主键生成

机制。

另外我们可以扩展Hibernate
的类来做自己的主键生成策略:


java 代码
 
  1. package
     com.gsta.eshore.framework.util.uid;  
  2. import
     java.io.Serializable;  
  3. import
     org.hibernate
    .engine.SessionImplementor;  
  4. import
     org.hibernate
    .id.AbstractUUIDGenerator;  
  5. import
     org.hibernate
    .id.Configurable;  
  6. import
     org.hibernate
    .id.IdentifierGenerator;  
  7. import
     java.util.Properties;  
  8. import
     org.hibernate
    .Hibernate
    ;  
  9. import
     org.hibernate
    .dialect.Dialect;  
  10. import
     org.hibernate
    .type.Type;  
  11. import
     org.hibernate
    .util.PropertiesHelper;  
  12. /**
     
  13.  * <b>uuid</b><br>
     
  14. * @author hwq
     
  15.  */
      
  16. public
     class
     UIDGenerator extends
     AbstractUUIDGenerator implements
     Configurable {  
  17.     private
     static
     long
     lastTime = System.currentTimeMillis();  
  18.     private
     static
     short
     lastCount = -32768
    ;  
  19.     private
     static
     Object mutex = new
     Object();  
  20.     private
     static
     long
     ONE_SECOND = 1000L;  
  21.     private
     String sep = ""
    ;  
  22.     public
     Serializable generate(SessionImplementor session, Object obj) {  
  23.         long
     l = 0L;  
  24.         short
     word0 = 0
    ;  
  25.         int
     i = 0
    ;  
  26.         synchronized
    (mutex)  
  27.         {  
  28.             if
    (lastCount == 32767
    )  
  29.             {  
  30.                 for
    (boolean
     flag = false
    ; !flag;)  
  31.                 {  
  32.                     l = System.currentTimeMillis();  
  33.                     if
    (l < lastTime + ONE_SECOND)  
  34.                     {  
  35.                         try
      
  36.                         {  
  37.                             Thread.currentThread();  
  38.                             Thread.sleep(ONE_SECOND);  
  39.                         }  
  40.                         catch
    (InterruptedException interruptedexception) { }  
  41.                     } else
      
  42.                     {  
  43.                         lastTime = l;  
  44.                         lastCount = -32768
    ;  
  45.                         flag = true
    ;  
  46.                     }  
  47.                 }  
  48.             } else
      
  49.             {  
  50.                 l = lastTime;  
  51.             }  
  52.             word0 = lastCount++;  
  53.             i = getHostUniqueNum();  
  54.         }  
  55.         String s = Integer.toString(i, 16
    ) + sep + Long.toString(l, 16
    ) + sep + Integer.toString(word0, 16
    );  
  56.         if
    (s.length() > 24
    )  
  57.             s = s.substring(s.length() - 24
    );  
  58.         return
     s;  
  59.     }  
  60.     public
     Serializable generate_old(SessionImplementor session, Object obj) {  
  61.         String name = obj.getClass().getName();  
  62.         return
     new
     StringBuffer(64
    )  
  63.             .append(name.substring(name.lastIndexOf('.'
    )+1
    )).append(sep)  
  64.             .append((short
    )getIP()).append(sep)  
  65.             .append(Math.abs((short
    )getJVM())).append(sep)  
  66.             .append(getCount())   
  67.             .toString();  
  68.     }  
  69.     private
     static
     int
     getHostUniqueNum()  
  70.     {  
  71.         return
     (new
     Object()).hashCode();  
  72.     }  
  73.     public
     void
     configure(Type type, Properties params, Dialect d) {  
  74.         sep = PropertiesHelper.getString("separator"
    , params, ""
    );  
  75.     }  
  76.     public
     static
     void
     main( String[] args ) throws
     Exception {  
  77.         Properties props = new
     Properties();  
  78.         props.setProperty("separator"
    ""
    );  
  79.         IdentifierGenerator gen = new
     UIDGenerator();  
  80.         ( (Configurable) gen ).configure(Hibernate
    .STRING, props, null
    );  
  81.         IdentifierGenerator gen2 = new
     UIDGenerator();  
  82.         ( (Configurable) gen2 ).configure(Hibernate
    .STRING, props, null
    );  
  83.         for
     ( int
     i=0
    ; i<10
    ; i++) {  
  84.             String id = (String) gen.generate(null
    , gen);  
  85.             System.out.println(id);  
  86.             String id2 = (String) gen2.generate(null
    , gen2);  
  87.             System.out.println(id2);  
  88.         }  
  89.     }  
  90. }  
这个类必须要扩展AbstractUUIDGenerator并实现Configurable接口,在generate方法中生成我们想要的主键。

hibernate
的配置文件中要做以下的配置:


xml 代码
 
  1. <
    id
     name
    ="id"
     type
    ="java.lang.String"
    >
      
  2.       <
    column
     name
    ="id"
     length
    ="24"
     />
      
  3.       <
    generator
     class
    ="com.gsta.eshore.framework.util.uid.UIDGenerator"
    >
    <
    param
     name
    ="separator"
    >
    -</
    param
    >
    </
    generator
    >
      
  4.   </
    id
    >
      
Generator的类要引用UIDGenerator,并且可以带参数生成主键,示例是根据时间,Ip等生成一个24位的字符串。这样做的灵活性大大提高了,提供了最好的性能和数据库平台适应性。

但是有时候我们在保存一条记录的时候是不能指定主键的,因为它的主键要来源于其他的表的主键,(hibernate
推荐用代理主键,但是有时候设计的时候没有用到)。这个时候的主键生成策略就要是assigned了。为了保持主键的连贯性,我总不能又用另外一种主键生成策略吧。

仿照上面的类,我们做一个生成24为随机字符串的类。


java 代码
 
  1. package
     com.gsta.eshore.framework.util.uid;  
  2. public
     class
     UID  
  3. {  
  4.     private
     static
     long
     lastTime = System.currentTimeMillis();  
  5.     private
     static
     short
     lastCount = -32768
    ;  
  6.     private
     static
     Object mutex = new
     Object();  
  7.     private
     static
     long
     ONE_SECOND = 1000L;  
  8.     public
     UID()  
  9.     {  
  10.     }  
  11.     public
     static
     String getUID()  
  12.     {  
  13.         long
     l = 0L;  
  14.         short
     word0 = 0
    ;  
  15.         int
     i = 0
    ;  
  16.         synchronized
    (mutex)  
  17.         {  
  18.             if
    (lastCount == 32767
    )  
  19.             {  
  20.                 for
    (boolean
     flag = false
    ; !flag;)  
  21.                 {  
  22.                     l = System.currentTimeMillis();  
  23.                     if
    (l < lastTime + ONE_SECOND)  
  24.                     {  
  25.                         try
      
  26.                         {  
  27.                             Thread.currentThread();  
  28.                             Thread.sleep(ONE_SECOND);  
  29.                         }  
  30.                         catch
    (InterruptedException interruptedexception) { }  
  31.                     } else
      
  32.                     {  
  33.                         lastTime = l;  
  34.                         lastCount = -32768
    ;  
  35.                         flag = true
    ;  
  36.                     }  
  37.                 }  
  38.             } else
      
  39.             {  
  40.                 l = lastTime;  
  41.             }  
  42.             word0 = lastCount++;  
  43.             i = getHostUniqueNum();  
  44.         }  
  45.         String s = Integer.toString(i, 16
    ) + "`"
     + Long.toString(l, 16
    ) + "`"
     + Integer.toString(word0, 16
    );  
  46.         if
    (s.length() > 24
    )  
  47.             s = s.substring(s.length() - 24
    );  
  48.         return
     s;  
  49.     }  
  50.     private
     static
     int
     getHostUniqueNum()  
  51.     {  
  52.         return
     (new
     Object()).hashCode();  
  53.     }  
  54.   
  55.    public
     static
     void
     main(String[] args) {  
  56.         for
     (int
     i = 0
    ; i < 100
    ; i++) {  
  57.             String uid=getUID();  
  58.             System.out.println(uid);  
  59.         }  
  60.         }  
  61. }  
在save一个实体的时候调用entity.setId(UID.getUID())。

呵呵,以后用hibernate
就不用烦用什么主键生成策略了,自己做一个。

<id name="id" type="java.lang.String"><column name="id" length="24"><id type="java.lang.String" name="id





http://onecan.javaeye.com/blog/93391


1) Assigned
主键由外部程序负责生成,无需Hibernate参与。
2) hilo
通过hi/lo 算法实现的主键生成机制,需要额外的数据库表保存主键生成历史状态。
3) seqhilo
与hilo 类似,通过hi/lo 算法实现的主键生成机制,只是主键历史状态保存在Sequence中,适用于支持Sequence的数据库,如Oracle。

4) increment
主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,之后每次需要生成主键的时候将此值加1作为主键。
这种方式可能产生的问题是:如果当前有多个实例访问同一个数据库,那么由于各个实例各自维护主键状态,不同实例可能生成同样的主键,从而造成主键重复异常。因此,如果同一数据库有多个实例访问,此方式必须避免使用。
5) identity
采用数据库提供的主键生成机制。如DB2、SQL Server、MySQL中的主键生成机制。
6) sequence
采用数据库提供的sequence 机制生成主键。如Oralce 中的Sequence。

7) native
由Hibernate根据底层数据库自行判断采用identity、hilo、sequence其中一种作为主键生成方式。
8) uuid.hex
由Hibernate基于128 位唯一值产生算法生成16 进制数值(编码后以长度32 的字符串表示)作为主键。
9) uuid.string
与uuid.hex 类似,只是生成的主键未进行编码(长度16)。在某些数据库中可能出现问题(如PostgreSQL)。
10) foreign
使用外部表的字段作为主键


使用外部表的字段作为主键。

一般而言,利用uuid.hex式生成主键将提供最好的性能和数据库平台适应性。

另外由于常用的数据库,如Oracle、DB2、SQLServer、MySql 等,都提供了易用的主键生成机制(Auto-Increase 字段或者Sequence)。我们可以在数据库提供的主键生成机制上,采用generator-class=native的主键生成方式。不过值得注意的是,一些数据库提供的主键生成机制在效率上未必最佳,大量并发insert数据时可能会引起表之间的互锁。

数据库提供的主键生成机制,往往是通过在一个内部表中保存当前主键状态(如对于自增型主键而言,此内部表中就维护着当前的最大值和递增量),之后每次插入数据会读取这个最大值,然后加上递增量作为新记录的主键,之后再把这个新的最大值更新回内部表中,这样,一次Insert操作可能导致数据库内部多次表读写操作,同时伴随的还有数据的加锁解锁操作,这对性能产生了较大影响


因此,对于并发Insert要求较高的系统,推荐采用uuid.hex 作为主键生成机制。

如果需要采用定制的主键产生算法,则在此处配置主键生成器,主键生成器必须实现 net.sf.hibernate.id.IdentifierGenerator 接口。

例如:我们可以写一个自己的主键生成类MyIdentifierGenerator 并且实现public Serializable generate(SessionImplementor session, Object object)
throws HibernateException;方法,代码可能是这个样子:

public class MyIncrementGenerator implements IdentifierGenerator, Configurable {

private static final Log log = LogFactory.getLog(MyIncrementGenerator.class);

private long next;
private String sql;

public synchronized Serializable generate(SessionImplementor session, Object object)
throws SQLException, HibernateException {
if (sql!=null) {
//获得下一个主键的编号,可以自己定义
getNext( session.connection() );
}
return String.valueOf(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);
returnClass = type.getReturnedClass();

sql = "select max(to_number(" + column + ")) from " + ( schema==null ? table : schema + '.' + table );
}


private void getNext(Connection conn) throws SQLException {
PreparedStatement st = conn.prepareStatement(sql);
ResultSet rs = null;
文章出处:DIY部落(http://www.diybl.com/course/3_program/java/javajs/20090226/156186.html#)

  • 采用hibernate主键生成策略,就可以比较灵活和方便的对表中的主键字段进行操作了。而且,不同的数据库,不同的主键形式,也只需要修改下映射文件就可以了。
  • 今天学习到了关于Hibernate主键生成策略的问题,总结下,不足之处,请大家指出。

    Hibernate为优秀的持久层框架的代表。在传统的JDBC+JavaBean操作中,实体对象都由程序员自己去封装,然后返回。而在Hibernate中,采用对象关系映射『ORM』,大大简化了对数据库的操作.

    在数据库的设计和操作中,我们通常会给表建立主键。主键,可以分为自然主键和代理主键。

    自然主键表示:采用具有业务逻辑含义的字段作为表的主键。比如在用户信息表中,采用用户的身份证号码作为主键。但是这样一来,随着业务逻辑的变化,主键就有可能要更改。比如,假设哪天身份证号码升级成19,2位,那。。。。。。。

    代理主键:在表中人为的增加一个字段,该字段并没有表示任何的业务逻辑,仅仅用来标识一行数据。比如说在用户信息表中,增加一个用户ID的字段。用来表示该条用户信息的记录。

    通常情况下,用的比较多的是代理主键的形式。而且,我们习惯于于让该主键字段能够自动增长,来保证其唯一性。但是,不同的数据库自动增长的方式并不是相同的。如在SQLSERVER中,用identity,MYSQL中,有increment,ORACLE中通常采用sequence。这样一来,在数据库的主键列操作上,便会显得比较麻烦。

    但是在Hibernate中,提供了Hibernate主键生成策略。下面是比较常用的几种:

    1:assigned

    表示在新增数据时由应用程序指定主键的值。主要针对主键是采用自然主键的形式。这种方式,适用于主键列不是自动增长列。

    其缺点为在执行新增操作时,需查询数据库判断生成的主键是否已经存在。

    2:increment

    表示新增数据操作时由hibernate自动生成主键值。其生成的值为:先查询该主键列的最大值,然后在最大值的基础上加上1.适用于采用代理主键形式的主键列。同样不能用于主键列是自动增长的表。但是,该主键生成策略也有些缺点。

    (1)新增数据前先查询一遍,影响了性能。
    (2)主键的类型只能为数值型的int或者long
    (3)并发操作时的冲突问题。

    3:identity

    不如说是为sqlerver数据库量身定做的。主要适用于sqlserver数据库的自动增长列的表。

    4:native

    表示根据不同的数据库采用不同的Hibernate主键生成策略。比如,当前数据库为sqlserver,则会采用identity,如为oracle,则采用

    oracle中的sequence等。区分数据库的不同是以hibernate主配置文件中sessionFactory中配置的数据库方言。

    Xml代码

        
        
    1. <id name="实体类属性名" type="java.lang.Integer">    
    2.       <column name="对应表中主键字段名" />    
    3.       <generator class="assiged|increment|identity|native|........" />    
    4. </id>   

    采用hibernate主键生成策略,就可以比较灵活和方便的对表中的主键字段进行操作了。而且,不同的数据库,不同的主键形式,也只需要修改下映射文件就可以了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值