EJB之JPA(EntityManager)

 

 

基本概念

EntityManager称为实体管理器,它由EntityManagerFactory所建立。EntityManagerFactory,做为EntityManager的工厂,包含有当前O-R映射的元数据信息,每一个EntityManagerFactory,可称为一个持久化单元(PersistenceUnit),每一个持久化单元可认为是一个数据源的映射(所谓数据源,可理解为一个数据库,咱们能够在应用服务器中配置多个数据源,同时使用不一样的PersistenceUnit来映射这些数据源,从而可以很方便的实现跨越多个数据库之间的事务操做!)java

 

       PersistenceContext,称为持久化上下文,它通常包含有当前事务范围内的,被管理的实体对象(Entity)的数据。每一个EntityManager,都会跟一个PersistenceContext相关联。PersistenceContext中存储的是实体对象的数据,而关系数据库中存储的是记录,EntityManager正是维护这种OR映射的中间者,它能够把数据从数据库中加载到PersistenceContext中,也能够把数据从PersistenceContext中持久化到数据库,EntityManager经过Persist、merge、remove、refresh、flush等操做来操纵PersistenceContext与数据库数据之间的同步!mysql

       EntityManager是应用程序操纵持久化数据的接口。它的做用与hibernate session相似。为了可以在一个请求周期中使用同一个session对象,在hibernate的解决方案中,提出了currentSession的概念,hibernate中的current session,能够跟JTA事务绑定,也能够跟当前线程绑定。在hibernate中,session管理着全部的持久化对象的数据。而在EJB3中,EntityManager管理着PersistenceContext,PersistenceContext正是被管理的持久化对象的集合。spring

       在Java EE环境下,一个JTA事务一般会横跨多个组件的调用(好比多个EJB组件的方法调用)。这些组件须要可以在单个事务范围内访问到一样的Persistence Context。为了知足这种状况的须要,当EntityManager被注入或经过jndi被查询的时候,它的Persistence Context将会在当前事务范围内自动传播,引用到同一个Persistence unit的EntityManager将使用一样的Persistence Context。这能够避免在不一样的组件之间传递EntityManager引用。sql

 

经过容器来传递PersistenceContext,而不是应用程序本身来传递EntityManager。这种方式(由容器管理着PersistenceContext,并负责传递到不一样的EntityManager)称为容器管理的实体管理器(Container-Managed EntityManager),它的生命周期由容器负责管理。数据库

 

有一种不常见的状况是,应用程序自身须要独立访问Persistence Context。即每次建立一个EntityManager都会迫使建立一个新的Persistence Context。这些Persistence Context即便在同一个事务范围内也不会跟其它EntityManager共享!这个建立过程能够由EntityManagerFactory的createEntityManager方法来建立。这被称为应用管理的实体管理器(application-managed entity manager)。缓存

底层事务控制

EntityManager的底层可使用JTA或RESOURCE_LOCAL类型的事务控制策略。JTA通常在容器环境中使用,而RESOURCE_LOCAL通常在J2SE的环境下使用。安全

好比,在J2SE的环境下,由应用程序自身来建立EntityManagerFactory,并由EntityManagerFactory建立EntityManager,经过EntityManager.getTransaction.begin()方法来开启事务,commit()方法提交事务等等,这种方式就是RESOURCE_LOCAL的基本使用方法。服务器

最经常使用的就是在容器环境下使用。也就是使用JTA类型的EntityManager,这样,EntityManager的调用都是在一个外部的JTA事务环境下进行的。session

Container-Managed EntityManager必须是JTA类型的EntityManager,而Application-Managed EntityManager则既能够是JTA类型的EntityManager,也能够是RESOURCE_LOCAL类型的EntityManager。oracle

配置示例:

<persistence-unit name="test" transaction-type="JTA">

Container-Managed Persistence Context

    @PersistenceContext(unitName="test")

    private EntityManager em;

persistence context的生命周期对应用程序来讲,老是被自动、透明的管理着的。也就是对应用程序自己来讲,它对persitence context的建立、销毁一无所知,彻底自动和透明。Persistence context随着JTA事务而传播。

 

一个容器管理的persistence context (即container-managed persistence context)能够被限定为单个事务范围,或,扩展其生存期跨越多个事务!这取决于当它的entity manager被建立的时候所定义的PersistenceContextType类型。它能够取值为TRANSACTION和EXTENDED。可称为:事务范围的persistence context或扩展的persistence context。

 

