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 代码
这个类必须要扩展AbstractUUIDGenerator并实现Configurable接口,在generate方法中生成我们想要的主键。
- package
com.gsta.eshore.framework.util.uid;
- import
java.io.Serializable;
- import
org.hibernate
.engine.SessionImplementor;
- import
org.hibernate
.id.AbstractUUIDGenerator;
- import
org.hibernate
.id.Configurable;
- import
org.hibernate
.id.IdentifierGenerator;
- import
java.util.Properties;
- import
org.hibernate
.Hibernate
;
- import
org.hibernate
.dialect.Dialect;
- import
org.hibernate
.type.Type;
- import
org.hibernate
.util.PropertiesHelper;
- /**
- * <b>uuid</b><br>
- * @author hwq
- */
- public
class
UIDGenerator extends
AbstractUUIDGenerator implements
Configurable {
- private
static
long
lastTime = System.currentTimeMillis();
- private
static
short
lastCount = -32768
;
- private
static
Object mutex = new
Object();
- private
static
long
ONE_SECOND = 1000L;
- private
String sep = ""
;
- public
Serializable generate(SessionImplementor session, Object obj) {
- long
l = 0L;
- short
word0 = 0
;
- int
i = 0
;
- synchronized
(mutex)
- {
- if
(lastCount == 32767
)
- {
- for
(boolean
flag = false
; !flag;)
- {
- l = System.currentTimeMillis();
- if
(l < lastTime + ONE_SECOND)
- {
- try
- {
- Thread.currentThread();
- Thread.sleep(ONE_SECOND);
- }
- catch
(InterruptedException interruptedexception) { }
- } else
- {
- lastTime = l;
- lastCount = -32768
;
- flag = true
;
- }
- }
- } else
- {
- l = lastTime;
- }
- word0 = lastCount++;
- i = getHostUniqueNum();
- }
- String s = Integer.toString(i, 16
) + sep + Long.toString(l, 16
) + sep + Integer.toString(word0, 16
);
- if
(s.length() > 24
)
- s = s.substring(s.length() - 24
);
- return
s;
- }
- public
Serializable generate_old(SessionImplementor session, Object obj) {
- String name = obj.getClass().getName();
- return
new
StringBuffer(64
)
- .append(name.substring(name.lastIndexOf('.'
)+1
)).append(sep)
- .append((short
)getIP()).append(sep)
- .append(Math.abs((short
)getJVM())).append(sep)
- .append(getCount())
- .toString();
- }
- private
static
int
getHostUniqueNum()
- {
- return
(new
Object()).hashCode();
- }
- public
void
configure(Type type, Properties params, Dialect d) {
- sep = PropertiesHelper.getString("separator"
, params, ""
);
- }
- public
static
void
main( String[] args ) throws
Exception {
- Properties props = new
Properties();
- props.setProperty("separator"
, ""
);
- IdentifierGenerator gen = new
UIDGenerator();
- ( (Configurable) gen ).configure(Hibernate
.STRING, props, null
);
- IdentifierGenerator gen2 = new
UIDGenerator();
- ( (Configurable) gen2 ).configure(Hibernate
.STRING, props, null
);
- for
( int
i=0
; i<10
; i++) {
- String id = (String) gen.generate(null
, gen);
- System.out.println(id);
- String id2 = (String) gen2.generate(null
, gen2);
- System.out.println(id2);
- }
- }
- }
在hibernate
的配置文件中要做以下的配置:
xml 代码
Generator的类要引用UIDGenerator,并且可以带参数生成主键,示例是根据时间,Ip等生成一个24位的字符串。这样做的灵活性大大提高了,提供了最好的性能和数据库平台适应性。
- <
id
name
="id"
type
="java.lang.String"
>
- <
column
name
="id"
length
="24"
/>
- <
generator
class
="com.gsta.eshore.framework.util.uid.UIDGenerator"
>
<
param
name
="separator"
>
-</
param
>
</
generator
>
- </
id
>
但是有时候我们在保存一条记录的时候是不能指定主键的,因为它的主键要来源于其他的表的主键,(hibernate
推荐用代理主键,但是有时候设计的时候没有用到)。这个时候的主键生成策略就要是assigned了。为了保持主键的连贯性,我总不能又用另外一种主键生成策略吧。
仿照上面的类,我们做一个生成24为随机字符串的类。
java 代码
在save一个实体的时候调用entity.setId(UID.getUID())。
- package
com.gsta.eshore.framework.util.uid;
- public
class
UID
- {
- private
static
long
lastTime = System.currentTimeMillis();
- private
static
short
lastCount = -32768
;
- private
static
Object mutex = new
Object();
- private
static
long
ONE_SECOND = 1000L;
- public
UID()
- {
- }
- public
static
String getUID()
- {
- long
l = 0L;
- short
word0 = 0
;
- int
i = 0
;
- synchronized
(mutex)
- {
- if
(lastCount == 32767
)
- {
- for
(boolean
flag = false
; !flag;)
- {
- l = System.currentTimeMillis();
- if
(l < lastTime + ONE_SECOND)
- {
- try
- {
- Thread.currentThread();
- Thread.sleep(ONE_SECOND);
- }
- catch
(InterruptedException interruptedexception) { }
- } else
- {
- lastTime = l;
- lastCount = -32768
;
- flag = true
;
- }
- }
- } else
- {
- l = lastTime;
- }
- word0 = lastCount++;
- i = getHostUniqueNum();
- }
- String s = Integer.toString(i, 16
) + "`"
+ Long.toString(l, 16
) + "`"
+ Integer.toString(word0, 16
);
- if
(s.length() > 24
)
- s = s.substring(s.length() - 24
);
- return
s;
- }
- private
static
int
getHostUniqueNum()
- {
- return
(new
Object()).hashCode();
- }
- public
static
void
main(String[] args) {
- for
(int
i = 0
; i < 100
; i++) {
- String uid=getUID();
- System.out.println(uid);
- }
- }
- }
呵呵,以后用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主键生成策略
- http://developer.51cto.com 2009-09-25 13:33 佚名 javaeye 我要评论(0)
- 采用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代码
- <id name="实体类属性名" type="java.lang.Integer">
- <column name="对应表中主键字段名" />
- <generator class="assiged|increment|identity|native|........" />
- </id>
采用hibernate主键生成策略,就可以比较灵活和方便的对表中的主键字段进行操作了。而且,不同的数据库,不同的主键形式,也只需要修改下映射文件就可以了