您的位置:首页 > 其它

JPA配置多数据源多persistence.xml文件

2017-05-31 16:52 519 查看

JPA的优势

1、JPA提供了对容器级特性的支持,JPA 框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。

2、JPA简单易用,集成方便,JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用javax.persistence.Entity进行注释;JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可
    以很容易的掌握。JPA基于非侵入式原则设计,因此可以很容易的和其它框架或者容器集成。

3、JPA拥有可媲美JDBC的查询能力,JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是Hibernate
HQL的等价物。JPA定义了独特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。

4、JPA支持面向对象的高级特性,JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。

5、JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何符合 JPA 标准的框架都遵循同样的架构,提供相同的访问 API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。

------------------------------------------------------------------------------------------------------------------------------------------------

          JPA提供了2个创建工厂类的实现类

1、EntityManagerFactory:它创建工厂类是通过JPA
PersistenceProvider 类的自动检测机制(根据JPA的 Java SE启动),它会自动扫描META-INF/persistence.xml文件persistenceunit名称,且它里面只能定义一个,它创建的工厂类只支持一个数据的访问,因为它是指定persistenceunit的名称创建的,spring本身其实并不对它进行设置,因此它是不支持多数据源的,相应的它的配置也相对简单(它适合于小型项目,不利于扩展,所以一般不推荐使用,它本身的设计初衷就是为了测试用的)。

    


            


           工厂类的配置信息都写在persistence.xml文件中。

           2、LocalContainerEntityManagerFactoryBean:它是让spring通过设置属性创建工厂类的办法实现的,实用性好。






