JPA in Spring

  JPA(Java Persistence API):Sun官方提出的Java持久化規範,定義了對象-關係映射(ORM)以及實體對象持久化的標準接口。Sun引入JPA出於兩個緣由:1、簡化現有Java EE和Java SE應用開發工做;2、整合多種ORM框架(Hibernate、TopLink、JDO等),在Java開發平臺上造成統一的ORM標準。java

  Spring Data JPA:爲簡化JPA的開發提供幫助。EntityManager的建立與銷燬、事務管理等代碼被抽取出來,交由Spring統一管理,在沒有特殊需求的狀況下,開發人員再也不須要關心這些;LocalContainerEntityManagerFactoryBean提供了很是靈活的配置方式,JPA規範中的配置信息(persistence.xml )能夠由其以屬性注入的方式進行配置;經過繼承Repository接口,Spring能夠爲開發人員自動實現Dao層的大部分操做(增刪改查、分頁、審 計等)。mysql

  下面就介紹一下在實際項目中使用過的Spring Data JPA相關技術。首先看如何配置LocalContainerEntityManagerFactoryBean。spring

<bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="packagesToScan" value="org.mmh.entity" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
        </property>
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.connection.driver_class">com.mysql.jdbc.Driver</prop>
                <prop key="hibernate.connection.url">jdbc:mysql://localhost:3306/ttf</prop>
                <prop key="hibernate.connection.username">root</prop>
                <prop key="hibernate.connection.password">mmh</prop>
                <prop key="hibernate.c3p0.min_size">10</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
            </props>
        </property>
    </bean>

  Spring提供了三種方式創建EntityManagerFactory,LocalEntityManagerFactoryBean、LocalContainerEntityManagerFactoryBean和Obtaining an EntityManagerFactory from JNDI。LocalEntityManagerFactoryBean用於簡單應用或測試,經過META-INF/persistence.xml整合其它ORM框架提供的JPA實現。Obtaining an EntityManagerFactory from JNDI用於Java EE應用服務器。LocalContainerEntityManagerFactoryBean用於獨立運行的應用程序,它使Spring可以徹底控制JPA。經過該方式,開發人員能在一個文件內完成JPA的全部配置。packagesToScan屬性使EntityManagerFactory自動掃描指定包內的實體類。jpaVendorAdapter屬性定義Hibernate爲JPA的提供者,這裏Spring採用了適配器模式將Hibernate集成進來。jpaProperties屬性詳細定義了Hibernate的配置參數,這個配置方式與常規的作法不一樣。通常來說,數據庫鏈接池會被單獨定義,而後集成到JPA中,而在這裏是經過Hibernate來管理全部涉及數據庫的配置,這樣不只簡化了配置,並且可以使模塊之間的關係更清晰。再加上Hibernate早已棄用了dbcp,並本身集成了c3p0,因此經過Hibernate自身來管理數據庫鏈接池應該是最穩妥的方式。Hibernate自己有鏈接池,官網上介紹它的鏈接池功能只是用做開發和測試,在實際的項目中須要集成第三方產品,經過設置hibernate.c3p0.*屬性就能啓動c3p0鏈接池。sql

  而後再進行JPA的事務管理和Dao配置。數據庫

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    
    <tx:annotation-driven transaction-manager="transactionManager" />

    <jpa:repositories base-package="org.mmh.dao"
        transaction-manager-ref="transactionManager"
        entity-manager-factory-ref="entityManagerFactory" />

  Spring爲JPA提供了專門的事務管理器,JpaTransactionManager,而且經過tx:annotation-driven標籤自動掃描代碼中添加了@Transactional的類和public成員函數。jpa:repositories標籤可以自動爲指定包內的繼承了Repository<T, ID extends Serializable>的Dao接口提供事務處理功能。緩存

  對於事務管理須要專門說明一下。首先,管理的對象必須是能夠進行事務操做的資源,例如數據庫、消息隊列、緩存等,而且這些資源要提供事務管理的功能。使用MyISAM引擎的MySQL 數據庫自己不支持事務,因此無須在Spring中配置事務管理器,由於即便配置了,也沒有實際用處。但Hibernate的事務管理和一級緩存有密切的關 系:當調用save、update等方法時,Hibernate並不直接向數據庫發送SQL語句,而是在提交事務或flush一級緩存時才真正向數據庫發送SQL。因此,即便數據庫不支持事務,Hibernate的事務管理也是有必定好處的,不會對數據操做的效率形成負面影響。也就是說Spring集成Hibernate後,不論數據庫是否支持事務,均可以進行事務管理的操做。甚至當Hibernate的connection.autocommit爲true時,Spring仍然可以控制事務。服務器

  另外,事務的屬性能夠經過@Transactional標籤進行設置。比較重要的屬性:傳播行爲,默認是PROPAGATION_REQUIRED,它表示有事務環境時就加入到已有的環境中,沒有就新建一個。好比,在Service層聲明瞭事務管理,同時Dao層由於繼承Repository接口也默認聲明瞭事務管理。當Service層中的函數調用Dao層中的函數時,兩個函數共用一個事務,即外層函數的事務。Spring中事務傳播行爲有七種,各有用途,詳細說明能夠參考相關資料。事務操做,Repository接口定義的增刪改默認是read和write,而查詢默認爲只讀。事務回滾,默認是對unchecked異常回滾,對checked異常不回滾。事務隔離級別,默認是DEFAULT,它與數據庫自己事務操做有關,若是沒有特殊要求,能夠忽略它。事務超時,默認是無,能夠設置以秒單位的時間。app

  寫一段代碼測試一下上面講述的內容。主程序初始化Spring容器,而後獲取處理帳戶的業務層Bean,最後保存一個帳戶實體。框架

