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的话,配置起来也很简单,原理跟注释声明事务同样。

相关文章
相关标签/搜索