在persistence.xml配置文件中的信息很少,只有这些包含指定操作的数据库对应的持久化对象,数据源,缓存信息都没有,因为另外的信息必须在spring中配置。

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

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"

    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"

    xsi:schemaLocation=" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">

    <!--开启@Transactional注解 -->

    <tx:annotation-driven transaction-manager="transactionManager" />

    <!-- 注入bean @Component、@Controller、@Service、@Repository -->

    <!-- 注入beas层 -->

    <context:component-scan base-package="com.sunxl.base.controller" />

    <context:component-scan base-package="com.sunxl.base.service" />

    <context:component-scan base-package="com.sunxl.base.dao" />

    <!-- 注入前台 -->

    <context:component-scan base-package="com.sunline.publics.controller" />

    <context:component-scan base-package="com.sunline.publics.service" />

    <context:component-scan base-package="com.sunline.publics.dao" />

    <!-- 注入后台 -->

    <context:component-scan base-package="com.sunline.privates.controller" />

    <context:component-scan base-package="com.sunline.privates.service" />

    <context:component-scan base-package="com.sunline.privates.dao" />

    <!-- 测试注入 -->

    <context:component-scan base-package="com.sunxl.test.controller" />

    <context:component-scan base-package="com.sunxl.test.service" />

    <context:component-scan base-package="com.sunxl.test.dao" />

    <!-- 读取的配置文件 -->

    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

        <property name="locations"><!-- 单个配置文件使用location,且不用使用list,多个配置文件使用locations -->

            <list>

                <value>classpath:/META-INF/system.properties</value>

                <value>classpath:/META-INF/dataSource.properties</value>

            </list>

        </property>

        <property name="fileEncoding" value="UTF-8" />

    </bean>

    <!-- 配置事务异常封装 -->

    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

    <!-- 设置解析velocity的视图 -->

    <bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">

        <property name="velocityProperties">

            <value> resource.loader=class class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader</value>

        </property>

    </bean>

    <!-- 启动对@AspectJ注解的支持 -->

    <aop:aspectj-autoproxy proxy-target-class="true" />

    <!-- 支持文件图片等的上传 -->

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />

    <!--创建数据源1,连接数据库dataSource1 -->

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">

        <property name="driverClass" value="${Mysql.sunxl.driverClass}" />

        <property name="jdbcUrl" value="${Mysql.sunxl.jdbcurl}" />

        <property name="user" value="${Mysql.sunxl.user}" />

        <property name="password" value="${Mysql.sunxl.password}" />

    </bean>

    <!--创建数据源2,连接数据库dataSource2 -->

    <bean id="dataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">

        <property name="driverClass" value="${Mysql.test.driverClass}" />

        <property name="jdbcUrl" value="${Mysql.test.jdbcurl}" />

        <property name="user" value="${Mysql.test.user}" />

        <property name="password" value="${Mysql.test.password}" />

    </bean>

    <!-- 面向切面定义的类,可以在servcie之前做一定的操作-->

    <bean id="dataSourceAspect" class="com.sunxl.base.dao.handover.DataSourceAspect" />

    <!-- 面向切面拦截所有service方法 -->

    <aop:config>

        <aop:aspect ref="dataSourceAspect">

            <aop:pointcut id="dataSourcePointcut" expression="execution(* com.sunxl.*.service.*.*(..))" />

            <aop:before pointcut-ref="dataSourcePointcut" method="intercept" />

        </aop:aspect>

    </aop:config>

    <!-- persistenceProvider接口的一种实现非必须 -->

    <bean id="persistenceProvider" class="org.hibernate.ejb.HibernatePersistence" />

    <!-- 厂商JPA的一种特定规则非必须 -->

    <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">

        <property name="generateDdl" value="false" />

        <property name="database" value="HSQL" />

    </bean>

    <!-- JPA的特定规范 -->

    <bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />

    <!-- 生成实体工厂类 -->

    <!-- *************************************开始创建第一个工厂类*************************************** -->

    <!-- 定义第一个工厂类,工厂类默认使用的id名称为entityManagerFactory,如果修改,其余的使用都需要指定工厂名,如在web.xml中使用了 -->

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

        <property name="persistenceUnitName" value="MYSQLONE" /><!-- jpa定义的名称 -->

        <property name="persistenceXmlLocation" value="classpath:/META-INF/one-persistence.xml" /><!-- jpa定义的路径 -->

        <property name="dataSource" ref="dataSource" />

        <property name="persistenceProvider" ref="persistenceProvider" />

        <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />

        <property name="jpaDialect" ref="jpaDialect" /><!--指定了如何获取连接对象、开启事务、关闭事务等事务管理相关的行为。  -->

        <property name="jpaProperties"><!-- jpa配置 -->

            <props>

                <prop key="hibernate.dialect">${Mysql.sunxl.dialect}</prop>

                <prop key="hibernate.max_fetch_depth">3</prop>

                <prop key="hibernate.bytecode.use_reflection_optimizer">true</prop>

                <prop key="hibernate.jdbc.use_streams_for_binary">true</prop>

                <prop key="javax.persistence.query.timeout">20000</prop>

                <prop key="javax.persistence.lock.timeout">5000</prop>

                <prop key="hibernate.cache.use_second_level_cache">true</prop>

                <prop key="hibernate.cache.use_query_cache">true</prop>

                <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>

                <prop key="net.sf.ehcache.configurationResourceName">classpath:/META-INF/one-ehcache.xml</prop>

                <prop key="hibernate.generate_statistics">false</prop>

                <prop key="hibernate.show_sql">true</prop>

                <prop key="jpa.showSql">false</prop>

                <prop key="hibernate.hbm2ddl.auto">update</prop>

            </props>

        </property>

    </bean>

    <!-- 开启控制层和视图层的延时加载功能,主要针对实体类中存在@ManyToMany(fetch = FetchType.LAZY)懒加载注解的实体类 -->

    <bean id="jpaInterceptor" class="org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor">

        <property name="persistenceUnitName" value="MYSQLONE" />

    </bean>

    <!-- 配置事物管理,默认使用transactionManager,如果没有,其余的使用都需要指定事物 -->

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">

        <property name="entityManagerFactory" ref="entityManagerFactory" />

    </bean>

    <!-- *****************************************完成第一个工厂类************************************* -->

    <!-- *************************************开始创建第二个工厂类*************************************** -->

    <bean id="entityManagerFactory1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

        <property name="persistenceUnitName" value="MYSQLTWO" /><!-- jpa定义的名称 -->

        <property name="persistenceXmlLocation" value="classpath:/META-INF/two-persistence.xml" /><!-- jpa定义的路径 -->

        <property name="dataSource" ref="dataSource1" />

        <property name="persistenceProvider" ref="persistenceProvider" />

        <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />

        <property name="jpaDialect" ref="jpaDialect" />

        <property name="jpaProperties"><!-- jpa配置 -->

            <props>

                <prop key="hibernate.dialect">${Mysql.sunxl.dialect}</prop>

                <prop key="hibernate.max_fetch_depth">3</prop>

                <prop key="hibernate.bytecode.use_reflection_optimizer">true</prop>

                <prop key="hibernate.jdbc.use_streams_for_binary">true</prop>

                <prop key="javax.persistence.query.timeout">20000</prop>

                <prop key="javax.persistence.lock.timeout">5000</prop>

                <prop key="hibernate.cache.use_second_level_cache">true</prop>

                <prop key="hibernate.cache.use_query_cache">true</prop>

                <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>

                <prop key="net.sf.ehcache.configurationResourceName">classpath:/META-INF/two-ehcache.xml</prop>

                <prop key="hibernate.generate_statistics">false</prop>

                <prop key="hibernate.show_sql">true</prop>

                <prop key="jpa.showSql">false</prop>

                <prop key="hibernate.hbm2ddl.auto">update</prop>

            </props>

        </property>

    </bean>

    <!-- 开启控制层和视图层的延时加载功能,主要针对实体类中存在@ManyToMany(fetch = FetchType.LAZY)懒加载注解的实体类 -->

    <bean id="jpaInterceptor1" class="org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor">

        <property name="persistenceUnitName" value="MYSQLONE" />

    </bean>

    <!-- 配置事物管理,默认使用transactionManager,如果没有,其余的使用都需要指定事物 -->

    <bean id="transactionManager1" class="org.springframework.orm.jpa.JpaTransactionManager">

        <property name="entityManagerFactory" ref="entityManagerFactory1" />

    </bean>

    <!-- *****************************************完成第一个工厂类************************************* -->

    <!-- 开启映射并注入延时加载 -->

    <bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">

        <property name="interceptors">

            <list>

                <ref bean="jpaInterceptor" />

                <ref bean="jpaInterceptor1" />

            </list>

        </property>

    </bean>