Persistence context老是会关联到一个entity manager factory。

 

-          Transaction-scope persistence context

当entity manager的方法被调用的时候,若是在当前JTA事务中尚未persistence context,那么将启动一个新的persistence context ,并将它跟当前的JTA事务关联。

-          Extended persistence context

扩展的persistence context 老是跟stateful session bean绑定在一块儿。当在stateful session bean中注入entity manager,并定义为extended persistence context时,从咱们开始调用stateful session bean开始,直到stateful session bean被销毁(移除)!一般,这能够经过调用一个在stateful session bean中被注解定义为@Remove的方法来结束一个stateful session bean的生命周期。

 

Application-Managed Persistence Context

即当咱们本身建立EntityManager的时候,咱们经过entityManager.close() / isOpen()方法来管理entityManager及其对应的persistence context.

 

实体对象的生命周期

几种类型:New,managed,detached,removed

 

New – 即未有id值,还没有跟persistence context创建关联的对象

Managed – 有id值,已跟persistence context创建了关联

Detached – 有id值,但没有(或再也不)跟persistence context创建关联

Removed – 有id值,并且跟persistence context尚有关联,但已准备好要从数据库中把它删除。

 

EntityManager的接口方法

 

添加:调用persist方法

* 将把一个对象持久化,若是对象的ID非空,则在调用persist方法时将抛出异常,没法持久化

 

删除:remove方法

不能直接new一个对象,而后给它的id赋值,而后删除。要删除一个对象,这个对象必须是处于持久化状态。

 

更新:merge方法

 

Find – 查找某个对象,若是查不到该对象,将返回null,至关于get

 

getReference – 查找某个对象,若是查找不到该对象,将抛出异常,至关于load

 

flush – 将实体对象由persistence context同步到底层的数据库

l  FlushMode

n  Auto – 即在同一个事务中,在查询发生前,将自动把数据从PersistenceContext持久化到数据库中。

n  Commit – 只在提交的时候,把数据从PersistenceContext中持久化到数据库中。若是设置FlushMode为commit,那么在同一个事务中,在查询以前,若是有更新的数据,这些数据是否会影响到查询的结果,这种状况,EJB3未做说明。

 

Lock – 锁定某个实体对象

实际上就是定义事务的隔离级别。总共有两种形式:READ和WRITE,表示:

不论是READ仍是WRITE,都应该可以避免脏读(读到另一个事务未提交的数据)和不可重复读(同一查询在同一事务中屡次进行,因为其余提交事务所作的修改或删除,每次返回不一样的结果集,此时发生非重复读。)

而对于WRITE来讲,还应该可以强迫版本号的增长(对那些标识了版本的对象而言)。所以,其它事务没法对其作任何更改操做!

 

Refresh – 将数据从数据库中加载到Persistenc Context。

 

Clear – 清除Persistence Context中缓存的实体对象数据

 

Contains – 测试当前Persistence Context中是否包含某实体对象

 

实体对象的监听器和回调方法

@PrePersist(在保存以前)等注解,能够被定义到Entity Bean的某些方法上面,定义其回调方法。(请参考persistenc规范3.5.1)

 

 

 

这些方法既能够被定义到Entity Bean上面,也能够被定义到Entity Listener类的方法上。

 

 

拦截器

