复习 - Hibernate中将detached状态对象重新与持久化环境关联的一些注意问题
首先来看一下这个例子:
package com.yxy.test;
import java.util.Date;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.stat.Statistics;
import com.yxy.bean.Student;
public class HibernateDetachedTest {
public static void main(String[] args){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
Statistics stat = sessionFactory.getStatistics();
/*
* --------------------- Session 1 ---------------------------
*/
Session session = sessionFactory.openSession();
session.getTransaction().begin();
Student student = (Student)session.load(Student.class, 1);
session.getTransaction().commit();
session.close();
/*
* --------------------- Session 2 ---------------------------
* student is in detached status.
*/
student.setName("Another Hibernate Session");
Session anotherSession = sessionFactory.openSession();
anotherSession.getTransaction().begin();
anotherSession.update(student);
student.setName("Another Session");
anotherSession.getTransaction().commit();
anotherSession.close();
}
}
student在第一个session中被加载,随后该session关闭,如果试图去更新student中的值. 就会发生LazyInitializationException,原因是在第一个session中student并没有真正被加载,持久化环境中仅有他的一个分身(Proxy),于是在detached状态时调用去initialize这个proxy,肯定就会有异常了. 如下所示
Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - the owning Session was closed
我们可以在session1中将真正的student实体给加载上来,就可以解决这个问题,如下所示
Student student = (Student)session.load(Student.class, 1);
student.setName("Now need load the real entity, not proxy");
可见在将detached对象重新同持久化环境进行关联的时候确实有很多需要注意的地方,稍有不慎可能就会出现一些意想不到的事情. 对于重新关联的一些类型,归纳如下:
(1). 如果我们很确定对象在进入持久化环境之前做过修改 ------------------> 使用update()
如上例中所示,我们在session2中使用
anotherSession.update(student);
将改变同步到数据库中. 当然在这个时候并没有发出update语句,而是要等到事务提交的时候才发出update语句.
(2). 如果我们不是很确定对象是否被修改 -------------------> 配置select-before-update
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.yxy.bean">
<class name="Student" table="student" select-before-update="true">
<id name="id" type="int">
<generator class="native" />
</id>
<property name="name" type="string" column="name"/>
<property name="sex" type="string" column="sex"/>
<property name="registerDate" type="date" column="register_date"/>
</class>
</hibernate-mapping>
这样可以让Hibernate在更新(调用update() api)之前先将对象select出来,检查是否同detached对象存在数据上差异,从而决定是否需要更新.这些都是Hibernate自动做的,呵呵.
来个例子:
package com.yxy.test;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yxy.bean.Student;
public class HibernateSelectBeforeUpdateTest {
public static void main(String[] args){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
/*
* --------------------- Session 1 ---------------------------
*/
Session session = sessionFactory.openSession();
session.getTransaction().begin();
Student s = new Student();
s.setName("Select before update");
s.setRegisterDate(new Date(System.currentTimeMillis()));
s.setSex("Female");
session.save(s);
session.getTransaction().commit();
session.close();
/*
* --------------------- Session 2 ---------------------------
*/
session = sessionFactory.openSession();
session.getTransaction().begin();
System.out.println("----------- select, not update -----------");
session.update(s);
session.getTransaction().commit();
session.close();
/*
* --------------------- Session 3 ---------------------------
* s is in detached status.
*/
s.setName("now update");
session = sessionFactory.openSession();
session.getTransaction().begin();
System.out.println("----------- update -------------");
session.update(s);
session.getTransaction().commit();
session.close();
}
}
在第一个session之前中并没有做任何改动,所以Hibernate在调用update()之前将对象取出来和持久化对象中的s进行比较时,没有发现差异,故不会调用update语句,而在第二个session之前我们对s做了修改,所以当用同样的方法进行比较时,Hiberante发现两者有区别,所以会调用update语句将变化同步到数据库中.
console中打印出来的信息如下:
Hibernate:
select
hibernate_sequence.nextval
from
dual
Hibernate:
/* insert com.yxy.bean.Student
*/ insert
into
student
(name, sex, register_date, id)
values
(?, ?, ?, ?)
----------- select, not update -----------
Hibernate:
/* get current state com.yxy.bean.Student */ select
student_.id,
student_.name as name0_,
student_.sex as sex0_,
student_.register_date as register4_0_
from
student student_
where
student_.id=?
----------- update -------------
Hibernate:
/* get current state com.yxy.bean.Student */ select
student_.id,
student_.name as name0_,
student_.sex as sex0_,
student_.register_date as register4_0_
from
student student_
where
student_.id=?
Hibernate:
/* update
com.yxy.bean.Student */ update
student
set
name=?,
sex=?,
register_date=?
where
id=?
(3). 我们不想让Hibernate为被修改了的detached对象生成update语句 -------------------> session.lock(***, LockMode.***);
关于LockMode的几种形式,在后面复习Hibernate事务的相关文章中再做总结了,这里需要记住的是. LockMode.NONE的特性是只有当cache里面没有相关的数据时才会去数据库中取,而且它并不会比较持久化环境和数据库中数据的差异,所以当然不会生成update语句
例子:
package com.yxy.test;
import java.util.Date;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yxy.bean.Student;
public class HibernateLockModeTest {
public static void main(String[] args){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
/*
* --------------------- Session 1 ---------------------------
*/
Session session = sessionFactory.openSession();
session.getTransaction().begin();
Student s = new Student();
s.setName("Select before update");
s.setRegisterDate(new Date(System.currentTimeMillis()));
s.setSex("Female");
session.save(s);
session.getTransaction().commit();
session.close();
/*
* --------------------- Session 2 ---------------------------
* s is in detached status.
*/
s.setName("Test LockMode NONE");
session = sessionFactory.openSession();
session.getTransaction().begin();
session.lock(s, LockMode.NONE);
session.update(s);
session.getTransaction().commit();
session.close();
}
}
这样设置的话,可以在console中看到不会生成update语句,仅有一个在session1中生成的insert语句,这里就不打印出来了.
(4). detached对象和持久化状态中存在的某个对象代表数据库中同一条记录的情况 -------------------> merge()
对于这种情况,因为会违反一个Persistent Context中只能同时存在数据库某条记录的唯一一个引用,所以Hiberante将不能区别到底该操作那个对象,故而抛出异常,
例子:
package com.yxy.test;
import java.util.Date;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yxy.bean.Student;
public class HibernateMergeTest {
public static void main(String[] args){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
/*
* --------------------- Session 1 ---------------------------
*/
Session session = sessionFactory.openSession();
session.getTransaction().begin();
Student s = new Student();
s.setName("merge test");
s.setRegisterDate(new Date(System.currentTimeMillis()));
s.setSex("Female");
session.save(s);
session.getTransaction().commit();
session.close();
/*
* --------------------- Session 2 ---------------------------
* s is in detached status.
*/
s.setName("merge test 2");
session = sessionFactory.openSession();
session.getTransaction().begin();
Student s1 = (Student)session.load(Student.class, 90);
session.update(s);
session.getTransaction().commit();
session.close();
}
}
出现的异常:
Exception in thread "main" org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.yxy.bean.Student#90]
这个时候可以使用merge()方法来进行解决,顾名思义,merge应该就是合并的意思.
merge会将detached对象的属性赋值给持久化环境中的那个对象,并且创建一个新的对象,这个对象的引用和持久化对象中新加载的那个对象的引用相同.
程序如下:
package com.yxy.test;
import java.util.Date;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.yxy.bean.Student;
public class HibernateMergeTest {
public static void main(String[] args){
SessionFactory sessionFactory = HibernateUtils.getSessionFactory();
/*
* --------------------- Session 1 ---------------------------
*/
Session session = sessionFactory.openSession();
session.getTransaction().begin();
Student s = new Student();
s.setName("merge test");
s.setRegisterDate(new Date(System.currentTimeMillis()));
s.setSex("Female");
session.save(s);
session.getTransaction().commit();
session.close();
/*
* --------------------- Session 2 ---------------------------
* s is in detached status.
*/
s.setName("merge test 91");
session = sessionFactory.openSession();
session.getTransaction().begin();
/*
* both s1 and mergeStudent reference to the same record.
*/
Student s1 = (Student)session.load(Student.class, 91);
Student mergeStudent = (Student)session.merge(s);
session.getTransaction().commit();
session.close();
}
}
从console中输出可以看出,生成了一条update语句,检查数据库中的数据,可以看到生成的91号Student的name已经更新成功.
分享到:
相关推荐
当持久化上下文(persistence context)关闭时,所有entities会变成Detached状态,无法直接访问数据库。 persistence context是一个包含了entities集合的管理环境,每个persistent identity在其中对应一个唯一的...
10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.2. 过滤集合 10.4.3. 条件查询(Criteria queries) 10.4.4. 使用原生SQL的查询 10.5. 修改...
- **persistent(持久化)**:对象与一个打开的Session关联,当Session保存或加载对象时,对象处于持久化状态。对这些对象的修改会在事务提交时自动更新到数据库。 - **detached(游离)**:对象曾经是持久化的,但...
- **Persistent**:持久化状态,已被EntityManager管理。 - **Detached**:游离状态,已从EntityManager中移除。 ##### 3.2 使对象持久化 通过EntityManager的persist()方法将对象变为持久化状态。 ##### 3.3 加载...
- **1.1.7 加载并存储对象**:演示如何使用 Hibernate API 来加载数据库中的数据到 Java 对象,并将 Java 对象持久化到数据库。 ##### 1.2 第二部分 — 关联映射 此章节深入探讨了 Hibernate 中对象之间的关联映射...
4. **持久化上下文(Persistence Context)**:是Hibernate Entity Manager维护的一个内存中的对象状态管理区域,确保在一段时间内对象的持久化状态是一致的。 5. **查询语言(JPQL)**:Java Persistence Query ...
10. **实体状态转换**: JPA中,实体有四种状态:瞬时(Transient)、持久化(Persistent)、游离(Detached)和已删除(Removed)。理解这些状态对于正确操作实体至关重要。 综上所述,"jpa最基本最全配置的jar包...
- **Entity生命周期**:EntityManager管理Entity的四种状态:Transient、Persistent、Detached和Removed,分别对应新建、已持久化、已分离和已删除。 - **持久化Entity**:调用`persist()`方法将瞬时对象变为持久化...
10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.2. 过滤集合 10.4.3. 条件查询(Criteria queries) 10.4.4. 使用原生SQL的查询 10.5. 修改...
10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.2. 过滤集合 10.4.3. 条件查询(Criteria queries) 10.4.4. 使用原生SQL的查询 10.5. 修改...
JPA定义了实体的四个状态:**新建(New)**、**持久化(Persistent)**、**托管(Managed)**和**游离(Detached)**。`EntityManager`负责监控实体状态的转换,并根据需要执行相应的数据库操作。 ### 5. ...
在JPA中,**持久化上下文(Persistence Context)** 是一个管理实体状态的核心概念。它维护了内存中实体的集合,并负责跟踪它们的状态变化。当实体在上下文中时,任何对它们的更改都会被自动持久化到数据库中。**...
4. **持久化上下文(Persistence Context)**: 这是一个临时存储区域,包含了所有处于活跃状态的实体。在该上下文中,JPA会自动跟踪实体的状态(新建、已修改、未变或已删除)。 5. **查询语言(JPQL)**: JPA提供...
10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.2. 过滤集合 10.4.3. 条件查询(Criteria queries) 10.4.4. 使用原生SQL的查询 10.5. 修改...
Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.1.1. 迭代式获取结果(Iterating results) 10.4.1.2. 返回元组(tuples)的查询 10.4.1.3. 标量...
10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.2. 过滤集合 10.4.3. 条件查询(Criteria queries) 10.4.4. 使用原生SQL的查询 10.5. 修改...
10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.1.1. 迭代式获取结果(Iterating results) 10.4.1.2. 返回元组(tuples)的查询 10.4.1.3. 标量(Scalar)...
2. **持久化上下文(Persistence Context)**:了解JPA中的持久化上下文是如何管理和跟踪实体状态的,包括瞬时(Transient)、持久化(Persistent)、托管(Managed)和脱管(Detached)状态。 3. **数据访问接口...
10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.1.1. 迭代式获取结果(Iterating results) 10.4.1.2. 返回元组(tuples)的查询 10.4.1.3. 标量(Scalar)...