Pro JPA2 第六章(实体管理器)
当创建实体时,实体并没有持久化自身.当作为垃圾回收时,它们也不会从数据库中删除自身.应用程序的逻辑是必须操纵实体以管理其持久化生命周期.为了让应用程序达到管理和搜索在关系数据库中的实体的目的,JPA提供了EntityManager接口.
- 6.1 持久化上下文
一个持久性单元是已命名的实体类的配置.持久化上下文是一个托管实体实例的集合.每一个持久化上下文关联一个持久性单元,从而先知托管实例的类需要满足持久性单元所定义的设置.若一个实体实例是托管的,则意味着它包含在一个持久化上下文中,并且可以由一个实体管理器对其进行操作.因为这个,所以说实体管理器管理持久化上下文.
尽管持久化上下文发挥了重要作用,但是它对于应用程序是从未实际可见的.应用程序总是通过实体管理器间接的访问它,并且假定当我们需要时,它一定存在. 6.2实体管理器
6.2.1 容器托管的实体管理器
最常见的方式是使用注解@PersistenceContext获取一个实体管理器.通过这种方法获得的实体管理器称为容器托管的实体管理器.
容器托管的实体管理器分为两类,容器托管的实体管理器的样式决定了如何与持久化上下文一起工作.事务范围的
由实体管理器管理的持久化上下文的范围限定在活动的JTA事务中,当事务完成时结束.public class ProjectServiceBean implements ProjectService { @PersistenceContext(unitName="EmployeeService") EntityManager em; public void assignEmployeeToProject(int empId,int projectId) { Project project = em.find(Project.class,projectId); Employee employee = em.find(Employee.class,empId); project.getEmployees().add(employee); employee.getProjects().add(project); } }
所有容器托管的实体管理器均依赖于JTA事务,因为它们可以使用事务作为跟踪持久化上下文的方式.每当在实体管理器上调用操作时,这些实体管理器的容器代理将会检查持久化上下文是否与容器的JTA事务关联.如果发现关联,那么实体管理器将使用这个持久化上下文,如果没有找到,那么它将创建一个新的持久化上下文,将它与事务相关联.当事务结束时,持久化上下文消失.
- 使用扩展的
略
6.2.2 应用程序托管的实体管理器
在Java应用程序中使用应用程序托管的实体管理器public class EmployeeClient { public static void main(String [] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory("EmployeeService"); EntityManager em = emf.createEntityManager(); List<Employee> emps = em.createQuery("SELECT e FROM Employee e").getResultList(); for(Employee e : emps) { System.out.println(e.getId() + "," + e.getName()); } em.close(); emf.close(); } }
在Java EE中使用应用程序托管的实体管理器
public class LoginServlet extends HttpServlet { @PersistenceUnit(unitName="EmployeeService") EntityManagerFactory emf; protected void doPost(HttpServletRequest request,HttpServletResponse response) { String userId = request.getparameter("user"); EntityManager em = emf.createEntityManager(); try { User user = em.find(User.class,userId); if(null == user) { } } finally { em.close(); } } }
6.3 事务管理
JPA支持两种类型的事务管理:- 资源本地(resource-local)的事务,它由持久性单元引用的JDBC驱动程序的本地事务.
JTA事务,它们是Java EE服务器的事务,支持多种参与的资源,事务生命周期管理和分布式XA事务
6.3.1 JTA事务管理
为了讨论JTA事务,必须首先讨论事务同步,事务关联以及事务传播之间的差异.事务同步是指使用事务注册持久化上下文的过程,使得持久化上下文可以在一个事务提交时获得通知.提供程序使用此通知来确保一个给定的持久化上下文正确地刷新到数据库中.事务关联是持久化上下文绑定到一个事务的行为,可以把它作为事务范围内的活动的持久化上下文.事务传播是在单个事务内多个容器托管的实体管理器之间共享一个持久化上下文的过程.
有且只有一个持久化上下文与JTA事务相关联,并且通过它传播.在同一个事务内,所有容器托管的实体管理器必须共享传播的相同的持久化上下文.- 事务范围的持久化上下文
一个事务范围的持久化上下文关系到事务的生命周期.它shit由容器在事务期间创建,并且当事务完成时关闭它.事务范围的实体管理器负责在需要时自动创建是事务范围的持久化上下文.
当在事务范围的实体管理器上调用方法时,首先必须查看是否有一个传播的持久化上下文.如果存在,那么实体管理器使用这个持久化上下文进行操作.如果不存在,那么实体管理器从持久化的提供程序中请求一个新的持久化上下文,然后标记这个新的持久化上下文,作为在调用方法之前用于事务传播的持久化上下文.此后,所有后续的事务范围的实体管理器操作,都将会使用这个心创建的持久化上下文.这种行为独立于是否使用了容器托管的或bean托管的事务划界.
持久化上下文的传播简化了企业应用程序的构建.当一个实体由事务内部的组件更新时,其后相同实体的任何引用都将始终对应到正确的实例.持久化上下文的传播为开发人员提供了构建松散耦合的应用程序的自由. - 扩展的持久化上下文
略 - 应用程序托管的持久化上下文
略
- 事务范围的持久化上下文
- 6.3.2 资源本地的事务
略 - 6.3.3 事务回顾和实体状态
当一个数据库事务回滚时,将在事务中的所有更改放弃.数据库恢复到在事务开始之前的状态.但是.Java内存模型不是事务性的.没有办法记录一次对象状态快照,然后当出现错误时恢复它.使用对象-关系映射解决方案更为困难的部分之一在于,虽然可以在应用程序中使用事务性语义来控制算法将数据提交到数据库,但是不能再内存中的持久化上下文上应用相同的技术.
每当与必须持久化到数据库的更改一起工作时,我们将和与事务同步的持久化上下文一起工作.在事务生命周期的某个时刻,通常在提交它之前,我们需要的更改将被翻译成相应的SQL语句并发送到数据库.这与是否使用JTA事务或资源本地事务都不相关.我们有一个参与事务的持久化上下文,起带有需要作出的更改.
如果事务回顾,第一是数据库事务将回滚,第二是持久化上下文将清空,从而分离所有托管的实体实例.如果持久化上下文是事务范围的,那么将删除它.
- 6.4 选择实体管理器
- 一般来说,我们认为容器托管的事务范围的实体管理器对于大多数应用程序是最佳模型.
- 容器托管的,扩展的持久化上下文提供了一种不同的编程模型,实体提交之后仍然处于托管状态,但是在这种情况下它们与Java EE组件的生命周期绑定.这样它们是不能适用于所有的应用程序的.
- 在大多数企业应用程序中,不可能看到太多应用程序托管的实体管理器的应用.
6.5 实体管理器操作
6.5.1 持久化实体
persist()操作是用于在数据库中还没有存在的新实体.Department dept = em.find(Department.class,30); Employee emp = new Employee(); emp.setId(53); emp.setName("Peter"); emp.setDepartment(dept); dept.getEmployees().add(emp); em.persist(emp);
6.5.2 寻找实体
find()方法是实体管理器的助理方法.
在所有情况下,find()操作都将返回一个托管的实体实例,除了在一个事务范围的实体管理器的事务之外调用时,在这种情况下,实体实例以分离的状态返回.它没有与任何持久化上下文相关联.
当我们只是想获得一个关联关系,而不是这个目标实体时,可以采用getReference().Department dept = em.getReference(Department.class,30); Employee emp = new Employee(); emp.setId(30); emp.setName("Peter"); emp.setDepartment(dept); dept.getEmployees().add(emp); em.persist(emp);
当我们调用getReference()时,提供程序可以返回一个Department实体的代理.这个方法能够有效地实现性能优化,因为它消除了检索目标实体实例的需要.
6.5.3 删除实体
Employee emp = em.find(Employee.class,empid); em.remove(emp.getParkingSpace());
6.5.4 级联操作
默认情况下,每一个实体管理器的操作仅适用于作为参数提供给操作的实体.操作将不会级联到与操作的实体有关系的其他实体.这对于类似remove()方法正是我们期望的.但是,类似于persist()的操作于此有些不同.如果有一个新的实体且它与另一个新的实体有关系,那么这两个实体必须一起持久化.级联持久化
@Entity public class Employee { @ManyToOne(cascade=CascadeType.PERSIST) Address address; }
这样,我们只需要确保在调用persist()方法之前,已经在Employee实体上设置了Address方法.
级联设置是单向的.这意味着,如果打算在两种情况下采用相同的行为,那么它们必须在关系的双方均显式地设置.- 级联删除
实际上,只有两种情况下,级联删除操作是合理的:一对一和一对多关系,其中有明确的父子关系.
@Entity
public class Employee {
@OneToOne(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
ParkingSpace parkingSpace;
@OneToMany(mappedBy="employee",cascade={CascadeType.PERSIST,Cascade.REMOVE})
Collection<Phone>phones;
}
- 6.6 与数据库同步
任何时候当持久化提供程序生成了SQL,并通过JDBC连接写出到数据库时,就说已经刷新了持久化上下文.也就是说,写出了所有的挂起更改.
如果存在挂起更改的托管实体,那么在两种情况下将确保发生刷新,一是当事务提交时,二十仅当实体管理器调用flush()方法时,保证发生刷新. - 6.7 分离和合并
略