儘管如今開源的框架已經很是優秀,可是缺少統一的標準有違軟件開源的初衷,所以Sun公司的JCP組織發佈了Java EE的JPA標準,並統一ORM規則、JPQL查詢語言、子查詢、高級查詢和批量處理等操做。推出JPA規範有兩點緣由:一是,但願簡化現有的SE和EE工做,特別是EJB3.0的推出,使得企業級項目的開發推向更高的層次(EJB3.0已經很是優秀,可是因爲時間的問題,而且Spring已經佔領了市場);二是,但願業界有一個統一的標準,就像當年JDBC推出同樣,只實現接口,其他API的實現交給項目廠商或者組織去完成。 java
實際項目開發應該儘可能使用JPA的API編程,爲何要這樣作?JPA是一個規範,不是產品,最終實現交給Hibernate這些ORM框架去實現,即便之後咱們要改變ORM的底層,也只須要簡單更改一下配置便可,JPA仍是屬於javax.persistence.*的包裏面,稍微有些不一樣的是主鍵的生成策略不一樣的ORM規範有所差別,但基本原理仍是同樣。 mysql
下面將分hibernate實現和整合Spring實現兩種案例說明JPA整合的好處。 spring
因爲Spring、Hibernate和log4j的maven配置比較繁瑣,這裏只提供版本管理: sql
<properties> <!--Spring版本號--> <spring.version>4.0.5.RELEASE</spring.version> <!--hibernate--> <hibernate.version>4.3.0.Final</hibernate.version> <!--log4j日誌文件管理包版本--> <slf4j.version>1.6.6</slf4j.version> <log4j.version>1.2.12</log4j.version> <!--項目編碼級別--> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!--spring jpa--> <spring-data-jpa.version>1.4.1.RELEASE</spring-data-jpa.version> <hibernate-jpa-api.version>1.0.1.Final</hibernate-jpa-api.version> </properties>
POJO實體 數據庫
因爲使用註解方式建立,所以本文將不建立orm.xml文件,而且JPA的註解也很是容易看懂,所以註解內容不作詳述,下面建立一個用戶實體對象,對應數據庫中的表爲t_user; 編程
import javax.persistence.*; import java.io.Serializable; /** * @author Barudisshu */ @Entity(name = "USER") @Table(name = "t_user",uniqueConstraints = {@UniqueConstraint(columnNames = {"username"})}) public class UserInfo implements Serializable{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 但願用戶名是惟一的 @Basic @Column(name = "username") private String username; @Basic @Column(name = "password") private String password; // 若是Id生成策略被指定,不該該有帶參數的構造函數 // 省略getter和setter方法 }
由於@Id中的Id字段是惟一的,若是生成策略已經指定,使用persist(Object obj)方法時將拋出異常,所以,要麼改成merge方法,要麼去掉@GeneratedVale註解,由於persist會尋找遊離態的實體是根據Id進行標識,若是沒有則自行建立。 api
默認persistence.xml文件處於根目錄的META-INF文件下,而且名稱固定,若是使用想使用Maven指定,須要配置ant插件: 安全
<plugins> <!--若是想指定persistence.xml位置,則添加以下插件--> <plugin> <artifactId>maven-antrun-plugin</artifactId> <version>1.3</version> <executions> <execution> <id>copy-test-persistence</id> <phase>process-test-resources</phase> <configuration> <tasks> <!--backup the "proper" persistence.xml--> <copy file="${project.build.outputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml.proper"/> <!--replace the "proper" persistence.xml with the "test" version--> <copy file="${project.build.testOutputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/> </tasks> </configuration> <goals> <goal>run</goal> </goals> </execution> <execution> <id>restore-persistence</id> <phase>prepare-package</phase> <configuration> <tasks> <!--restore the "proper" persistence.xml--> <copy file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/> </tasks> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin> </plugins>
OK,前面已經說過,只使用註解方式,不使用xml方式,因此persistence.xml使用類加載的方式。 服務器
<?xml version='1.0' encoding='utf-8'?> <persistence 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" version="2.1"> <!--持久化單元,使用JTA必須架構在JBOSS服務器上--> <persistence-unit name="person_pu" transaction-type="RESOURCE_LOCAL"> <!--hibernate jpa提供者爲HibernatePersistenceProvider--> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <!--持久化對象--> <class>ls.jpa.entity.UserInfo</class> <properties> <!--基本屬性--> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/db_test"/> <property name="javax.persistence.jdbc.user" value="root"/> <property name="javax.persistence.jdbc.password" value="***"/> <!--hibernate配置--> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="true"/> </properties> </persistence-unit> </persistence>
注意:JPA2.0和JPA1.0有較大差異,JPA2.0是提供了類型安全的查詢機制、豐富的表達式等功能,而且已經從EJB中獨立開來。 架構
單元測試類不用顯式加載persistence.xml文件,到底persistence.xml文件是如何加載的爲何名稱是固定的?筆者找了好久,原來是是在org.hibernate.jpa.boot.internal.PersistenceXmlParser類下定義的,若是單純從Persistence類下是找不到的,由於hibernate.jpa.boot包下會加載全部配置文件並轉換爲properties屬性存儲在Map中,而後經過工具類或者經過ClassLoader進行屬性訪問。下面爲單元測試代碼:
import ***; /** * @author Barudisshu */ public class LogonTest { private static final Logger logger = Logger.getLogger(LogonTest.class); private static final String PERSISTENCE_UNIT_NAME = "person_pu"; @Test public void simpleTests(){ // Obtaining an entity manager EntityManagerFactory factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME); EntityManager entityManager = factory.createEntityManager(); // Read the existing entries and write to console TypedQuery<UserInfo> query = entityManager.createQuery("select u from USER u", UserInfo.class); List<UserInfo> userList = query.getResultList(); // List user name for (UserInfo userInfo : userList) { logger.info(userInfo.getUsername()); } logger.info("Size: " + userList.size()); // Simple transaction entityManager.getTransaction().begin(); UserInfo userInfo = new UserInfo(); userInfo.setUsername("Barudisshu"); userInfo.setPassword("88888888"); entityManager.persist(userInfo); entityManager.getTransaction().commit(); // Close the EM and EMF when done entityManager.close(); factory.close(); } }
單純使用JPA很難知足業務上的需求,所以能夠經過Spring進行集成,進而架構成爲邏輯清晰的業務層次。首先添加相應的Maven管理包,以下:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>${spring-data-jpa.version}</version> </dependency>
爲了達到分層的架構,一下分別添加Dao層和Service層,代碼清單以下:
Dao層,自動裝配持久化上下文:
import ***; /** * @author Barudisshu */ @Repository public class UserDao { @PersistenceContext private EntityManager em; @Transactional public UserInfo save(UserInfo userInfo){ em.persist(userInfo); return userInfo; } }Service層,建立服務:
import ***; /** * @author Barudisshu */ @Service public class UserService { @Autowired private UserDao userDao; @Transactional public UserInfo create(String name,String password){ UserInfo userInfo = new UserInfo(); userInfo.setUsername(name); userInfo.setPassword(password); return userDao.save(userInfo); } }
上述代碼沒有使用JpaTemplate模版,自spring3.1以後就沒有了,多是JPA規範作得足夠好的緣由,就像自spring3.2.x以後再也不支持HibernateTemplate同樣。
Spring整合Hibernate時,Spring能夠經過容器來管理Hibernate的SessionFactory;相似地,Spring整合JPA時,Spring能夠經過容器管理JPA的EntityManagerFactory。Spring爲JPA EntityManagerFactory管理提供了兩種方式:
LocalEntityMangerFactoryBean功能比較有限,只能經過persistence.xml內容的屬性構建,所以不能使用Spring容器中已有的DataSource,也不能切換全局事務。而LocalContainerEntityManagerFactoryBean則恰好彌補了這方面的不足,而且可使用Spring容器中已有的數據源,這樣persistence.xml就不用編寫任何數據源了。
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="ls.jpa"/> <bean id="emf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean"> <!--注入JPA持久化單元--> <property name="persistenceUnitName" value="person_pu"/> </bean> <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="emf"/> </bean> <!--開啓AOP監聽--> <aop:aspectj-autoproxy expose-proxy="true"/> <!--使用聲明式事務--> <tx:annotation-driven transaction-manager="txManager"/> </beans>OK,全部配置已經完成,下面進行簡單的單元測試:
import ***; /** * @author Barudisshu */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:spring-config.xml"}) @Transactional @TransactionConfiguration(transactionManager = "txManager",defaultRollback = true) public class SimpleTest { private static final Logger logger = Logger.getLogger(SimpleTest.class); @Autowired private UserService userService; @Test public void userTests(){ UserInfo userInfo = userService.create("Barudisshu","liter"); logger.info(JSON.toJSONString(userInfo)); } }
單元測試綠條經過後,打開數據庫能夠看到,已經自動爲你建立t_user用戶表:
Well done!