hibernate将orm对象的种类分为四种,分别是:
临时状态
临时状态一般来说就是我们使用new关键字创建的对象,还没有和session关联上,并且对象的id为null。调用session的save方法、saveorupdate方法、persist方法、merge方法、get方法和load方法都可以使临时状态对象变为持久化状态对象。
持久化状态
持久化状态也被称为托管状态,又session缓存进行托管,持久化对象的id不为空,并且与数据库中的记录相互对应。调用session的evict方法、clear方法和close方法可以使持久化状态对象变为游离状态对象。
游离状态
游离状态可以理解为对象从session缓存中取出后,session缓存被清空了,或者session被关闭了,又或者调用了evict方法将该对象手动从session缓存中删除了,这时session不再对该对象进行托管。游离状态与临时状态的一个区别就是,游离状态的对象的id不为空,并且数据库中有可能还保存着这个对象的信息。
调用session的update方法、saveorupdate方法和merge方法可以使游离状态对象变为持久化状态对象。
删除状态
调用了session的delete方法后,被delete的对象的状态将变为删除状态。一般来说删除状态的对象将不会在使用了,当事务commit的时候,数据库中也会将该对象删除。
以上就是orm对象在session中的四种状态,接下来测试一下session的常用方法。首先是save方法:
@Test
public void testSave() {
// 创建一个对象,该对象现在为临时状态。
User user = new User();
// 此处设置id的值不会生效
// save方法将调用sql本地生成方式从新为对象id赋值。
user.setId(100);
user.setName("Kobe");
user.setBirthday(new Date());
// 调用save方法将对象状态改变为持久化状态,由session缓存托管。
session.save(user);
// 持久化后的对象id不允许修改,否则将出现异常。
// org.hibernate.HibernateException:
// identifier of an instance of cn.net.bysoft.model.User was altered
// from 1 to 13
// user.setId(13);
}
上面是save方法的测试,需要注意的就是在save之前,设置对象的id无效,save方法将调用id生成方式生成一个id复制给对象。在save后,不可修改id,否则将会抛出异常。接下来了persist方法:
@Test
public void testPersist() {
User user = new User();
// 使用persist保存对象,如果设置了id值将会抛出异常。
// user.setId(111);
user.setName("Jordan");
user.setBirthday(new Date());
session.persist(user);
}
使用persist方法也可以将对象从临时状态改变为持久化状态,并且保存到数据库中。与save方法的区别在于如果,调用save方法前设置的id无效,但不会发生异常,而使用persist方法之前如果设置了id,将会抛出异常。
在看看get方法:
@Test
public void testGet() {
// 从数据库中获得id=3的记录,加载到User对象中。
// User对象从临时状态改变到持久化状态。
User user = (User) session.get(User.class, 3);
System.out.println(user);
/**
* output: User [id=3, name=Jordan, birthday=2016-03-25 19:56:14.0]
* */
// 如果传入的id在数据库中找不到记录,则返回null。
User user2 = (User) session.get(User.class, 44);
System.out.println(user2);
/**
* output: null
* */
}
调用get方法后,会发送一条select语句到数据库,查找id对应的记录,如果查找到,则将记录加载到对象中,并把对象的状态改变为持久化状态,如果没用通过id获得记录,则返回null。
get方法之后,看看load方法:
@Test
public void testLoad() {
// load方法创建了一个user的代理对象,执行load后并不会发送select语句。
User user = (User) session.load(User.class, 3);
// 在使用user对象的属性时,才发送select语句查询数据,这种机制叫做延迟加载。
System.out.println(user);
/**
* output: User [id=3, name=Jordan, birthday=2016-03-25 19:56:14.0]
* */
// 如果传入的id在数据库中找不到记录。
User user2 = (User) session.load(User.class, 44);
// 在使用该对象时会抛出异常。
System.out.println(user2);
}
在执行load方法后,hibernate不会立即去发送select语句查询数据库,而是创建一个代理,等到调用对象的属性时,由代理去发送select语句查询数据。
若load对象使用的id在数据库中不存在,则会抛出异常。
虽然都是从数据库查询数据加载到对象,但两个方法略有不同,对比一下get方法与load方法:
get方法 | load方法 | |
何时执行select语句 | 调用get方法后立即执行select语句。 | 调用load方法后不执行select语句,而是创建一个代理。 等到使用对象的属性时再执行select语句。 |
如果id在数据库中不存在 | 调用get方法返回null | 调用load方法后,在使用该对象时会抛出异常。 |
在使用对象前调用session.close | 不会抛出异常 | 会抛出异常 |
以上是get方法与load方法,接下来测试一下update方法:
@Test
public void testUpdate(){
// update方法可以将游离对象变为持久化对象。
User user = new User();
user.setId(2);
user.setName("Jack");
user.setBirthday(new Date());
// 调用update将上面的游离状态对象变为持久化状态对象。
// 并且更新数据库中的记录。
session.update(user);
}
创建一个User对象,这里因为给user设置了id,id不为空的话就不是临时对象,而是游离对象。通过update方法把游离对象的状态改成持久化对象,并且向数据库发送了update语句。
在调用flush的时候,如有必要,hibernate也会发送update语句,这里,数据库中若有触发器,则会频繁出发。这些出发中可能有不必要的update操作。hibernate提供了select-before-update属性来解决这个问题,将该属性设置成true,在update之前会查询一次数据库,如果对象没有变化则不执行update。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- update之前先进行select,对象与数据库中的记录一致,则不执行update -->
<class name="cn.net.bysoft.model.User" table="S_USER" select-before-update="true">
<id name="id" type="integer" column="ID">
<!-- 指定主键的生成方式,native是使用数据库本地的方式 -->
<generator class="native"></generator>
</id>
<property name="name" type="string" column="NAME"></property>
<property name="birthday" type="timestamp" column="BIRTHDAY"></property>
</class>
</hibernate-mapping>
update时,传入的id如果数据库中不存在,则抛出异常。
saveorupdate方法顾名思义是传入一个对象,由hibernate分析是save还是update。规则是,如果对象id是null就save,如果id不是null就update。
@Test
public void testSaveOrUpdate() {
User user = new User();
user.setName("Mary");
user.setBirthday(new Date());
// user的id为null,执行insert操作。
session.saveOrUpdate(user);
User user2 = new User();
user2.setId(2);
user2.setName("Kobe");
user2.setBirthday(new Date());
// user的id为2,数据库中有id等于2的记录,将执行update操作。
session.saveOrUpdate(user2);
}
第一个saveorupdate将输出insert语句,因为id is null。第二个将输出update语句,因为id=2,在数据库中存在这条记录。
若在*.hbm.xml的id属性上设置了unsaved-value的值,在做saveorupdate时,id不为null,但是值等于unsaved-value设置的值,也会进行save。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- update之前先进行select,对象与数据库中的记录一致,则不执行update -->
<class name="cn.net.bysoft.model.User" table="S_USER" select-before-update="true">
<id name="id" type="integer" column="ID" unsaved-value="11">
<!-- 指定主键的生成方式,native是使用数据库本地的方式 -->
<generator class="native"></generator>
</id>
<property name="name" type="string" column="NAME"></property>
<property name="birthday" type="timestamp" column="BIRTHDAY"></property>
</class>
</hibernate-mapping>
将id节点的设置了一个unsaved-value属性,值是11。
@Test
public void testSaveOrUpdate() {
User user = new User();
user.setId(11);
user.setName("Mark");
user.setBirthday(new Date());
// user的id为null,执行insert操作。
session.saveOrUpdate(user);
}
这里设置了user的id等于11,与unsaved-value中的值一样。执行saveorupdate会发送一条insert语句。
但是数据库中的id值不是11,而是自增的id数值。
以上就是saveorupdate的基本应用。还剩下两个方法,一个是delete,一个是evict。先测试一下delete方法:
@Test
public void testSaveOrUpdate() {
User user = new User();
user.setId(6);
session.delete(user);
// 调用delete后,对象的状态变为删除状态。
// 在事务commit之前,打印这个对象看看。
System.out.println(user);
/**
* output: User [id=6, name=null, birthday=null]
* */
// 可以看到在commit之前,user对象还有id,那么在commit之前调用save或者update就会出现问题。
// 建议不要删除状态的对象。
}
调用delete方法会把传入方法中的对象的状态改变成删除状态,但是在commit之前,这个对象还可以进行操作。建议不要使用删除状态的对象。
hibernate提供了hibernate.use_identifier_rollback属性,在hibernate.cfg.xml中设置,将属性的值等于true。在调用delete方法时,hibernate会自动将删除的对象的id置空。
<!-- 删除对象时,将对象的id设置成null -->
<property name="hibernate.use_identifier_rollback">true</property>
@Test
public void testSaveOrUpdate() {
User user = new User();
user.setId(5);
session.delete(user);
System.out.println(user);
/**
* output:User [id=0, name=null, birthday=null]
* */
}
设置了hibernate.use_identifier_rollback=true后,在删除对象可以看到输出的结果,对象的id被设置成了0。
还有就是,如果删除的对象id在数据库中不存在,将会抛出异常。
最后一个方法是evict,这个方法用来送session缓存中移除一个托管对象。也就是说将对象从持久化状态编程游离状态。
@Test
public void testSaveOrUpdate() {
// 持久化对象。
User user = (User) session.get(User.class, 3);
System.out.println(user);
// 调用evict方法变成游离对象。
session.evict(user);
}
还有一点需要注意的,同一个id的对象在session的缓存中只能有一个。
@Test
public void testSaveOrUpdate() {
// 持久化对象。
User user = (User) session.get(User.class, 3);
System.out.println(user);
// 调用evict方法把id为3的user变成游离对象。
// 这是session缓存中已经没有对象了。
session.evict(user);
// 把一个新的id为3的user对象放到缓存中。
User user1 = (User) session.get(User.class, 3);
// 在尝试把旧的id为3的user对象放到缓存中,会抛出异常。
// 此时session的缓存中已经有一个id为3的user对象了
// org.hibernate.NonUniqueObjectException:
// A different object with the same identifier value was already associated with the session :
// [cn.net.bysoft.model.User#3]
session.update(user);
}
以上就是session的常用方法。