通过本章的学习,读者将深入掌握JPA中有关持久化上下文、事务处理的相关知识,从而能够更加深入地应用JPA。
11.1 获得EntityManager对象
那么如何获得EntityManager对象呢?这又是JPA中另外一个很重要的问题。
11.1.1 Java EE环境与J2SE环境
在详细讲述EntityManager对象之前,读者首先要分清楚两个概念,即Java EE环境与J2SE环境。因为在本章后面的学习中要经常提到这两个概念,所以读者一定要先理解它们,为以后的学习打好基础。
— Java EE环境,包括EJB容器和Web容器。
(1)Web容器:只运行Web应用的容器,例如Tomcat就是开源的Web容器,它可以运行JSP、Servlet等。
(2)EJB容器:运行在EJB组件的容器,提供EJB组件的状态管理、事务管理、线程管理、远程数据资源访问、连接管理和安全性管理等系统级服务。例如JBoss为EJB容器和Web容器(Web容器是集成了Tomcat)结合。
部署在EJB容器中的JAR包都可以认为是运行在EJB容器中。但JBoss中的Web应用,比如war包中的类就不是运行在EJB容器中,而是运行在Web容器中。
— J2SE环境
最普通Java运行环境,例如一个HelloWorld的Java程序就是运行在J2SE的环境中,通常使用main入口方法作为程序启动的触发。
如图11-1所示,它说明了Java EE与J2SE环境的关系。
11.1.2 两种类型的EntityManager对象
根据EntityManager对象的管理方式,可以有以下两种类型。
— 容器托管的(container-managed)EntityManager对象
容器托管的EntityManager对象最简单,程序员不需要考虑EntityManager连接的释放,以及事务等复杂的问题,所有这些都交 给容器去管理。容器托管的EntityManager对象必须在EJB容器中运行,而不能在Web容器和J2SE的环境中运行。本书前面讲述的 EntityManager对象都是通过注入 @PersistenceContext注释来获得的,其实,这种获得EntityManager对象的方式就是容器托管的。
— 应用托管的(application-managed)EntityManager对象
应用托管的EntityManager对象,程序员需要手动地控制它的释放和连接、手动地控制事务等。但这种获得应用托管的 EntityManager对象的方式,不仅可以在EJB容器中应用,也可以使 JPA脱离EJB容器,而与任何的Java环境集成,比如说Web容器、J2SE环境等。所以从某种角度上来说,这种方式是JPA能够独立于EJB环境运 行的基础。
理想状态下,最好是选用容器托管的EntityManager对象的方式,但在特殊的环境下,还是需要使用应用托管的EntityManager对象这种方式。
正是因为应用托管的EntityManager对象的连接释放、事务控制比较复杂,所以在使用时涉及的相关内容比较多,这些内容将在本章后面部分详细讲述,这里读者应对两种方式有一个大致的了解,两种EntityManager对象类型的比较如表11-1所示。
表11-1 容器托管与应用托管的EntityManager对象对比
比较内容 容器托管的(container-managed)EntityManager对象 应用托管的(application-managed)EntityManager对象
获得方式 两种方式:1 @PersistenceContex注入 2 JNDI获得 EntityManagerFactory创建
支持事务 JTA JTA、RESOURCE_LOCAL
运行环境 EJB容器 EJB容器、Web容器、J2SE环境
11.1.3 容器托管的(container-managed)EntityManager对象
容器托管的EntityManager对象只能运行在EJB容器中。所以可以这样理解,只有在EJB-JAR包中,才可以获得容器托管的EntityManager对象,否则只能获得应用托管的EntityManager对象。
在EJB容器中获得EntityManager对象主要有两种方式,即@PersistenceContext注释注入和JNDI方式获得。
11.1.3.1 通过@PersistenceContext注释注入
这种方式获得EntityManager对象最为常用,例如下面代码所示。
- @Stateless
- public class CustomerService implements ICustomerService {
- @PersistenceContext(unitName = "jpaUnit")
- private EntityManager entityManager;
- public List<CustomerEO> findAllCustomers() {
- Query query = entityManager.createQuery("SELECT c FROM CustomerEO c");
- List<CustomerEO> result = query.getResultList();
- for (CustomerEO c : result) {
- System.out.println(c.getId()+","+c.getName());
- }
- return result;
- }
- }
在使用此种方式创建EntityManager对象时,需要注意以下几个问题。
— @PersistenceContext注释中,其中unitName为persistence.xml文件中<persistence-unit>元素中的属性“name”的值,表示要初始化哪个持久化单元,如下所示。
Xml代码
- <persistence>
- <persistence-unit name="jpaUnit" transaction-type="JTA">
- </persistence-unit>
- </persistence>
Java代码
- @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
- public @interface PersistenceContext{
- String name() default "";
- String unitName() default "";
- PersistenceContextType type default TRANSACTION;
- PersistenceProperty[] properties() default {};
- }
— 其中PersistenceContextType可以设置创建EntityManager对象时,持久化上下文的作用范围,它的意义在于对有状态的Bean(Stateless Bean)可以跨事务操作实体。它主要有两种方式,定义如下所示。
Java代码
- public enum PersistenceContextType {
- TRANSACTION,
- EXTENDED
- }
默认情况下使用TRANSACTION,有关TRANSACTION方式和EXTENDED方式创建EntityManager对象的异同,将在下文中详细讲述,这里读者简单了解一下即可。
11.1.3.2 通过JNDI的方式获得
如果指定了@PersistenceContext注释中的name值,则设置了持久化上下文的JNDI名称。通过SessionContext可以创建EntityManager对象。
例如,下面代码为通过JNDI方式获得EntityManager对象。
- @Stateless
- @PersistenceContext(name="jpa")
- public class CustomerService implements ICustomerService {
- @Resource
- SessionContext ctx;
- public List<CustomerEO> findAllCustomers() {
- EntityManager entityManager = (EntityManager) ctx.lookup("jpa");
- Query query = entityManager.createQuery("SELECT c FROM CustomerEO c");
- List<CustomerEO> result = query.getResultList();
- for (CustomerEO c : result) {
- System.out.println(c.getId()+","+c.getName());
- }
- return result;
- }
- }
11.1.4 应用托管的(application-managed)EntityManager对象
应用托管的EntityManager对象,不仅可以在Java EE环境中获得,也可以应用在J2SE的环境中。但无论是在什么情况下获得的EntityManager对象,都是通过实体管理器工厂 (EntityManagerFactory)对象创建的。所以如何获得应用托管的EntityManager对象关键是 EntityManagerFactory对象如何获得。
下面就分别讲述在EJB容器、Web容器和J2SE环境中如何获得EntityManagerFactory对象。
11.1.4.1 EJB容器中获得
在EJB容器中,EntityManagerFactory对象可以通过使用注入@PersistenceUnit注释获得,例如下面代码为在EJB容器中,获得应用托管的EntityManager对象的方法。
- @Stateless
- public class CustomerService implements ICustomerService {
- @PersistenceUnit(unitName="jpaUnit")
- private EntityManagerFactory emf;
- public List<CustomerEO> findAllCustomers() {
- /**创建EntityManager对象*/
- EntityManager em = emf.createEntityManager();
- Query query = em.createQuery("SELECT c FROM CustomerEO c");
- List<CustomerEO> result = query.getResultList();
- for (CustomerEO c : result) {
- System.out.println(c.getId()+","+c.getName());
- }
- /**关闭EntityManager */
- em.close();
- return result;
- }
- }
通过以上的EntityManager对象代码,可以总结出以下几个问题。
— 应用托管的EntityManager对象,要在代码中手动地创建和关闭,例如下面代码所示。
EntityManager em = emf.createEntityManager();
/**其他的业务逻辑*/
em.close();
这点正是与容器托管的EntityManager对象的最大不同之处。事实上,容器托管的EntityManager对象,它的创建和关闭是由容器负责管理的,所以不需要编写代码来控制。
— 应用托管的EntityManager对象,都是通EntityManagerFactory对象来创建的。在容器中可以通过使用注入@PersistenceUnit注释的方法实现,它的定义如下所示。
- @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
- public @interface PersistenceUnit{
- String name() default "";
- String unitName() default "";
- }
其中,属性unitName为persistence.xml文件中<persistence-unit>元素中的属性“name”的值,表示要初始化哪个持久化单元,与@PersistenceContext注释中unitName属性相同。
11.1.4.2 Web容器中获得
在Web容器中,EntityManagerFactory对象也可以通过使用注入@PersistenceUnit注释获得。例如,下面代码为在 Servlet中,获得应用托管的EntityManager对象的方法。
- public class TestServlet extends HttpServlet {
- @PersistenceUnit(unitName = "jpaUnit")
- private EntityManagerFactory emf;
- public TestServlet() {
- super();
- }
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doPost(request, response);
- }
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- response.setContentType("text/html");
- PrintWriter out = response.getWriter();
- out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional //EN\">");
- out.println("<HTML>");
- out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
- out.println(" <BODY>");
- if (emf != null) {
- /**创建EntityManager 对象*/
- EntityManager entityManager = emf.createEntityManager();
- try {
- Query query = entityManager
- .createQuery("SELECT c FROM CustomerEO c");
- List<CustomerEO> result = query.getResultList();
- for (CustomerEO c : result) {
- System.out.println(c.getId() + "," + c.getName());
- }
- } finally {
- /**关闭EntityManager*/
- entityManager.close();
- }
- }
- out.println(" </BODY>");
- out.println("</HTML>");
- out.flush();
- out.close();
- }
- }
由于是通过注入方法获得的EntityManagerFactory对象,所以EntityManagerFactory对象的创建不需要手动创建和关闭,这里与在EJB容器中的获得方法相同。@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface PersistenceUnit{ String name() default ""; String unitName() default ""; }
事务管理是JPA中另一项重要的内容,了解了JPA中的事务管理,能够进一步掌握JPA的使用。
事务管理是对一系列操作的管理,它最终只有两个结果,要么成功,要么失败。一旦失败,所有的操作将回滚到初始状态。一旦成功,才最终提交,最终持久化。事务管理对银行系统最为典型。例如一个人去银行取款,他取款的钱此时大于银行账户中的钱,此时交易失败,所以取款不成功,事务回滚到操作前的状态。
在JPA中,对于实体的“CRUD”基本操作,其中涉及事务的是“C”、“U”和“D”,即“新建”、“更新”和“删除”,因为这些操作都会影响数据库中的数据变化,所以必须使用事务保证其一致性;对于“R”查询,只是查询数据,没有对数据产生变化,所以并不需要控制事务。
所以,一说到事务,读者首先应确定所使用的操作是否需要关联事务,先要界定事务所有效使用的范围。
11.4.1 事务与EntityManager
EntityManager对象的事务管理方式有两种,分别为JTA和RESOURCE_LOCAL,即Java Transaction API方法和本地的事务管理。
JPA中的事务类型通过persistence.xml文件中的“transaction-type”元素配置。例如,配置事务为JTA方式的代码如下所示。
<persistence>
<persistence-unit name="demo" transaction-type="JTA">
//其他配置省略
</persistence-unit>
</persistence>
如果使用RESOURCE_LOCAL管理事务,则配置代码如下所示。
<persistence>
<persistence-unit name="demo" transaction-type="RESOURCE_LOCAL">
//其他配置省略
</persistence-unit>
</persistence>
除了在配置文件时指明了事务的类型,不同的事务类型,不同类型的EntityManager对象,在代码中控制事务也是不同的。表11-2为不同的运行环境、不同的EntityManager对象所支持的事务类型。
表11-2 事务类型与EntityManager
运行环境 类型 | J2EE环境 | J2SE环境 | |
EJB容器 | Web容器 | ||
应用托管的EntityManager | JTA,RESOURCE_LOCAL | JTA,RESOURCE_LOCAL | RESOURCE_LOCAL |
容器托管的EntityManager | JTA | 不支持 | 不支持 |
从表11-2中可以看出,对于不同的EntityManager类型与所运行的环境,所支持的事务类型是不一样的。
其中两种情况下最为简单,一种是容器托管的EntityManager只能运行在EJB容器中,只能采用JTA的方式管理事务;另一种是J2SE环境下,只能使用应用托管的EntityManager并且只能采用RESOURCE_LOCAL的方式管理事务。本节的事务只针对这两种情况讲述,而对于应用托管的EntityManager在EJB容器和Web容器中由于都可以选择不同的事务管理方式,情况比较复杂,所以将在第11.5节中详细讲述。
11.4.2 JTA管理事务
JTA事务(Java Transaction API)是J2EE规范中有关事务的标准。它是容器级别的事务,只能运行在J2EE服务器中。它的最大优势是可以支持分布式的事务,如果系统采用的是分布式的数据库,那么只能选择JTA管理EntityManager事务。
使用JTA管理EntityManager事务时,需要注意以下几个问题。
— JTA事务只能运行在J2EE的环境中,即EJB容器中和Web容器中;而在J2SE环境中只能使用RESOURCE_LOCAL管理事务。
— 容器托管的EntityManager对象只能采用JTA的事务,而不能采用RESOURCE_LOCAL事务。
在第11.3节中,已经简单了解了一些JTA事务与EntityManager之间的关系,但当Bean的方法中又调用了另一个Bean的方法时,那么此时事务传播(Propagation)是如何进行的?下面就深入了解事务的传播与持久化上下文的关系。
有这样一个记录日志的会话Bean,它负责记录相关的日志信息等,它有一个记录日志的方法recordLog,代码如下所示。
@Stateless
public class LogService implements ILogService {
@PersistenceContext(unitName = "jpaUnit")
private EntityManager entityManager;
/**记录日志*/
public void recordLog(Integer id, String reason) {
LogEO log = new LogEO();
log.setId(id);
log.setReason(reason);
entityManager.persist(log);
}
}
此时在CustomerService的会话Bean中,addCustomer方法中需要新建客户后,再调用日志组件来记录日志信息,代码如下所示。
@Stateless
public class CustomerService implements ICustomerService {
@PersistenceContext(unitName = "jpaUnit")
private EntityManager entityManager;
@EJB
private ILogService logService ;
public CustomerEO addCustomer(CustomerEO customer) {
entityManager.persist(customer);
logService.recordLog(customer.getId(), "新建Customer");
return customer;
}
}
此时EntityManager对象是容器托管的,并且设置的事务类型为JTA。下面仔细分析一下,当在一个EJB组件中调用另外一个EJB组件时,事务的传播与持久化上下文环境的关系。
— 当客户端调用addCustomer方法时,此时容器自动关联一个JTA的事务,一个事务开始,这里将该事务记为事务A。
— 当调用persist方法持久化客户时,EntityManager对象发现当前有一个JTA的事务A,则此时将EntityManager对象的事务附加到JTA的事务A中,并且创建了一个新的持久化上下文。
— 调用日志组件的recordLog方法,容器发现调用了另外一个EJB的方法,所以首先检查当前是否存在事务,由于当前状态下存在事务A,所以将recordLog方法的事务附加到事务A中(由于默认情况下,CustomerService的事务类型是REQUIRED)。
— 当进入recordLog方法时,再次调用persist方法持久化日志时,由于此时EntityManager对象的事务是附加到JTA事务A中的,所以仍与之前调用的persist方法时所在的持久化上下文相同,所以,可以直接调用持久化客户后的customer.getId(),来获得持久化客户的Id值。虽然在一个EJB组件中调用了另外一个EJB组件的方法,但两次调用的persist方法所在的持久化上下文是相同的。
— recordLog方法结束,又回到addCustomer方法中,此时事务A提交,一个持久化上下文也就随之结束了。
11.4.3 RESOURCE_LOCAL管理事务
RESOURCE_LOCAL事务数据库本地的事务。它是数据库级别的事务,只能针对一种数据库,不支持分布式的事务。对于中小型的应用,可以采用RESOURCE_LOCAL管理EntityManager事务。
使用RESOURCE_LOCAL管理EntityManager事务时需要注意以下几个问题。
— 在J2SE环境中,只能使用RESOURCE_LOCAL管理EntityManager事务,并且EntityManager对象是以应用托管方式获得的。
— 代码中使用RESOURCE_LOCAL管理事务时,要通过调用EntityManager的getTransaction()方法获得本地事务对象。
例如,在J2SE环境中,使用RESOURCE_LOCAL管理EntityManager事务的代码如下所示。
public class CustomerClient {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpaUnit");
EntityManager entityManager = emf.createEntityManager();
try {
/** 事务开始 */
entityManager.getTransaction().begin();
CustomerEO customer = new CustomerEO();
customer.setName("Janet");
customer.setEmail("janetvsfei@yahoo.com.cn");
customer.setAsset(100000.00);
/** 事务提交 */
entityManager.getTransaction().commit();
} finally {
entityManager.close();
emf.close();
}
}
}
★ 提示 ★
采用RESOURCE_LOCAL管理事务时,要保证数据库支持事务。例如使用MySQL时,需要设置数据库的引擎类型为“InnoDB”,而“MyISAM”类型是不支持事务的。
— 在代码中,entityManager.getTransaction()方法获得本地事务EntityTransaction对象,然后通过该对象提供的方法来控制本地的事务。有关EntityTransaction的API将在下一节讲述。
— 控制本地事务时,开始一个新事务,使用begin()方法;事务完成后,使用commit()方法提交。控制事务时,并没有调用rollback()方法回滚,这是因为在事务开始后,一旦有异常抛出,EntityTransaction对象将自动回滚,所以并不需要显式地调用rollback()方法回滚。
11.4.4 EntityTransaction API
下面来看本地事务EntityTransaction中所定义的方法EntityTransaction API,以及它们的作用,如下所示。
EntityTransaction API
package javax.persistence;
public interface EntityTransaction {
public void begin();
public void commit();
public void rollback();
public void setRollbackOnly();
public boolean getRollbackOnly();
public boolean isActive();
}
下面具体来看各个方法所表示的意义,每个方法都从作用、方法参数、异常信息,以及返回值这几个方面来讲述。
— public void begin()
作用:声明事务开始。
方法参数:无。
异常信息:如果此时事务处于激活状态,即isActive()为true,将抛出IllegalStateException异常。
返回值:无返回值。
— public void commit()
作用:提交事务,事务所涉及的数据的更新将全部同步到数据库中。
方法参数:无。
异常信息:如果此时事务处于未激活状态,即isActive()为false,将抛出IllegalState Exception异常;如果此时提交不成功,则抛出RollbackException异常。
返回值:无返回值。
— public void rollback()
作用:事务回滚。
方法参数:无。
异常信息:如果此时事务处于未激活状态,即isActive()为false,将抛出IllegalState Exception异常;如果此时回滚失败,则抛出PersistenceException异常。
返回值:无返回值。
— public void setRollbackOnly()
作用:设置当前的事务只能是回滚状态。
方法参数:无。
异常信息:如果此时事务处于未激活状态,即isActive()为false,将抛出IllegalState Exception异常。
返回值:无返回值。
— public boolean getRollbackOnly()
作用:获得当前事务的回滚状态。
方法参数:无。
异常信息:如果此时事务处于未激活状态,即isActive()为false,将抛出IllegalState Exception异常。
返回值:true表示只能回滚状态。
— public boolean isActive ()
作用:判断当前事务是否处于激活状态。
方法参数:无。
异常信息:如果发生了未知的异常,将抛出PersistenceException异常。
返回值:true表示当前事务处于激活状态,false表示当前事务未处于激活状态。
11.5 应用托管的EntityManager的持久化上下文
通过表11-2所总结的各种情况,应用托管EntityManager对象在EJB容器中和Web容器中,可选择的事务类型比较复杂,既可以支持JTA,又可以支持RESOURCE_LOCAL。下面讲述在这两种情况下,如何控制事务。
11.5.1 无状态的会话Bean与JTA事务(事务范围)
在会话Bean里以注入的方式获得EntityManagerFactory对象,不需要负责它的关闭,所以此时,只需要控制EntityManager的打开和关闭。当客户端每次调用Bean中的方法时,都首先创建EntityManager对象,然后在方法结束前关闭EntityManager对象。EntityManager对象的事务使用的是容器自动管理的事务JTA。
代码如下所示。
@Stateless
public class CustomerService implements ICustomerService {
@PersistenceUnit(unitName="jpaUnit")
private EntityManagerFactory emf;
public CustomerEO findCustomerById(Integer customerId) {
EntityManager em = emf.createEntityManager();
CustomerEO customer = em.find(CustomerEO.class, customerId);
em.close();
return customer;
}
public void placeOrder(Integer customerId, OrderEO order) {
EntityManager em = emf.createEntityManager();
CustomerEO customer = em.find(CustomerEO.class, customerId);
customer.getOrders().add(order);
em.merge(customer);
em.close();
}
}
11.5.2 无状态的会话Bean与JTA事务(扩展范围)
与上个会话Bean中的管理方式不同,此时EntityManager对象为Bean的属性,当Bean初始化后,也就是标注@PostConstruct方法后,创建EntityManager对象;当Bean销毁前,也就是标注@PreDestroy方法后,关闭EntityManager对象,所以EntityManager对象是整个的Bean的声明周期中。当客户端调用需要关联事务的方法时,需要使用joinTransaction()方法合并到上一次的事务中。
代码如下所示。
@Stateless
public class CustomerService implements ICustomerService {
@PersistenceUnit(unitName="jpaUnit")
private EntityManagerFactory emf;
private EntityManager em;
@PostConstruct
public void init (){
em = emf.createEntityManager();
}
public CustomerEO findCustomerById(Integer customerId) {
/**查询不需要关联事务*/
CustomerEO customer = em.find(CustomerEO.class, customerId);
em.clear();
return customer;
}
public void placeOrder(Integer customerId, OrderEO order) {
/**
*EntityManager 对象的作用范围是这个Bean的生命周期
*所以,每次使用时要合并到上一次的事务中
*/
em.joinTransaction();
CustomerEO customer = em.find(CustomerEO.class, customerId);
customer.getOrders().add(order);
em.merge(customer);
/**
* 手动脱离当前事务和持久化上下文
*/
em.flush();
em.clear();
}
@PreDestroy
public void destroy(){
em.close();
}
}
11.5.3 有状态的会话Bean与JTA事务
同样是EntityManager对象在整个的Bean的声明周期中,但由于会话Bean此时是有状态的Bean,所以当客户端调用任何方法时,都处在同一个持久化上下文中。所以每次并不需要调用clear()方法来手动地脱离当前的上下文,但每次客户端的调用仍需要使用joinTransaction()方法合并到上一次的事务中。
代码如下所示。
@Stateful
public class CustomerService implements ICustomerService {
@PersistenceUnit(unitName="jpaUnit")
private EntityManagerFactory emf;
private EntityManager em;
private CustomerEO customer ;
@PostConstruct
public void init (){
em = emf.createEntityManager();
}
public CustomerEO findCustomerById(Integer customerId) {
customer = em.find(CustomerEO.class, customerId);
return customer;
}
public void placeOrder(Integer customerId, OrderEO order) {
em.joinTransaction();
customer.getOrders().add(order);
}
@Remove
public void destroy(){
em.close();
}
}
11.5.4 RESOURCE_LOCAL事务
前面三节的例子讲述的是JTA事务,当在J2SE环境中,必须采用RESOURCE_LOCAL事务,而且需要手动创建和关闭EntityManagerFactory、EntityManager对象。关联事务时要使用EntityManager对象的getTransaction().begin()和getTransaction().commit()方法。
代码如下所示。
public class CustomerService {
private EntityManagerFactory emf;
private EntityManager em;
public CustomerService (){
emf = Persistence.createEntityManagerFactory("jpaUnit");
em = emf.createEntityManager();
}
private CustomerEO customer ;
public CustomerEO findCustomerById(Integer customerId) {
customer = em.find(CustomerEO.class, customerId);
return customer;
}
public void placeOrder(Integer customerId, OrderEO order) {
em.getTransaction().begin();
customer.getOrders().add(order);
em.getTransaction().commit();
}
public void destroy(){
em.close();
emf.close();
}
}
===============================
包含DAO层实例
通过容器来连接数据库的,程序通过JNDI来调用
在建立项目之前先进入jboss 目录:\jboss-4.2.3.GA\docs\examples\jca 找到mysql-ds.xml ,因为我使用的是mysql所以就要它另存为其它地方,如果是oracle或db2找相对应的就OK
然后修改下数据库连接及其它设置修改如下
- <?xml version="1.0"
- encoding="UTF-8"?>
- <datasources>
- <local-tx-datasource>
- <jndi-name>demoDS</jndi-name>
- <connection-
- url>jdbc:mysql://127.0.0.1:3306/demo</connection-url>
- <driver-class>com.mysql.jdbc.Driver</driver-class>
- <user-name>root</user-name>
- <password>root</password>
- <min-pool-size>3</min-pool-size>
- <max-pool-size>10</max-pool-size>
- <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>
- <?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="demo" transaction-type="JTA">
- <jta-data-source>java:demoDS</jta-data-source>
- </persistence-unit>
- </persistence>
- public interface JpaDao {
- /**
- * 查询接口
- * @param jpaql jpaql语句
- * @param parameters 数组参数
- * @return 返回对象数组
- */
- public List<Object> find(String jpaql, Object[] parameters);
- /**
- * 查询接口
- * @param jpaql jpaql语句
- * @param parameters Map作为参数key指占位符字符串,value指具体的值对象
- * @return 返回对象数组
- */
- public List<Object> find(String jpaql, Map<String, Object> parameters);
- /**
- * 保存持久化对象
- * @param obj 持久化对象
- */
- public void save(Object obj);
- }
- @Stateless
- @Local //申明一个本地的会话bean
- public class JpaDaoImpl implements JpaDao {
- @PersistenceContext(unitName="demo") EntityManager em;
-
- @SuppressWarnings("unchecked")
- public List<Object> find(String jpaql, Object[] parameters) {
- //查询接口
- Query query = em.createQuery(jpaql);
- for (int i = 0; i < parameters.length; i++) {
- Object parameter = parameters[i];
- query.setParameter(i+1, parameter);
- }
- return query.getResultList();
- }
- @SuppressWarnings("unchecked")
- public List<Object> find(String jpaql, Map<String, Object> parameters) {
- Query query = em.createQuery(jpaql);
- Iterator<Entry<String, Object>> it = parameters.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<String, Object> entry = it.next();
- query.setParameter(entry.getKey(), entry.getValue());
- }
- return query.getResultList();
- }
- public void save(Object obj) {
- //类似hibernate.save(Object ojb);
- em.persist(obj);
- }
- }
- public interface UserService {
- public User verifyUser(String username,String password);
- public void saveUser(User user);
- public void saveUser(List<User> users);
- }
- /*这是一个可远程访问的Session Bean */
- @Stateless // @Stateless 声明这是一个无状态sessionBean
- @Remote // @Remote 声明这是一个可远程调用的sessionBean,如果未声明默认为local(本地),本地可以节省网络开销,需在同一个jvm中,内存调用
- //声明事物 事物分为两种(CMP、BMP)cmp可以理解为api事物(也可理解为用程序来控制事物),bmp可以理解为容器来处理事物(无需要写代码)
- //@TransactionManagement(TransactionManagementType.CONTAINER) 我们用的是bmp事物,我完全是用白话文说的哈,可能说的不对,见谅
- //我书读的不多,也许说得不够准确,但大概意思是说到了的,如果不明白建议google、baidu
- @TransactionManagement(TransactionManagementType.CONTAINER)
- public class UserServiceImpl implements UserService {
- //@EJB 这里指调用同一个jvm的ejb对象
- @EJB
- JpaDao jpaDao;
- /*验证用户信息*/
- public User verifyUser(String username, String password) {
- List<Object> users =jpaDao.find("from User u where u.userName=?1 and u.passWord=?2", new Object[]{username,password});
- return (User) (users.isEmpty()?null:users.get(0));
- }
- //保持用户信息
- //常用的事物类型2种
- // 1、REQUIRED 大概意思是,如果事物不存在创建一个,如果事物存在就调用当前事物,还是那句话不明白就google
- // 2、REQUIRES_NEW 大概意思,无论事物是否存在都,再次创建一个事物
- @TransactionAttribute(TransactionAttributeType.REQUIRED)//这里声明一个事物类型
- public void saveUser(User user) {
- jpaDao.save(user);
- }
- // 做一个类似批量保存,本应该抽象在dao层来做批量,但为了事物所以写在这个方法中~ 加入事物后保持一致性,如果有一条不成功,回滚
- // 这个就有点像spring的aop面向切面编程,意思就是这个方法开始之前执行一个行为,结束之前在执行一个行为,事物也就可以用在这里了
- // 调用public void saveUser(List<User> users)这个方法之前打开一个事物,当这个方法执行完之前提交事物,并关闭当前会话session
- // 自我感觉说的很明白了哈,如果还是无法理解请google..呵呵
- @TransactionAttribute(TransactionAttributeType.REQUIRED)
- public void saveUser(List<User> users) {
- for(User user : users){
- jpaDao.save(user);
- }
- }
- }