2017年7月20日星期四

How to Make Two or More Persistence Units in one persistence.xml Work in Your JPA Related Project

JPA is not a new concept for the people who works with Java and J2EE related technologies. In fact, as a specification, JPA has a lot of different implements. For example, Wildfly from JBoss, Spring and Hibernate. To use JPA in a project, we need a file called persistence.xml which defines data sources, JDO classes, data operation details and some special properties. You can find tons of simple samples of persistence.xml on Internet, but most of them only include one data source inside. So, we are going to talk about something different, a little bit more complicated scenario here: Define two data sources inside one persistence.xml. And we will mention something you may need to know to handle this kind of persistence.xml.

Let us start with a sample first.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
 <persistence-unit name="user-module" transaction-type="JTA">
         <description>RFID Module Persistence Unit</description>
  <jta-data-source>java:jboss/datasources/DS1</jta-data-source>
  <class>org.jsgarage.example.User</class>
  <class>org.jsgarage.example.Role</class>
  <class>org.jsgarage.example.Permission</class>
  <exclude-unlisted-classes/>
  <properties>
   <property name="hibernate.hbm2ddl.auto" value="update" />
   <property name="hibernate.show_sql" value="false" />
   <property name="hibernate.format_sql" value="false" />
   <property name="hibernate.use_sql_comments" value="false" />
   <property name="hibernate.jdbc.fetch_size" value="1000" />
   <property name="hibernate.jdbc.batch_size" value="50" />
  </properties>
 </persistence-unit>
 <persistence-unit name="delivery-module" transaction-type="JTA">
  <jta-data-source>java:jboss/datasources/DS2</jta-data-source>
         <class>org.jsgarage.example.Article</class>
         <class>org.jsgarage.example.Order</class>
         <class>org.jsgarage.example.Product</class>
         <exclude-unlisted-classes/>
         <properties>
             <property name="hibernate.hbm2ddl.auto" value="validate" />
   <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServer2008Dialect"/>
  </properties>
 </persistence-unit> 
</persistence>

Let us take a look what we have in the persistence.xml.

Element
Attribute
Properties
Description
persistence-unit Includes data source, JDO classes and special properties. Defines transaction management type.
transaction-type JTA means you are using Container Transaction Management. Such as inside a J2EE container. RESOURCE_LOCAL means you manage transactions by yourself.
jta-data-source Data source defined inside an application server can be referred with a JNDI naming.
class class listed here should be the one with @Entity annotation.
exclude-unlisted-classes Without this element showing up, your JPA implement may not be able to finish its job because it try to map all the classes can be found in the persistence.xml, not matter those classes belong to which unit. Ridiculous, right?
hibernate.hbm2ddl.auto update means your JPA implement will try to update database table schemas with your JDO class entity definition. validate means the schemas will be checked to make sure consistent.
hibernate.dialect Let your JPA implement know what kind of Database is behind the data source.
Note: some simple and easy to understand properties have been ignored. You alwarys can find some official explaination with Google.

It looks like simple enough, right? But be ready, there are some rules you need to obey if you want to have more than one data source included in your persistence.xml:

  • Make sure you have <exclude-unlisted-classes> element within your <persistence-unit>. Otherwise, the unit will try to grab all the classes inside this xml file and fail to map those tables which are not existed in this unit related data source. Of course, if your design is to have exact the same schemas in all the data sources defined in this xml file, it would be fine. But I would be very curious about the purpose.
  • Set property hibernate.hbm2ddl.auto value carefully. "update" will generate or alter the table schemas in your database automatically during the initial mapping process. It is a risk to apply in a production environment. Sure enough, if you have no <exclude-unlisted-classes> defined here, you may be glad to see all the data sources will have same tables inside. "validate" will not cause this mess, but any difference between your JDO class entity and table schemas in DB will fail the initial mapping process.
  • Property hibernate.dialect can help your JPA implement to know what kind of DB behind the data source. Although the definition may include the database type with it, but one brand may have a huge range of different versions. If you JPA implement need it, let it know.
After you defined persistence units in your persistence.xml, don't forget to use annotation to make them work in your java project. And you can define your own annotations to distingguish the different EntityManager instances during the @Inject process. Example here:

 @Produces
 @UserDBQualifier
 @PersistenceContext(unitName = "user-module")
 private EntityManager emUser;
 
 @Produces
 @DeliveryDBQualifier
 @PersistenceContext(unitName = "delivery-module")
 private EntityManager emDelivery;

At last, if you are using Wildfly and Switchyard, congratulations, Switchyard module may not be able to let you use Container Management Transaction if your codes are not part of the Switchyard Service or not a interface defined on the module. But do not worry, UserTransaction can serve you well, like this:


 @Resource(lookup = "java:jboss/UserTransaction")
 private UserTransaction userTransaction;

没有评论:

发表评论