@Interceptors(InterceptorForStudent1Manager.class) public class StudentManagerImpl implements StudentManager { public class InterceptorForStudent1Manager { @AroundInvoke public Object doit(InvocationContext context) throws Exception{ System.out.println("将要开始执行方法:"+context.getMethod().getName()); Object obj = context.proceed(); System.out.println("方法"+context.getMethod().getName()+"已被成功执行"); return obj; } }

 

 

将Interceptors定义到类或特定方法上面能够对类的全部方法或特定方法的调用进行拦截!

 

EntityManager实例操做介绍

EJB3如何实现继承关系的映射?

继承关系的映射,总共有三种策略,与Hibernate相似

 

一、SINGLE_TABLE策略:整个类继承树对应的对象都存放在一张表

 

父类定义:

@Entity @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="animalType") @DiscriminatorValue("A") public class Animal { 

 

子类定义:

 

@Entity @DiscriminatorValue(value="B") public class Bird extends Animal{

 

 

二、JOINED策略:父类和子类都对应不一样的表,子类中只存在其扩展的特殊的属性(不包含

父类的属性)

 

父类定义:

@Entity @Inheritance(strategy=InheritanceType.JOINED) public class Animal {

 

 

子类定义:

@Entity public class Bird extends Animal{ 

 

三、TABLE_PER_CLASS策略:父类和子类都对应不一样的表,子类中存在全部的属性(包含从

父类继承下来的全部属性)

@Entity @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) public class Animal { 

 

子类定义:

@Entity public class Bird extends Animal{

 

 

如何注入EntityManager对象?

一、首先要确保persistence.xml中已定义了相应的persistence-unit,好比:

<?xml version="1.0" encoding="UTF-8"?>

<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">

    <persistence-unit name="test" transaction-type="JTA">

        <jta-data-source>java:/MySqlDS</jta-data-source>

        <properties>

           <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>

          <property name="hibernate.hbm2ddl.auto" value="create"/>

          <property name="hibernate.show_sql" value="true"/>

        </properties>

    </persistence-unit>

</persistence>

 

 

二、在session bean中,直接定义EntityManager,并使用@PersistenceContext注解注入便可:

@Stateless(name="userManager") public class UserManagerImpl implements UserManager { /** * 若是只定义了一个Persistence Unit,则无需指定unitName,但一旦定义了 * 多个,则必须指定unitName */ @PersistenceContext(unitName="test") private EntityManager em;

 

 

如何知道有哪些实体类?或哪些实体类将要被映射到数据库表?

咱们学习hibernate的时候知道,hibernate有一个hibernate.cfg.xml,能够在这个文件中指定哪些实体类将要被映射到数据库表。那么EJB3中,是如何指定的呢?默认状况下,会将EJB JAR包中的全部被定义了@Entity注解的类映射到数据库表。咱们也能够经过下列方法来指定要映射的类:

    

<persistence-unit name="test" transaction-type="JTA">

        <jta-data-source>java:/MySqlDS</jta-data-source>

        <class>com.bjsxt.jpa.User</class>

        <exclude-unlisted-classes>true</exclude-unlisted-classes>

        <properties>

           <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>

          <property name="hibernate.hbm2ddl.auto" value="create"/>

          <property name="hibernate.show_sql" value="true"/>

        </properties>

    </persistence-unit>

 

正如这里所表示的同样,使用<class>标签和<exclude-unlisted-classes>标签的联合使用,能够指定咱们要映射的实体类。

 

如何由应用程序来管理EntityManager?

咱们通常状况下,都不须要由应用程序自身来管理EntityManager,最好就是使用容器管理的EntityManager对象。但某些特殊状况下,咱们可能想要由应用程序自己来管理它,那么,能够采起以下方法来办到这一点。

一、注入EntityManagerFactory对象

  

/** * 也能够注入EntityManagerFactory,手工管理PersistenceContext */ @PersistenceUnit(unitName="test") private EntityManagerFactory factory;

 

 

二、在程序中,用factory来建立和管理EntityManager对象

     

EntityManager em = factory.createEntityManager(); Student student = new Student(); student.setName("张三"); em.persist(student); em.close();

 

 

如何利用JTA实现跨越多个数据库的事务控制(一)?

JTA 自己就支持跨越多个数据库的事务控制。要实现这一点,咱们须要有以下步骤:

一、  首先是数据源的配置,为了支持两个以上的数据库,咱们必须针对每一个数据库单独配置它的数据源,下面是配置了两个MySQL数据库的示例:

<datasources>

  <local-tx-datasource>

    <jndi-name>MySqlDS</jndi-name>

    <connection-url>jdbc:mysql://localhost:3306/ejb3</connection-url>

    <driver-class>com.mysql.jdbc.Driver</driver-class>

    <user-name>root</user-name>

    <password>mysql</password>

    <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>

   

    <metadata>

       <type-mapping>mySQL</type-mapping>

    </metadata>

</local-tx-datasource>

 

  <local-tx-datasource>

    <jndi-name>MySqlDS2</jndi-name>

    <connection-url>jdbc:mysql://localhost:3306/ejb4</connection-url>

    <driver-class>com.mysql.jdbc.Driver</driver-class>

    <user-name>root</user-name>

    <password>mysql</password>

    <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>

    <metadata>

       <type-mapping>mySQL</type-mapping>

    </metadata>

  </local-tx-datasource>

</datasources>

 

二、其次,在persistence.xml中,能够定义两个以上的persistence-unit配置:

<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">

    <!-- transaction-type 也能够取值为RESOURCE_LOCAL -->

    <!-- 若是在一个EJB JAR包内定义了两个以上的persistence-unit,则在这个EJB JAR包内的全部实体类,将 同时映射到这两个persistence-unit所表明的数据库中! -->

    <persistence-unit name="test" transaction-type="JTA">

        <jta-data-source>java:/MySqlDS</jta-data-source>

        <class>com.bjsxt.jpa.User</class>

        <exclude-unlisted-classes>true</exclude-unlisted-classes>

        <properties>

           <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>

          <property name="hibernate.hbm2ddl.auto" value="create"/>

          <property name="hibernate.show_sql" value="true"/>

        </properties>

    </persistence-unit>

    <persistence-unit name="test2" transaction-type="JTA">

        <jta-data-source>java:/MySqlDS2</jta-data-source>

        <class>com.bjsxt.jpa.Person</class>

        <exclude-unlisted-classes>true</exclude-unlisted-classes>

        <properties>

           <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>

          <property name="hibernate.hbm2ddl.auto" value="create"/>

          <property name="hibernate.show_sql" value="true"/>

        </properties>

    </persistence-unit>

</persistence>

 

 

三、编写SESSION BEAN

 

@Stateless(name="userManager") public class UserManagerImpl implements UserManager { /** * 若是只定义了一个Persistence Unit,则无需指定unitName,但一旦定义了 * 多个,则必须指定unitName */ @PersistenceContext(unitName="test") private EntityManager em; @PersistenceContext(unitName="test2") private EntityManager em2; public void addUser() { User user = new User(); user.setName("张三"); em.persist(user); Person person = new Person(); person.setName("Test Person"); em2.persist(person); //若是抛出异常,将会致使整个事务回滚!这就是跨越数据库的事务管理

       throw new RuntimeException("随便一个异常"); } }

 

 

 

如何利用JTA实现跨越多个数据库的事务控制(二)?

下面,咱们的例子是,利用JTA的能力,在同一个session bean的事务中同时操纵mysql和Oracle的数据库,同样能够实现事务的管理。

 

总体过程与上面所述的过程是同样的,只是具体的实现不一样而已:

一、  配置mysql的数据源(请参考上述配置)

二、  配置oracle的数据源(请参考JBOSS中相应的数据源配置模板,注意,须要拷贝oracle的数据库驱动到jboss/server/default/lib下面)

三、  配置persistence.xml,示例以下:

<?xml version="1.0" encoding="UTF-8"?>

<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">

    <!-- transaction-type 也能够取值为RESOURCE_LOCAL -->

    <!-- 若是在一个EJB JAR包内定义了两个以上的persistence-unit,则在这个EJB JAR包内的全部实体类,将 同时映射到这两个persistence-unit所表明的数据库中! -->

    <persistence-unit name="mysql" transaction-type="JTA">

        <jta-data-source>java:/MySqlDS</jta-data-source>

        <class>com.bjsxt.jpa.User</class>

        <exclude-unlisted-classes>true</exclude-unlisted-classes>

        <properties>

           <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>

          <property name="hibernate.hbm2ddl.auto" value="create"/>

          <property name="hibernate.show_sql" value="true"/>

        </properties>

    </persistence-unit>

    <persistence-unit name="oracle" transaction-type="JTA">

        <jta-data-source>java:/OracleDS</jta-data-source>

        <class>com.bjsxt.jpa.Person</class>

        <exclude-unlisted-classes>true</exclude-unlisted-classes>

        <properties>

           <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle9Dialect"/>

          <property name="hibernate.hbm2ddl.auto" value="create"/>

          <property name="hibernate.show_sql" value="true"/>

        </properties>

    </persistence-unit>

</persistence>

 

 

 

四、  编写测试的session bean

 

@Stateless(name="userManager") public class UserManagerImpl implements UserManager { @PersistenceContext(unitName="mysql") private EntityManager mysql; @PersistenceContext(unitName="oracle") private EntityManager oracle; public void addUser() { for(int i=0; i<10; i++){ User user = new User(); user.setName("张三"+i); mysql.persist(user); } for(int i=0; i<10; i++){ Person person = new Person(); person.setName("Person"+i); oracle.persist(person); } throw new RuntimeException("随便一个异常!"); } } 

 

 

在声明式事务管理中,如何控制事务的回滚?

在spring中,咱们知道,默认状况下,抛出RuntimeException及其子类,将会致使事务的回滚,固然也能够定义rollback-for属性或not-rollback-for属性来指定相应的异常类是回滚或不回滚。

在EJB3中,回滚的策略是:

一、  默认状况下,抛出RuntimeException及其子类将致使事务回滚,其它异常不会回滚。

二、  咱们能够定义一个本身的异常类,而后定义这个异常类用注解定义为ApplicationException,指定它的回滚特性便可。

 

先看一段代码:

public class MyException extends Exception { public class MyException1 extends RuntimeException { public void addMultiPerson() throws Exception { for(int i=0; i< 10; i++){ Person4 person = new Person4(); person.setName("P"+i); em.persist(person); if(i == 5){ //默认状况下,抛出Exception及其子类不会进行回滚 //throw new MyException("抛出MyException"); //默认状况下,抛出RuntimeException及其子类会进行回滚

              throw new MyException1("抛出MyException1"); } } }

 

在上面这段代码中,已经说得很是清楚了。这是EJB3默认的回滚特性演示。

那么,如何改变这种默认策略呢?请看以下代码:

@ApplicationException(rollback=true) public class MyException extends Exception { @ApplicationException(rollback=false) public class MyException1 extends RuntimeException { public void addMultiPerson() throws Exception { for(int i=0; i< 10; i++){ Person4 person = new Person4(); person.setName("P"+i); em.persist(person); if(i == 5){ //在MyException上定义ApplicationException,并设置rollback=true,会致使抛出异常的时候,事务的回滚 //throw new MyException("抛出MyException"); //在MyException1上定义ApplicationException,并设置rollback=false,会致使抛出异常的时候,事务不回滚

              throw new MyException1("抛出MyException1"); } } }

 

 

 

 

如何定义事务的传播特性?

在容器管理的事务中,咱们能够定义事务的传播特性。

事务传播特性是指,当事务跨越多个组件的方法调用时,如何将事务由上级调用传送到下级中去:

Not Supported – 不支持,若是当前有事务上下文,将挂起当前的事务

Supports - 支持,若是有事务,将使用事务,若是没有事务,将不使用事务

Required - 须要,若是当前有事务上下文,将使用当前的上下文事务,若是没有,将建立新的事务

Required New - 须要新的事务,若是当前有事务上下文,将挂起当前的事务,并建立新的事务去执行任务,执行完成以后,再恢复原来的事务

Mandatory - 当前必需要有事务上下文,若是当前没有事务,将抛出异常

Never - 当前必须不能有事务上下文,若是有事务,将抛出异常

 

可经过@TransactionAttribute注解来定义

 

如何监听实体对象的调用?

需求是:在存储一个实体对象以后,须要主动触发调用一段代码

实现过程是:

一、假设咱们要监控Student对象的调用,那么监听器能够以下编写:

public class MyMonitor { /** * 必需带一个参数!!!!不然部署不会成功! * 添加了Listener以后,JBoss须要从新启动!! * @param student */ @PostLoad public void dosomething(Student student){ System.out.println("实体对象已经被成功加载!!!!!!name = "+ student.getName()); } }

 

二、只须要在Student实体类中指定咱们要采用哪一个类做为监听器便可

@Entity @EntityListeners(MyMonitor.class) public class Student implements Serializable{

 

 

如何拦截session bean的方法调用?

需求是:咱们想要拦截某个session bean的方法调用,可能要在拦截器中增长日志记录或安全控制之类的代码(在spring中,咱们能够经过AOP来作到这一点)

实现过程是:

一、首先实现一个拦截器类

public class InterceptorForStudent1Manager { @AroundInvoke public Object doit(InvocationContext context) throws Exception{ System.out.println("将要开始执行方法:"+context.getMethod().getName()); Object obj = context.proceed(); System.out.println("方法"+context.getMethod().getName()+"已被成功执行"); return obj; } }

 

二、在session bean 上以下定义便可:

@Stateless(name="Student1Manager") @Remote @Interceptors(InterceptorForStudent1Manager.class) public class Student1ManagerImpl implements Student1Manager {
相关文章
相关标签/搜索