一、hibernate使用对象标识符(OID)来区分对象
看如下一个例子:
1.新建工程Hibernate03;
2.新建类Student以及它的映射文件Student.hbm.xml
package com.test.model;
public class Student {
private long id;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.test.model">
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="native"></generator>
</id>
<property name="name"></property>
</class>
</hibernate-mapping>
3.hibernate配置文件以及HibernateUtil.java文件拷贝到工程中;
4.新建测试类:
package com.test.service;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.test.model.Student;
import com.test.util.HibernateUtil;
public class StudentTest {
public static void main(String[] args) {
SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); // 获取Session工厂
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Student s1 = (Student)session.get(Student.class, Long.valueOf(2));
Student s2 = (Student)session.get(Student.class, Long.valueOf(3));
Student s3 = (Student)session.get(Student.class, Long.valueOf(2));
System.out.println(s1==s2);
System.out.println(s1==s3);//s1和s3指向的是同一个对象
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
}
此时的数据库中数据为:
运行测试函数,控制台输出结果为:
先看结果:上面的结果显示s1与s2是不同的对象,而s1与s3是指向同一个对象的,这个就是hibernate使用对象标识符来表示对象的结果。看第一部分,这里只有两个sql语句,分别是取对象s1和s2的sql语句,而s3并没有新的sql语句,这说明了取对象s3的时候并没有到数据库中去操作。整个过程如下图:
当取对象s1的时候,hibernate会在数据库表t_student中取出这个对象放在缓存中,这个对象的OID值与其在表中的主键值是相同的,故其OID=1,s1指向缓存中OID等于1的这个对象。当取s2的时候,s2对象的主键值是2,hibernate会先到session缓存中看有没有OID值等于2的对象,没有,hibernate会在数据库中取出主键为2的对象放在缓存中,使s2指向这个对象,并给这个对象的OID赋值为2;当取s3的时候,s3的主键值为1,hibernate到缓存中查找有没有OID值等于1的对象,此时有,就直接将s3指向这个对象。
所以,这里s1和s3是指向缓存中同一个对象的。
二、Hibernate对象标识符生成策略
上面说到了对象标识符其实就是对象的主键值。所谓对象标识符的生成策略也就是对象主键的生成策略。
首先说一下主键分为代理主键和业务主键两种,代理主键是不具有业务性的主键,比如说上面数据库表中的主键是1、2、3等。业务主键是具有业务性的,比如说学生的学号,这个是唯一的,但是学号是具有业务意义的。
当数据库建立完成后,如果数据库中的数据不会经常变动的话,可以使用业务主键,这样会带来一些方便,不需要在额外搞一个字段。如果业务需要经常变动的,使用代理主键会比较好一些。
看一下上一个工程中主键的生成方式:
这里的主键生成方式为native方式。hibernate常用的主键生成方式有如下几种:
1,increment 由Hibernate 自动以递增的方式生成标识符,适用代理主键;
2,identity 由底层数据库生成标识符;适用代理主键;
3,sequcence 由Hibernate 根据底层数据库的序列来生成标识符;适用代理主键;
4,hilo Hibernate 根据high/low 算法来生成标识符。适用代理主键
5,native 根据底层数据库对自动生成标识符的支持能力, 来选择identity,sequence 或hilo;适用代理主键;
其中,increment为hibernate自动以递增的方式生成主键,看如下例子:
1.修改映射文件为increment方式:
<hibernate-mapping package="com.test.model">
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="increment"></generator>
</id>
<property name="name"></property>
</class>
</hibernate-mapping>
2.删除数据库中t_student表,使其重新生成;
3.写一个测试类:
package com.test.service;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.test.model.Student;
import com.test.util.HibernateUtil;
public class StudentTest2 {
public static void main(String[] args) {
SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); // 获取Session工厂
Session session=sessionFactory.openSession(); // 生成一个session
session.beginTransaction(); // 开启事务
Student s = new Student();
s.setName("张三");
session.save(s);
session.getTransaction().commit(); // 提交事务
session.close(); // 关闭session
}
}
执行这个测试函数,控制台运行结果为:
可以看到这里hibernate执行了2条sql语句。第一条语句是hibernate到数据库中查找当前的最大主键值是多少,第二条语句执行的时候把最大主键值加1插入到数据库中。
这里主键值的大小是hibernate来自动完成的。看一下此时生成的t_student表结构为:
这里主键并没有设置自增这一项。可见increment方式的主键自增是hibernate实现的,而不是数据库实现的。
identity方式是由数据库来实现主键自增的。
1.修改映射文件为:
<hibernate-mapping package="com.test.model">
<class name="Student" table="t_student">
<id name="id" column="stuId">
<generator class="identity"></generator>
</id>
<property name="name"></property>
</class>
</hibernate-mapping>
2.删除表格t_student并重新运行测试函数。控制台输出为:
这里hibernate只执行了一条插入语句,只插入了name一个字段。这里主键字段是由数据库自动赋值的。此时生成的t_student表结构为:
可以看到此时数据库对主键设置了自增选项。
sequence由Hibernate 根据底层数据库的序列来生成标识符,mysql不支持这个方式,oracle支持这种方式。
hilo是Hibernate 根据high/low 算法来生成标识符。
native根据底层数据库对自动生成标识符的支持能力, 来选择identity,sequence 或hilo。所以对于mysql而言,native方式与identity方式是相同的。