1.前言
今天来谈一下,几种管理EntityManger的方式。
2.从一个Demo说起
最近做了一个Demo,通过JBOSS数据源来管理EntityManager对象,下面为大家展示一下。
用到的工具有JBOSS5.1,mysql3.13
1.配置JBOSS数据源
可以从JBOSS的安装路径\jboss-5.0.1.GA\docs\examples\jca的安装路径中拷贝相关的数据源配置文件,其中在这个路径中有好多数据库的配置,选择Mysql.xml拷贝到\jboss-5.0.1.GA\server\default\deploy下面,具体配置如下
<span style="font-family:SimSun;font-size:18px;"><?xml version="1.0" encoding="UTF-8"?> <!-- $Id: mysql-ds.xml 41017 2006-02-07 14:26:14Z acoliver $ --> <!-- Datasource config for MySQL using 3.0.9 available from: http://www.mysql.com/downloads/api-jdbc-stable.html --> <datasources> <local-tx-datasource> <!-- JNDI名称--> <jndi-name>firstds</jndi-name> <!-- 数据库的配置--> <connection-url>jdbc:mysql://localhost:3306/test</connection-url> <!-- 驱动配置--> <driver-class>com.mysql.jdbc.Driver</driver-class> <!-- 数据库的用户名和密码--> <user-name>root</user-name> <password></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> </span>
2.JPA实体编写
<span style="font-family:SimSun;font-size:18px;">package org.crazyit.model; import javax.persistence.*; @Entity @Table(name="news_table") public class News { //消息类的标识属性 @Id /* 用于修饰标识属性 */ /* 指定该主键列的主键生成策略 */ @GeneratedValue(strategy=GenerationType.IDENTITY) private int id; //消息标题 /* @Column指定该Field映射的列信息,此处指定了列名、长度 */ @Column(name="news_title" , length=50) private String title; //消息内容 /* @Column指定该Field映射的列信息,此处指定允许为null */ @Column(nullable=true) private String content; //构造器 public News() { } //标识属性的setter和getter方法 public void setId(int id) { this.id = id; } public int getId() { return (this.id); } //消息标题的setter方法和getter方法 public void setTitle(String title) { this.title = title; } public String getTitle() { return (this.title); } //消息内容的setter方法和getter方法 public void setContent(String content) { this.content = content; } public String getContent() { return (this.content); } } </span>
3.测试的Servlet
<span style="font-family:SimSun;font-size:18px;">package lee; import javax.persistence.*; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import javax.annotation.*; import javax.transaction.*; import javax.naming.*; import org.crazyit.model.*; public class AddNewsServlet extends HttpServlet { //通过依赖注入来注入EntityManagerFactory对象 @PersistenceUnit(unitName="newsUnit") private EntityManagerFactory emf; //依赖注入容器管理的JTA事务 @Resource private UserTransaction tx; public void service(HttpServletRequest request , HttpServletResponse response) throws IOException , ServletException { request.setCharacterEncoding("GBK"); //获取请求参数 String title = request.getParameter("title"); String content = request.getParameter("content"); //创建实体对象 News news = new News(); news.setTitle(title); news.setContent(content); try { //开始事务 tx.begin(); //应用程序通过emf的createEntityManager()方法创建EntityManager EntityManager em = emf.createEntityManager(); //持久化News实体 em.persist(news); tx.commit(); //应用程序关闭EntityManager对象 em.close(); PrintStream out = new PrintStream(response.getOutputStream()); out.println("<h3>消息添加成功!</h3>"); } catch (Exception ex) { ex.printStackTrace(); } } }</span>注意:大家可以注意到,在上面的代码中通过容器管理的EntityManagerFactory,由容器通过依赖注入将JPA的EntityManagerFactory注入到Servlet。这样就保证在每次的创建过程中,都会创建新的EntityManager,防止了线程的冲突。另外需要注意的是,如果采用JTA全局事务,那么必须保证先执行UserTransaction对象的begn
4.配置Persistence.XML
<span style="font-family:SimSun;font-size:18px;"><?xml version="1.0" encoding="GBK"?> <persistence version="1.0" 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"> <!-- 在应用服务器中使用JTA全局事务 --> <persistence-unit name="newsUnit" transaction-type="JTA"> <!-- 直接使用应用服务器管理的数据源 --> <jta-data-source>java:/firstds</jta-data-source> <!-- 列出该应用所需要的所有Entity类 --> <class>org.crazyit.model.News</class> <!-- properties元素用于为特定JPA实现包配置属性 --> <!-- 下面列举的是Hibernate JPA实现中可以配置的部分属性 --> <properties> <!-- 指定连接数据库的方言 --> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLInnoDBDialect"/> <property name="hibernate.show_sql" value="true"/> <!-- 设置是否格式化SQL语句 --> <property name="hibernate.format_sql" value="true"/> <!-- 设置是否根据要求自动建表 --> <property name="hibernate.hbm2ddl.auto" value="update"/> </properties> </persistence-unit> </persistence></span>
注意:在此需要注意的是<jta-data-source>java:/firstds</jta-data-source>,后面的firstds才是jboss中的jndi的名称,前面必须是java:/
3.依赖注入EntityManager
大致的过程与上述类似,不一样的地方如下
<span style="font-family:SimSun;font-size:18px;">package lee; import javax.persistence.*; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import javax.annotation.*; import javax.transaction.*; import javax.naming.*; import org.crazyit.model.*; //为JPA持久化单元配置一个引用,指定引用名为newsUnit。 @PersistenceContext(name="newsUnit" , unitName="newsUnit") public class AddNewsServlet extends HttpServlet { //依赖注入容器管理的JTA事务 @Resource private UserTransaction tx; public void service(HttpServletRequest request , HttpServletResponse response) throws IOException , ServletException { request.setCharacterEncoding("GBK"); //获取请求参数 String title = request.getParameter("title"); String content = request.getParameter("content"); //创建实体对象 News news = new News(); news.setTitle(title); news.setContent(content); try { tx.begin(); //通过JNDI查找获取EntityManager Context ctx = new InitialContext(); EntityManager em = (EntityManager)ctx.lookup("java:/comp/env/" + "newsUnit"); //持久化News实体 em.persist(news); tx.commit(); PrintStream out = new PrintStream(response.getOutputStream()); out.println("<h3>消息添加成功!</h3>"); } catch (Exception ex) { ex.printStackTrace(); } } }</span>
注意:在事务中通过依赖查找的方式,来管理EntityManager。需要注意的地方是EntityManager em = (EntityManager)ctx.lookup("java:/comp/env/"+ "newsUnit");
4.直接注入
也可以通过JBOSS直接注入我们所需要的EntityManager
<span style="font-family:SimSun;font-size:18px;">package lee; import javax.persistence.*; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import javax.annotation.*; import javax.transaction.*; import javax.naming.*; import org.crazyit.model.*; public class AddNewsServlet extends HttpServlet { //采用依赖注入的方式注入EntityManager @PersistenceContext(unitName="newsUnit") private EntityManager em; //依赖注入容器管理的JTA事务 @Resource private UserTransaction tx; public void service(HttpServletRequest request , HttpServletResponse response) throws IOException , ServletException { request.setCharacterEncoding("GBK"); //获取请求参数 String title = request.getParameter("title"); String content = request.getParameter("content"); //创建实体对象 News news = new News(); news.setTitle(title); news.setContent(content); try { tx.begin(); //持久化News实体 em.persist(news); tx.commit(); PrintStream out = new PrintStream(response.getOutputStream()); out.println("<h3>消息添加成功!</h3>"); } catch (Exception ex) { ex.printStackTrace(); } } }</span>
注意:与第一种方式相比,只不过是换了一种标签而已。由原来的EntityManagerFactory上@PersistenceUnit(unitName="newsUnit"),换成了@PersistenceContext(unitName="newsUnit")。
5.ThreadLocal类来管理
也可以自定义一个EntityManagerUtil工具类,在这种工具类中使用ThreadLocal来保存EntityManager。
<span style="font-family:SimSun;font-size:18px;">package org.crazyit.util; import javax.persistence.*; public class EntityManagerUtil { //保存系统中的EntityManagerFactory private static final EntityManagerFactory emf; //使用ThreadLocal来保证EntityManager的线程安全 private static final ThreadLocal<EntityManager> threadLocal; /**初始化*/ static { //初始化EntityManagerFactory对象 emf = Persistence.createEntityManagerFactory("newsUnit"); threadLocal = new ThreadLocal<EntityManager>(); } //通过ThreadLocal获取EntityManager对象 public static EntityManager getEntityManager() { //获取当前线程关联的EntityManager对象 EntityManager em = threadLocal.get(); //如果当前线程关联的EntityManager为null,或没有打开 if (em == null || !em.isOpen()) { //创建新的EntityManager em = emf.createEntityManager(); threadLocal.set(em); } return em; } //关闭EntityManager对象 public static void closeEntityManager() { EntityManager em = threadLocal.get(); threadLocal.set(null); if (em != null) { em.close(); } } //开始事务 public static void beginTransaction() { getEntityManager().getTransaction().begin(); } //提交事务 public static void commit() { getEntityManager().getTransaction().commit(); } //创建查询 public static Query createQuery(String jpql) { return getEntityManager().createQuery(jpql); } }</span>