跑江湖安磨器:Hibernate标识符属性(主键)生成策略全析
来源:百度文库 编辑:中财网 时间:2024/05/13 16:56:42
Hibernate标识符属性(主键)生成策略全析
文章分类:Java编程 数据库中的主键能够唯一识别一条记录,它可以是一个字段也可以是多个字段的组合。主键的主要作用是标识表中的一条记录,还有和其他表中的数据进行关联。数据库中的主键类型必须符合唯一性约束和非空约束。作为附加属性,主键应该尽可能简洁,不要包含过多属性。根据这个原则,主键可以分为自然主键和代理主键。自然主键是数据表中有逻辑含义的字段,比如身份证号来唯一确定一条个人记录。也可以通过学号和课程号来唯一确定成绩。使用自然主键时如果业务发生变化则对数据库调整是极其麻烦的,所以数据库设计使用代理主键是非常好的选择。代理主键是一个和业务无关的流水号,一般采用数据库中自动增长的机制自动生成。例如Oracle数据库使用序列,MySQL和SQL Server有自动增长(auto increment)类型,该字段类型一般为Integer,名称设置为ID或XXID。下面来看看Hibernate中的主键生成策略,实验都以Oracle为主,MySQL为辅来说明。
第一种是increment策略,Hibernate配置如下:
Xml代码
name="id" column="ID" type="java.lang.Integer" length="10"> -
class="increment">
运行结果如下:
Sql代码
- Hibernate: select max(ID) from USERS
- Hibernate: insert into USERS (NAME, PHONE, DEPARTMENT, CITY, HIRE_TIME, ID) values(?, ?, ?, ?, ?, ?)
Hibernate: select max(ID) from USERSHibernate: insert into USERS (NAME, PHONE, DEPARTMENT, CITY, HIRE_TIME, ID) values(?, ?, ?, ?, ?, ?)
可以看出,Hibernate是从数据库中先找出已经存在的最大主键数值,然后加1后作为新纪录的主键再执行插入语句。这种策略不适合非独享数据库或者分布式的Hibernate应用,否则就很难保证主键值的唯一了。
第二种是identity策略,Hibernate中配置如下:
Xml代码
name="id" column="ID" type="java.lang.Integer" length="10"> -
class="identity">
这种策略在Oracle中无法使用,因为这种策略是针对数据库中字段自动增长类型。改在MySQL中测试,得到如下结果:
Sql代码
- Hibernate: insert into USERS (NAME, PHONE, DEPARTMENT, CITY, HIRE_TIME) values(?, ?, ?, ?, ?)
Hibernate: insert into USERS (NAME, PHONE, DEPARTMENT, CITY, HIRE_TIME) values(?, ?, ?, ?, ?)
多次运行程序,在数据库中查看,可以看到主键是自动进行递增的。
第三种是sequence策略,Hibernate中配置如下:
Xml代码
class="sequence"> - name="sequence">SEQ_HIBERNATE_USERS
SEQ_HIBERNATE_USERS
使用序列策略是,需要现在数据库中创建一条序列,运行程序,得到如下结果:
Sql代码
- Hibernate: select SEQ_HIBERNATE_USERS.nextval from dual
- Hibernate: insert into USERS (NAME, PHONE, DEPARTMENT, CITY, HIRE_TIME, ID) values (?, ?, ?, ?, ?, ?)
Hibernate: select SEQ_HIBERNATE_USERS.nextval from dualHibernate: insert into USERS (NAME, PHONE, DEPARTMENT, CITY, HIRE_TIME, ID) values (?, ?, ?, ?, ?, ?)
可以看出Hibernate先从序列中得到下一个数值,然后执行插入操作。
第四种是hilo策略,这是Hibernate采用一种称为“高/低位”(hi/lo)的算法产生标识符属性值,该算法采用一个高位值和一个低位值进行运算,结果作为标识符属性的值。使用该策略时,需要在数据库中建立一个表和其中一个字段,名称可以自定。其中的字段值作为高位值的来源。在Hibernate中配置如下:
Xml代码
class="hilo"> - name="table">HIBERNATE_KEY
- name="column">NEXT_HIVALUE
HIBERNATE_KEYNEXT_HIVALUE
可以见名知意,我使用的表名是HIBERNATE_KEY,字段名是NEXT_HIVALUE,然后给该字段设置一个值,作为高位。低位Hibernate有自己的管理机制可不用创建,也可以给出在配置文件中用1的形式给出即可,一般使用情况下使用1更好。该算法是每次按max_lo数值递增。Hibernate的执行结果还是一条语句,如下:
Sql代码
- Hibernate: insert into USERS (NAME, PHONE, DEPARTMENT, CITY, HIRE_TIME, ID) values (?, ?, ?, ?, ?, ?)
Hibernate: insert into USERS (NAME, PHONE, DEPARTMENT, CITY, HIRE_TIME, ID) values (?, ?, ?, ?, ?, ?)
执行后高位自动加1,然后到数据库中就可以查看生成的主键数值。该策略与底层的数据库无关,只要保证高位降低,那么生成的主键值是唯一的,并且可跨数据库使用。
第五种是seqhilo策略,基于hilo策略,这种方式的标识符属性生成时指定一个序列作为高位值,那么在Hibernate中的配置如下:
Xml代码
-
class="seqhilo"> - name="sequence">SEQ_HIBERNATE_USERS
SEQ_HIBERNATE_USERS
执行插入后,Hibernate首先取出序列的下一个值作为高位值,然后计算一个ID出来进行操作,得到如下结果:
Sql代码
- Hibernate: select SEQ_HIBERNATE_USERS.nextval from dual
- Hibernate: insert into USERS (NAME, PHONE, DEPARTMENT, CITY, HIRE_TIME, ID) values (?, ?, ?, ?, ?, ?)
Hibernate: select SEQ_HIBERNATE_USERS.nextval from dualHibernate: insert into USERS (NAME, PHONE, DEPARTMENT, CITY, HIRE_TIME, ID) values (?, ?, ?, ?, ?, ?)
第六种是uuid策略,这种策略是使用128位的UUID算法来生成字符串类型标识符属性,这种算法使用IP地址,JVM的启动时间(精确到1/4秒),系统时间和一个计数器(在当前JVM中唯一)这些数值经过计算得到一个标识符属性的值,产生的值是32位长度的字符串,则使用前需要将数据库字段调整到varchar2(32),持久化类的ID属性改为String进行测试。Hibernate中配置如下,注意字段信息的修改:
Xml代码
name="id" column="ID" type="java.lang.String" length="32"> -
class="uuid"> - lt;/id>
执行后Hibernate的结果还是一条插入语句,这里不再复制了,我们看看数据库中的结果吧,如下图,就得到了32位的主键值了。
在做文件下载时,用这种策略生成的主键就可以作为下载链接,而不用在人为去制作链接,是一个不错的选择。
第七种是guid策略,Hibernate中配置如下:
Xml代码
class="guid">
这个在Oracle中是用了sys_guid()函数生成的值,而在MySQL中使用uuid()函数生成值,这个值要设置成varchar2/varchar类型,区别在于Oracle中是32位长度,而MySQL原生是36位(有4个-隔开)。运行程序得到如下结果:(Oracle环境下)
Sql代码
- Hibernate: select rawtohex(sys_guid()) from dual
- Hibernate: insert into USERS (NAME, PHONE, DEPARTMENT, CITY, HIRE_TIME, ID) values (?, ?, ?, ?, ?, ?)
Hibernate: select rawtohex(sys_guid()) from dualHibernate: insert into USERS (NAME, PHONE, DEPARTMENT, CITY, HIRE_TIME, ID) values (?, ?, ?, ?, ?, ?)
(MySQL环境下)
Sql代码
- Hibernate: select uuid()
- Hibernate: insert into USERS (NAME, PHONE, DEPARTMENT, CITY, HIRE_TIME, ID) values (?, ?, ?, ?, ?, ?)
Hibernate: select uuid()Hibernate: insert into USERS (NAME, PHONE, DEPARTMENT, CITY, HIRE_TIME, ID) values (?, ?, ?, ?, ?, ?)
第八种是native策略,native字面意思是“本地的”,那么对于数据库,Hibernate该如何选择呢?Hibernate根据所使用的数据库支持能力从identity,sequence或者hilo策略中选择一种,Hibernate中配置很简单:
Xml代码
class="native">
在Oracle下,Oracle先创建一个序列,使用默认名(数据库名_SEQUENCE),然后执行插入操作,而在MySQL下则使用identity策略,使用了自动增长的字段。Oracle中测试结果如下:
Sql代码
- Hibernate: select hibernate_sequence.nextval from dual
- Hibernate: insert into USERS (NAME, PHONE, DEPARTMENT, CITY, HIRE_TIME, ID) values (?, ?, ?, ?, ?, ?)
Hibernate: select hibernate_sequence.nextval from dualHibernate: insert into USERS (NAME, PHONE, DEPARTMENT, CITY, HIRE_TIME, ID) values (?, ?, ?, ?, ?, ?)
第九种是assigned策略,这种方式也是Hibernate中
Xml代码
class="assigned">
第十种是foreign策略,这种方式是通过关联的持久化对象为当前的持久化对象设置标识符属性,当他们是一对一关联时,一个持久化类的主键值可以参考关联持久化类的标识符属性值。我们做一个完整的实例来看。(使用Hibernate为我们自动创建表)
hibernate.cfg.xml
Xml代码
- version='1.0' encoding='UTF-8'?>
- "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
- "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
-
-
name="connection.driver_class">oracle.jdbc.driver.OracleDriver -
name="connection.url">jdbc:oracle:thin:@localhost:1521:orcl -
name="hibernate.connection.username">hibernate -
name="hibernate.connection.password">hibernate -
name="hibernate.dialect">org.hibernate.dialect.OracleDialect -
name="current_session_context_class">thread -
name="hbm2ddl.auto">create -
name="show_sql">true -
name="format_sql">false -
resource="demo/domain/User.hbm.xml" /> -
resource="demo/domain/Profile.hbm.xml" />
oracle.jdbc.driver.OracleDriver jdbc:oracle:thin:@localhost:1521:orcl hibernate hibernate org.hibernate.dialect.OracleDialect thread create true false
User持久化类和映射文件:
Java代码
- package demo.domain;
- public class User implements java.io.Serializable {
- private Integer id;
- private String name;
- private String password;
- private Profile profile;
- public User() {
- }
- // get和set方法
- public Integer getId() {
- return id;
- }
- public void setId(Integer id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- public Profile getProfile() {
- return profile;
- }
- public void setProfile(Profile profile) {
- this.profile = profile;
- }
- }
package demo.domain;public class User implements java.io.Serializable {private Integer id;private String name;private String password;private Profile profile;public User() {}// get和set方法public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public Profile getProfile() {return profile;}public void setProfile(Profile profile) {this.profile = profile;}}
Xml代码
- version="1.0" encoding="UTF-8"?>
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
-
name="demo.domain.User" table="USERS"> -
name="id" column="ID" type="java.lang.Integer" length="10"> -
class="native"> -
name="name" column="NAME" type="java.lang.String" - length="20" not-null="true" />
-
name="password" column="PASSWORD" type="java.lang.String" - length="32" />
-
name="profile" class="demo.domain.Profile" - outer-join="true" cascade="all" />
Profile持久化类和映射文件如下:
Java代码
- package demo.domain;
- public class Profile implements java.io.Serializable {
- private Integer id;
- private String info;
- private User user;
- public Profile() {
- }
- public Integer getId() {
- return id;
- }
- public void setId(Integer id) {
- this.id = id;
- }
- public String getInfo() {
- return info;
- }
- public void setInfo(String info) {
- this.info = info;
- }
- public User getUser() {
- return user;
- }
- public void setUser(User user) {
- this.user = user;
- }
- }
package demo.domain;public class Profile implements java.io.Serializable {private Integer id;private String info;private User user;public Profile() {}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getInfo() {return info;}public void setInfo(String info) {this.info = info;}public User getUser() {return user;}public void setUser(User user) {this.user = user;}}
Xml代码
- version="1.0" encoding="UTF-8"?>
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
-
name="demo.domain.Profile" table="PROFILES"> -
name="id" column="ID" type="java.lang.Integer" length="10"> -
class="foreign"> - name="property">user
-
name="info" column="INFO" type="java.lang.String" - length="200" not-null="true" />
-
name="user" class="demo.domain.User" - constrained="true" />
user
注意二者
Java代码
- package demo;
- import org.hibernate.*;
- import org.hibernate.cfg.Configuration;
- import demo.domain.*;
- public class Test {
- public static void main(String[] args) {
- Configuration config = new Configuration();
- config.configure();
- SessionFactory sessionFactory = config.buildSessionFactory();
- Session session = sessionFactory.getCurrentSession();
- Transaction tx = session.beginTransaction();
- User user = new User();
- user.setName("Tom");
- user.setPassword("123456");
- Profile profile = new Profile();
- profile.setInfo("Information");
- //双向关联
- user.setProfile(profile);
- profile.setUser(user);
- session.save(user);
- tx.commit();
- }
- }
package demo;import org.hibernate.*;import org.hibernate.cfg.Configuration;import demo.domain.*;public class Test {public static void main(String[] args) {Configuration config = new Configuration();config.configure();SessionFactory sessionFactory = config.buildSessionFactory();Session session = sessionFactory.getCurrentSession();Transaction tx = session.beginTransaction();User user = new User();user.setName("Tom");user.setPassword("123456");Profile profile = new Profile();profile.setInfo("Information");//双向关联user.setProfile(profile);profile.setUser(user);session.save(user);tx.commit();}}
执行程序,我们得到如下结果:
Sql代码
- Hibernate: select hibernate_sequence.nextval from dual
- Hibernate: insert into USERS (NAME, PASSWORD, ID) values (?, ?, ?)
- Hibernate: insert into PROFILES (INFO, ID) values (?, ?)
Hibernate: select hibernate_sequence.nextval from dualHibernate: insert into USERS (NAME, PASSWORD, ID) values (?, ?, ?)Hibernate: insert into PROFILES (INFO, ID) values (?, ?)
我们为User对象设置的标识符属性生成策略是native,则在Oracle数据库中使用序列生成。为Profie对象设置的标识符属性生成策略是foreign,它就按照主体对象的标识符属性获取。其实这种一对一关系完全可以合并到一个表中,只是为了说明,分开来演示例子而已。而实际情况就要具体问题具体分析了,主要在业务层面考虑而不是具体的技术实现。
最后来说一下标识符属性生成策略的选择方式。应用不需要分布式时,在数据库支持的sequence,identity,hilo,seqhilo和uuid中选择比较好。而分布式数据库应用中uuid是最佳选择。若是改造遗留系统,那么使用assigned是最合适的了。
欢迎交流,希望对使用者有用。
- 大小: 6.7 KB
如何在Hibernate映射文件中,写两个字段数据都可以自动生成?(其中一个是主键))
Hibernate 联合主键如何自增呢?
hibernate的映射文件生成问题
hibernate
从ACCESS中导入的数据表主键属性消失了
对象标识符
hibernate 问题
hibernate 异常
hibernate 是什么?
可否设置一类文件生成时就具有隐藏属性?
这样清除显卡的生成右键“图形选项”、“图形属性”
怎样清除显卡生成的“图形属性”、“图形选项”?
样清除显卡生成的“图形属性”、“图形选项”?
(vb)怎样改变生成的.exe程序的属性
标识符的问题
JAVA需要标识符??
什么叫标识符?
JAva 标识符问题
JAva 标识符问题
Java标识符命名
函数标识符 random 实例
硬件标识符是什么东东?
文件标识符是什么
简单的hibernate问题