package org.mmh.main;

import org.mmh.entity.Account;
import org.mmh.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Application {

    public static void main(String[] args) throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "appContext.xml");

        AccountService service = context.getBean(AccountService.class);

        Account account = new Account();
        account.setUserName("yzp");
        account.setPassword("123");
        account.setPhoneSN("1234567890");

        service.saveAccount(account);
    }
}

  在業務層中,實現一個處理帳戶的業務,只有保存帳戶的功能。爲了驗證Spring事務管理的有效性,在保存完一個新帳戶後打印出數據庫中帳戶的記錄數,而後執行一個除以0的操做,系統會拋出unchecked異常,最後致使事務回滾,在數據庫裏查詢不到新的帳戶記錄。函數

package org.mmh.service;

import org.mmh.dao.AccountDao;
import org.mmh.entity.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class AccountService {
    
    @Autowired
    private AccountDao accountDao;
    
    public void saveAccount(Account account) throws Exception {
        accountDao.save(account);
        
        System.out.println("帳戶記錄數: " + accountDao.count());
        
        int foo = 1 / 0;
    }
}
//輸出結果:
/*
* Hibernate: select account0_.userName as userName1_0_0_, account0_.password as password2_0_0_, 
* account0_.phoneSN as phoneSN3_0_0_ from t_account account0_ where account0_.userName=? * Hibernate: insert into t_account (password, phoneSN, userName) values (?, ?, ?) * Hibernate: select count(*) as col_0_0_ from t_account account0_ * 帳戶記錄數: 3 * Exception in thread "main" java.lang.ArithmeticException: / by zero
*/

  以上是Spring JPA以及事務管理的默認配置,從效果來看,其功能已經能夠知足大多狀況下的需求了。在實際項目中最有可能改動的地方也就是讓checked異常也能實現事務回滾。要實現這個功能很簡單,標記@Transactional(rollbackFor=Exception.class)。這樣全部繼承Exception的異常都能觸發事務回滾了。固然也能夠選擇在配置文件中經過AOP聲明事務。熟悉AOP的話,配置起來也很簡單,原理跟註釋聲明事務同樣。

相關文章
相關標籤/搜索