对于一个与hibernate存在映射关系的java对象而言,它具有以下几种状态
- 临时状态(Transient)。临时状态的对象只存在于内存中,而不存在于数据库中,这个对象的属性变化不会被hibernate持久化存储。
- 持久状态(Persistent)。持久状态的对象存在于数据库中(当然,对象也必须存在于内存中)。持久状态的对象的属性变化会被提交到数据库中。
- 分离状态(Detached),也有人翻译为游离状态,一个意思,就是指这个对象存在于数据库中,但是跟session对象已经失去了关系,所以,这个对象的属性变化也不会被提交到数据库中。通常情况下,如果通过session查询出一个对象后session又关闭了,这个对象就会变成分离状态。
session.evict()
方法也会手动地将一个对象设置为分离状态。 - 已删除状态(Removed)。这个好理解,如果一个对象被hibernate删除了,则数据库中与该对象对应的数据会被删除,并且该对象也就成了已删除状态。已删除状态的对象的属性变化不会被持久化到数据库中。
接下来讨论几个跟生命周期相关的几个操作,顺便介绍一下session对象的几个对应的方法的用法。
保存
try (Session session = factory.openSession()) {
Transaction tx = session.beginTransaction();
Person person = new Person();
person.setName(testName);
session.save(person);
tx.commit();
}
在上面的代码中,session.save()
方法会保存一个新的对象到数据库中。session.persist()
方法的用法和用途与session.save()
一样。
加载
try (Session session = factory.openSession()) {
Person person = session.load(Person.class,personId);
}
上面的session.load()
方法会从数据库中查询出一个唯一的Person对象,如果不能保证这个对象存在,则可以使用session.get()
方法,如果使用这个方法查询的结果为空的话,会返回一个空指针,而前者则会直接抛异常。
通过session.load()
或session.get()
方法查询到的对象,就是所谓的持久状态的对象。
对象的相等性和唯一性
看下面的代码
try (Session session = factory.openSession()) {
Person person = session.load(Person.class,personId);
Person person2 = session.load(Person.class,personId);
Assert.assertEquals(person,person2);
}
上面的断言的结果是正确的,也就是说,在一个session范围内,多次查询同一个对象的话,这两个对象的引用指向同一块内存,或者说两个对象是同一个对象。这是由hibernate的一级缓存决定的。关于hibernate的缓存机制,后面的博客中可能会专门来介绍。
如果在不同的session范围内多次查询同一个对象的话,则是不同的对象。
合并(Merge)
合并是指,将一个处于分离状态(detached)的对象与一个session对象重新连接起来,然后,这个对象的属性变化就可以被更新到数据库中。
下面的代码就是一个使用session.merge()
方法的例子。
@Test
public void testMerge(){
Person person = null;
try(Session session = factory.openSession()){
person = session.get(Person.class,generateId);
}
//分离状态下更新属性
person.setName("another name");
try(Session session2 = factory.openSession()){
Transaction tran = session2.beginTransaction();
//合并,与session2对象关联
session2.merge(person);
//提交事务后,person对象的修改就会被更新到数据库中
tran.commit();
}
try(Session session3 = factory.openSession()){
Person person3 = session3.load(Person.class,generateId);
Assert.assertEquals(person3.getName(),"another name");
}
}
刷新(refresh)
session.refresh
方法与session.merge()
方法是一对反向操作,它的目的是用数据库中最新的值来覆盖一个分离状态的对象的当前值。看一下下面的代码
@Test
public void testRefresh(){
Person person = null;
String originName;
try(Session session = factory.openSession()){
person = session.get(Person.class,generateId);
originName = person.getName();
}
person.setName("another name");
try(Session session2 = factory.openSession()){
session2.refresh(person);
}
try(Session session3 = factory.openSession()){
Person person3 = session3.load(Person.class,generateId);
Assert.assertEquals(person3.getName(),originName);
}
}
更新映射对象
在一个session的范围内,一个持久化状态的对象的属性被修改了以后,hibernate会跟踪这些变化并记录下来,在事务被提交时,这些变化会更新到数据库里面,开发者不需要做额外的工作。
如果持久化对象被修改后,你不想在提交事务是把这些变化更新到数据库的话,可以在commit之前先调用一下session.flush()
方法,它用来清理掉整个session对变化的所有跟踪和记录。
如果想查询session是否已经跟踪到了变化并将被更新到数据库,可以调用session.isDirty()
方法。