</beans>

到了这里就我们就定义了2个工厂类了,有多少个就可以配置多少个。

之后就到了我们想要指定使用那个数据源,一般有2种思路:

第一种情况:定义一个注解,之后使用spring面向切面的功能,设定在访问所有的service层时设定EntityManager,这里要注意一定需要给它个默认的EntityManager,要不然你每次都需要设定,如果有默认的EntityManager,就只需要在修改的时候加上注解,其余的时候都使用默认的EntityManager(这种方法容易漏写);

第二种情况:定义多个basedao,操作指定的类就使用指定的EntityManager,优点操作简单,不易出错,效率更高,缺点出现重复代码,我这里用的就是第二种。















注意事物依赖于EntityManager对象,所以多个EntityManager就会有多个事物,因此一个事物方法里面有多个dao,就等于有多个事物,默认只开启一个,所以在basedao里面的CUD需要判断事物是否开启,因为JPA进行CUD操作默认需要开启事物,否则抛出


Executing an update/delete query


,网上有人说这个错误是未开启事务,这是对的,但说在service上面加上@transactional就可以解决就不一定了,因为一个方法里面有多个dao,并对多个entity进行了CUD操作,解决办法




,每个CUD操作都判断一遍,是否开启了事物,没有开启就开启,但不要提交,也不要回滚,让@transactional去提交和回滚,不过这样写还有一个问题,那就是多线程并发的时候,因为多线程同时执行一个方法时,它就还是使用的一个对象,所以就表示还是使用一个一个事物,可能这个线程只是开启事物,那个线程就提交了事物,这就出现了数据错误,但这个问题还没想好怎么解决,我下面项目中用的是对并发的数据操作使用SunxlConnectionFactory操作,后期看下能不能修改为安全的JPA。

这样就能很方便的找到对应的数据源,而且不用重复写baseDao,使用注释的也可以,删掉下面的super.em=emf.createEntityManager();,但

EntityManager是线程不安全的,并发和多线程可能造成脏数据,使用这种方法也有个不好的地方,就是会为每个连接创建一个EntityManager,可能影响性能,还可以通过锁机制等方法解决。

项目路径:https://github.com/xionglangs/xionglang

          
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: