#1 前面的文章索引java
詳見第一篇文章jdbc-jdbcTemplate-hibernate-jpa-springDataJpa系列(一)mysql
這裏繼續第一篇文章的內容,開始介紹jpagit
#2 jpa原生開發和事務的使用spring
##2.1 jpa的來源sql
因爲各類orm框架層出不窮,爲了統一你們,就出現了jpa這一層接口規範,不一樣的orm框架都去實現這一規範,而後咱們就只關心使用jpa的編程接口來進行編程,不用再關係底層到底使用的是那種orm框架,同時也很容易切換底層所使用的orm框架數據庫
##2.2 項目地址編程
##2.3 jpa的配置session
第一步:就是jpa的核心配置文件 persistence.xml框架
默認狀況下該配置文件存放的位置是classpath路徑下 META-INF/persistence.xml。配置文件的內容以下:ide
<persistence-unit name="test" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <properties> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" /> <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/test" /> <property name="hibernate.connection.username" value="root" /> <property name="hibernate.connection.password" value="ligang" /> <property name="hibernate.show_sql" value="true" /> <property name="hibernate.hbm2ddl.auto" value="update" /> </properties> </persistence-unit>
咱們能夠看到這個核心配置文件其實就是指明瞭jpa底層所使用的是何種orm框架,這裏稱做爲provider。而後properties裏面的內容,其實都是爲provider準備的一些配置信息。
第二步: 根據核心配置文件建立EntityManagerFactory
EntityManagerFactory entityManagerFactory=Persistence.createEntityManagerFactory("test");
第三步:根據EntityManagerFactory建立出EntityManager
EntityManager entityManager=entityManagerFactory.createEntityManager();
第四步:根據EntityManager對實體entity進行增刪改查
entityManager.persist(user)
##2.4 jpa的幾個重要對象說明
建議與Hibenrate的幾個重要的原生對象進行對比,地址Hibernate的原生xml方式開發和事務的使用
PersistenceProvider接口:
根據持久化單元名稱和配置參數建立出EntityManagerFactory,接口定義以下:
public interface PersistenceProvider { public EntityManagerFactory createEntityManagerFactory(String emName, Map map); public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map); //略 }
jpa僅僅是一層接口規範,不一樣的底層的實現者提供各自的provider。如hibernate提供的provider實現是org.hibernate.jpa.HibernatePersistenceProvider
EntityManagerFactory接口:
就是EntityManager的工廠。其實能夠相似於Hibernate中的SessionFactory。對於Hibernate來講,其實它就是對SessionFactory的封裝,Hibernate實現的EntityManagerFactory是EntityManagerFactoryImpl,源碼以下:
public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory { private final transient SessionFactoryImpl sessionFactory; //略 }
EntityManager接口:
可以對實體entity進行增刪改查,其實能夠相似於Hibernate中的Session。對於Hibernate來講,其實它就是對Session的封裝。Hibernate實現的EntityManager是EntityManagerImpl,源碼以下:
public class EntityManagerImpl extends AbstractEntityManagerImpl implements SessionOwner { protected Session session; //略 }
EntityTransaction接口:
jpa定義的事務接口,其實能夠相似於Hibernate原生的的Transaction接口。對於Hibernate來講,其實它就是對Transaction的封裝。Hibernate實現的EntityTransaction是TransactionImpl,源碼以下:
public class TransactionImpl implements EntityTransaction { private Transaction tx; //略 }
##2.5 jpa的使用案例
@Repository public class JpaDao { private EntityManagerFactory entityManagerFactory; public JpaDao(){ entityManagerFactory=Persistence.createEntityManagerFactory("test"); } public EntityManager getEntityManager(){ return entityManagerFactory.createEntityManager(); } } @Repository public class UserDao { @Autowired private JpaDao jpaDao; public void save(User user){ EntityManager entityManager=jpaDao.getEntityManager(); EntityTransaction tx=null; try { tx=entityManager.getTransaction(); tx.begin(); entityManager.persist(user); tx.commit(); } catch (Exception e) { if(tx!=null){ tx.rollback(); } }finally{ entityManager.close(); } } }
咱們能夠看到,上述的save過程和Hibernate的過程很是類似,只不過把Hibernate的那一套對象換成了對應的jpa對象。
建議與Hibenrate的使用過程進行對比,地址Hibernate的原生xml方式開發和事務的使用
#3 jpa與spring的集成
上述jpa原生方式,尚未使用dataSource,爲了引入dataSource來更好的管理數據庫鏈接,爲了簡化jpa的配置,同時能夠去掉jpa的核心配置文件,spring針對原生的jpa作了本身的集成工做。
##3.1 項目地址
##3.2 配置
第一步:配置數據庫鏈接池dataSource
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass"> <value>${jdbc.driverClass}</value> </property> <property name="jdbcUrl"> <value>${jdbc.url}</value> </property> <property name="user"> <value>${jdbc.user}</value> </property> <property name="password"> <value>${jdbc.password}</value> </property> <!--鏈接池中保留的最小鏈接數。--> <property name="minPoolSize"> <value>${jdbc.minPoolSize}</value> </property> <!--鏈接池中保留的最大鏈接數。Default: 15 --> <property name="maxPoolSize"> <value>${jdbc.maxPoolSize}</value> </property> <!--初始化時獲取的鏈接數,取值應在minPoolSize與maxPoolSize之間。Default: 3 --> <property name="initialPoolSize"> <value>${jdbc.initialPoolSize}</value> </property> </bean>
第二步:配置entityManagerFactory
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/> <property name="packagesToScan" value="com.demo.entity"/> </bean>
其中的jpaVendorAdapter實際上是收集配置參數,而後告訴LocalContainerEntityManagerFactoryBean所使用的PersistenceProvider,稍後詳細分析,配置以下:
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="database" value="MYSQL"/> <property name="showSql" value="true"/> <property name="generateDdl" value="true"/> <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/> </bean>
第三步:配置事務管理器JpaTransactionManager
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"></property> </bean>
JpaTransactionManager是依賴於entityManagerFactory的
第四步:啓動@Transactional註解的處理器
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
處理器依賴於transactionManager的
##3.3 使用案例
@Repository public class UserDao { @PersistenceContext private EntityManager entityManager; @Transactional public void save(User user){ entityManager.persist(user); } }
使用@PersistenceContext註解來獲取entityManagerFactory建立的EntityManager對象,而後使用EntityManager進行增刪改查
##3.4 原理分析
###3.4.1 如何建立EntityManagerFactory
咱們能夠看到spring是使用了一個工廠bean LocalContainerEntityManagerFactoryBean來建立entityManagerFactory。雖然配置的是一個工廠bean,可是容器在根據id來獲取bean的時候,返回的是該工廠bean所建立的實體,即LocalContainerEntityManagerFactoryBean所建立的EntityManagerFactory。
spring建立EntityManagerFactory有2中方式,以下圖所示:
LocalEntityManagerFactoryBean
它分紅2種狀況來建立
方式1:當LocalEntityManagerFactoryBean自己指定了PersistenceProvider,就使用該PersistenceProvider來建立EntityManagerFactory,詳見上文的PersistenceProvider接口定義
方式2:使用上文jpa原生方式的:
EntityManagerFactory entityManagerFactory=Persistence.createEntityManagerFactory("test");
LocalEntityManagerFactoryBean的源碼以下:
public class LocalEntityManagerFactoryBean extends AbstractEntityManagerFactoryBean { @Override protected EntityManagerFactory createNativeEntityManagerFactory() throws PersistenceException { PersistenceProvider provider = getPersistenceProvider(); if (provider != null) { // Create EntityManagerFactory directly through PersistenceProvider. EntityManagerFactory emf = provider.createEntityManagerFactory (getPersistenceUnitName(), getJpaPropertyMap()); return emf; } else { // Let JPA perform its standard PersistenceProvider autodetection. return Persistence.createEntityManagerFactory( getPersistenceUnitName(), getJpaPropertyMap()); } } //略了部份內容 }
咱們再詳細瞭解下下面的這個過程:
EntityManagerFactory entityManagerFactory=Persistence.createEntityManagerFactory("test");
它其實也是先獲取全部的PersistenceProvider,而後遍歷PersistenceProvider來建立EntityManagerFactory,源碼以下:
public static EntityManagerFactory createEntityManagerFactory(String persistenceUnitName, Map properties) { EntityManagerFactory emf = null; List<PersistenceProvider> providers = getProviders(); for ( PersistenceProvider provider : providers ) { emf = provider.createEntityManagerFactory( persistenceUnitName, properties ); if ( emf != null ) { break; } } if ( emf == null ) { throw new PersistenceException( "No Persistence provider for EntityManager named " + persistenceUnitName ); } return emf; }
那它是如何來獲取全部的PersistenceProvider的呢?
這裏就用到了<font color="red">java的SPI機制(Service Provider Interfaces)</font>。
以下簡單說明下,詳細內容能夠自行搜索:
文件裏面的內容就是該接口對應的實現類,內容以下:
org.hibernate.jpa.HibernatePersistenceProvider # The deprecated provider, logs warnings when used. org.hibernate.ejb.HibernatePersistence
這就很容易方便jpa來尋找PersistenceProvider全部的實現類
LocalContainerEntityManagerFactoryBean
它建立一個PersistenceProvider須要如下幾個重要的屬性元素
jpaVendorAdapter
它的主要做用就是收集一些配置信息,而且提供一個PersistenceProvider。接口定義以下:
public interface JpaVendorAdapter { PersistenceProvider getPersistenceProvider(); Map<String, ?> getJpaPropertyMap(); //略 }
以HibernateJpaVendorAdapter爲例,在它初始化的時候就會建立出HibernatePersistenceProvider,以下:
public class HibernateJpaVendorAdapter extends AbstractJpaVendorAdapter private final PersistenceProvider persistenceProvider; public HibernateJpaVendorAdapter() { PersistenceProvider providerToUse; try { ClassLoader cl = HibernateJpaVendorAdapter.class.getClassLoader(); Class<?> hibernatePersistenceProviderClass = cl.loadClass ("org.hibernate.jpa.HibernatePersistenceProvider"); providerToUse = (PersistenceProvider) hibernatePersistenceProviderClass.newInstance(); } this.persistenceProvider = providerToUse; //略 } }
PersistenceProvider :
它的來歷至少有兩種方式:
dataSource
再也不像原生的jpa那樣直接使用核心配置文件中的鏈接信息(這些鏈接信息是配置給PersistenceProvider的)
packagesToScan等信息
用於指定註解式實體所在的包路徑,和dataSource同樣都做爲配置信息,最終都反應在PersistenceProvider建立的EntityManagerFactory上了
###3.4.2 如何獲取EntityManager
咱們看到在例子中,是經過使用@PersistenceContext來獲取一個EntityManager的,咱們知道這個EntityManager就是經過咱們配置的上述EntityManagerFactory來建立的,但具體是一個什麼過程呢?
忽然發現這一塊源碼內容也好多,主要是PersistenceAnnotationBeanPostProcessor這個處理器在處理
@PersistenceContext註解和咱們經常使用的@Autowired是相似的,他們都是實現依賴注入的,內容仍是不少,因此打算以後另開一篇博客,單獨講解這類依賴注入的註解
至此咱們就大體瞭解jpa與Spring是怎麼集成的,總之仍是經過PersistenceProvider和配置信息來建立出EntityManagerFactory。
###3.4.3 事務過程是怎麼實現的
過程比較複雜,總之原理仍是使用ThreadLocal機制
@PersistenceContext private EntityManager entityManager; @Transactional public void save(User user){ entityManager.persist(user); throw new RuntimeException(); }
1 在執行save方法以前,使用了事務管理器JpaTransactionManager執行了開啓事務的操做
2 經過注入進來的entityManager來保存user
這個注入進來的entityManager僅僅是一個空殼子,是一個代理對象,它會獲取當前線程綁定的上述EntityManagerImpl對象,來實現保存user
3 一旦出現異常,則使用1.1步驟中建立的EntityManagerImpl中的事務TransactionImpl來實現回滾
總之,保證了業務代碼和事務代碼使用的是同一個EntityManager對象對象,因此能夠正常回滾。 總之使用@Transactional註解式的事務,總要使用ThreadLocal模式來保證業務代碼和事務代碼中的使用的connection是一致的這一原則。
#4 多數據源下jpa與spring集成開發和事務的使用
##4.1 項目地址
##4.2 配置
###4.2.1 配置兩個數據庫鏈接池dataSource
<bean id="dataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass"> <value>${jdbc1.driverClass}</value> </property> <property name="jdbcUrl"> <value>${jdbc1.url}</value> </property> //略,見項目中的配置 </bean> <!-- 配置數據源 --> <bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass"> <value>${jdbc2.driverClass}</value> </property> <property name="jdbcUrl"> <value>${jdbc2.url}</value> </property> //略,見項目中的配置 </bean>
記得根據配置建立相應的數據庫和表
###4.2.2 配置2個entityManagerFactory
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="database" value="MYSQL"/> <property name="showSql" value="true"/> <property name="generateDdl" value="true"/> <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/> </bean> <bean id="entityManagerFactory1" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource1"/> <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/> <property name="packagesToScan" value="com.demo.entity"/> </bean> <bean id="entityManagerFactory2" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource2"/> <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/> <property name="packagesToScan" value="com.demo.entity"/> <property name="persistenceUnitName" value="test2"/> </bean>
這兩個entityManagerFactory使用不一樣的dataSource,各自有一個persistenceUnitName名字(持久化單元的名字),分別叫"test1"和"test2"。在上文中,並無配置persistenceUnitName,採用默認值"default"
###4.2.3 配置2個事務管理器
<bean id="transactionManager1" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory1"></property> </bean> <bean id="transactionManager2" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory2"></property> </bean>
###4.2.4 開啓@Transactional的處理器
<tx:annotation-driven proxy-target-class="true"/>
##4.3 使用方式
@Repository public class UserDao { @PersistenceContext(unitName="test1") private EntityManager entityManager; @PersistenceContext(unitName="test2") private EntityManager entityManagerTest2; @Transactional("transactionManager1") public void save(User user){ entityManager.persist(user); } @Transactional("transactionManager2") public void save2(User user){ entityManagerTest2.persist(user); } }
@PersistenceContext(unitName="test1")表示使用persistenceUnitName="test1"的entityManagerFactory來建立EntityManager,同理@PersistenceContext(unitName="test2")也同樣。
若是有多個entityManagerFactory,可是隻使用@PersistenceContext沒有指定unitName,則會報錯,spring不知道該選擇哪個,因此須要指定unitName的名字
@Transactional("transactionManager1")表示使用id="transactionManager1"的JpaTransactionManager來做爲事務管理器 同理@Transactional("transactionManager2")也同樣。
多數據源就再也不詳細說明了
#5 spring-data-jpa的開發和事務的使用
##5.1 背景
引用IBM的一篇文章使用 Spring Data JPA 簡化 JPA 開發
Spring 對 JPA 的支持已經很是強大,開發者只需關心核心業務邏輯的實現代碼,無需過多關注 EntityManager 的建立、事務處理等 JPA 相關的處理,這基本上也是做爲一個開發框架而言所能作到的極限了。然而,Spring 開發小組並無止步,他們再接再礪,於最近推出了 Spring Data JPA 框架,主要針對的就是 Spring 惟一沒有簡化到的業務邏輯代碼,至此,開發者連僅剩的實現持久層業務邏輯的工做都省了,惟一要作的,就只是聲明持久層的接口,其餘都交給 Spring Data JPA 來幫你完成
至此,讀者可能會存在一個疑問,框架怎麼可能代替開發者實現業務邏輯呢?畢竟,每個應用的持久層業務甚至領域對象都不盡相同,框架是怎麼作到的呢?其實這背後的思想並不複雜,好比,當你看到 UserDao.findUserById() 這樣一個方法聲明,大體應該能判斷出這是根據給定條件的 ID 查詢出知足條件的 User 對象。Spring Data JPA 作的即是規範方法的名字,根據符合規範的名字來肯定方法須要實現什麼樣的邏輯
這篇文章的內容已經很詳細了,這裏就僅僅詳細地列出配置,真正跑起來
##5.2 配置
有了前面的基礎後,就很是簡單了
###5.2.1 配置dataSource
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass"> <value>${jdbc.driverClass}</value> </property> <property name="jdbcUrl"> <value>${jdbc.url}</value> </property> //略 </bean>
###5.2.2 配置entityManagerFactory
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="database" value="MYSQL"/> <property name="showSql" value="true"/> <property name="generateDdl" value="true"/> <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/> <property name="packagesToScan" value="com.demo.entity"/> </bean>
###5.2.3 配置transactionManager
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"></property> </bean>
###5.2.4 開啓@Transactional的處理器
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager"/>
###5.2.5 配置要掃描的dao的路徑
<jpa:repositories base-package="com.demo.dao"/>
使用jpa命名空間的元素,須要加入以下jpa的約束配置:
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:jpa="http://www.springframework.org/schema/data/jpa" //略 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.2.xsd">
咱們以前須要本身手寫一個UserDao,本身實現相應的方法,注入EntityManager,而後進行增刪改查,如今不須要了,只需定義一個接口便可
public interface UserDao extends CrudRepository<User,Long>{ }
CrudRepository<User,Long> 類型中的前者User表示User實體,後者Long表示User實體的主鍵類型
##5.3 使用過程
咱們只需定義上述一個接口,便可在別的地方注入使用UserDao,來進行增刪改查,以下:
@Autowired private UserDao userDao; @Test public void testSaveUser(){ User user=new User(); user.setName("王五"); user.setAge(22); userDao.save(user); }
##5.4 spring-data-jpa實現的功能介紹
上面僅僅是一個簡單的使用例子,更多複雜的例子,見IBM的一篇文章使用 Spring Data JPA 簡化 JPA 開發