Spring+Hibernate : 声明式事务

本博文来学习和总结Spring + Hibernate 的声明式事务,

  1. Spring 中配置数据源的4中方式,主要关注c3p0数据源;
  2. Spring 的PropertyPlaceholderConfigurer :将properties属性值读入到xml文件中;
  3. 事务的常见六种传播特性介绍;
  4. Spring 和 Hibernate 集成声明式事务的四大步骤;
  5. Spring 和 Hibernate 集成过程中版本冲突问题;
  6. 项目代码演示;
  7. 总结语;

1. Spring中配置数据源

1.1 Spring 自带的数据源: DriverManagerDataSource;
spring自身提供了三个没有连接池功能的数据源类(均位于org.springframework.jdbc.datasource 包中)

  • DriverManagerDatasource
  • SimpleDriverDatasource
  • SingleConnectionDatasource

    xml代码:

    <bean id="dataSource"     
        <class="org.springframework.jdbc.datasource.DriverManagerDataSource">     
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />  
        <property name="url" value="jdbc:oracle:thin:@localhost:1521:ORCL" />  
        <property name="username" value="scott" />     
        <property name="password" value="****" />  
    </bean>  

1.2 第三方数据源:dbcp 数据源和 c3p0数据源
Apache CommonsDBCP数据源地址
c3p0数据源地址

1.2.1 dbcp
dbcp的配置依赖于2个jar包commons-dbcp.jar,commons-pool.jar。

xml配置:

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"         
            destroy-method="close">         
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />  
        <property name="url" value="jdbc:oracle:thin:@localhost:1521:ORCL" />  
        <property name="username" value="scott" />     
        <property name="password" value="****" />        
    </bean>   

1.2.2 c3p0数据源

xml配置:

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"         
                destroy-method="close">        
            <property name="driverClass" value=" oracle.jdbc.driver.OracleDriver "/>        
            <property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:ORCL"/>        
            <property name="user" value="scott"/>        
            <property name="password" value="****"/>        
     </bean>   

c3p0参数配置:
官网上对各个参数及其意义及如何配置说的很清楚;

这里写图片描述

  • acquireIncrement:当连接池中的连接用完时,C3P0一次性创建新连接的数目;

  • acquireRetryAttempts:定义在从数据库获取新连接失败后重复尝试获取的次数,默认为30;

  • acquireRetryDelay:两次连接中间隔时间,单位毫秒,默认为1000;

  • autoCommitOnClose:连接关闭时默认将所有未提交的操作回滚。默认为false;

  • automaticTestTable
    C3P0将建一张名为Test的空表,并使用其自带的查询语句进行测试。

  • breakAfterAcquireFailure:获取连接失败将会引起所有等待获取连接的线程抛出异常。但是数据源仍有效保留,并在下次调
    用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。默认为
    false;

  • checkoutTimeout:当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒,默认为0;

  • connectionTesterClassName
    通过实现ConnectionTester或QueryConnectionTester的类来测试连接,类名需设置为全限定名。默认为
    com.mchange.v2.C3P0.impl.DefaultConnectionTester;

  • idleConnectionTestPeriod:隔多少秒检查所有连接池中的空闲连接,默认为0表示不检查;

  • initialPoolSize:初始化时创建的连接数,应在minPoolSize与maxPoolSize之间取值。默认为3;

  • maxIdleTime:最大空闲时间,超过空闲时间的连接将被丢弃。为0或负数则永不丢弃。默认为0;

  • maxPoolSize:连接池中保留的最大连接数。默认为15;

  • maxStatements:JDBC的标准参数,用以控制数据源内加载的PreparedStatement数量。但由于预缓存的Statement属
    于单个Connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素,如果maxStatements与
    maxStatementsPerConnection均为0,则缓存被关闭。默认为0;

  • maxStatementsPerConnection:连接池内单个连接所拥有的最大缓存Statement数。默认为0;

  • numHelperThreads:C3P0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能,通过多线程实现多个操作同时被执行。默认为3;

  • preferredTestQuery:定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个参数能显著提高测试速度。测试的表必须在初始数据源的时候就存在。默认为null;

  • propertyCycle: 用户修改系统配置参数执行前最多等待的秒数。默认为300;

  • testConnectionOnCheckout:因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的时候都
    将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable
    等方法来提升连接测试的性能。默认为false;

  • testConnectionOnCheckin:如果设为true那么在取得连接的同时将校验连接的有效性。默认为false。


1.4 JNDI数据源
Spring应用程序通常部署在Java EE应用服务器中,如JBoss,Tomcat这样的Web容器。这些服务器允许配置通过jndi获取数据源。

在spring配置文件中如下设置:

<beans>
    <jee:jndi-lookup id="datasource" jndi-name="java:comp/env/jdbc/myDataSource"/>
</beans>

在服务器中的配置以tomcat为例:

<Resource 
    name="jdbc/myDataSource" 
    type="javax.sql.DataSource"
    driverClassName="oracle.jdbc.driver.OracleDriver"
    url="jdbc:oracle:thin:@193.23.42.6:1521:myDataSource"
    username="scott" 
    password="****" 
    maxActive="80" 
/>


  • Spring 的PropertyPlaceholderConfigurer :将properties属性值读入到xml文件中

2.1 在这之前我们先看看Hibernate的文件 *hibernate.cfg.xml 是怎么配置,*

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <!-- Database connection settings -->
        <property name="connection.driver_class">oracle.jdbc.OracleDriver</property>
        <property name="connection.url">jdbc:oracle:thin:@localhost:1521:ORCL</property>
        <property name="connection.username">scott</property>
        <property name="connection.password">qian</property>
        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>

        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.OracleDialect</property>
        <!-- Disable the second-level cache  -->
        <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>
        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property
        <property name="hbm2ddl.auto">update</property>

        <mapping resource="com/qian/domain/User.hbm.xml"/>
     </session-factory>
</hibernate-configuration>

2.2 接下来,我们以读取配置文件的方式配置数据源,那么hibernate.cfg.xml如下:

hibernate.cfg.xml

<hibernate-configuration>

    <session-factory>

        <!-- 配置hibernate基本信息 -->  
        <!-- 1.数据源配置在IOC容器中,此处不需要额外配置 -->  
        <!-- 2.关联的.hbm.xml文件也可以在IOC容器配置SessionFactory时配置 -->  
        <!-- 3.此处配置hibernate的基本信息:数据库方言、SQL显示及格式化,及生成数据表的策略,二级缓存等 --> 

        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.OracleDialect</property>
        <!-- Disable the second-level cache  -->
        <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>
        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>
        <property name="hibernate.format_sql">true</property
    <property name="hibernate.hbm2ddl.auto">update</property>

        <mapping resource="com/spring/trans/domain/User.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

2.3 使用c3p0在 spring IOC容器中显示配置

applicationContext.xml

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="driverClass" value="oracle.jdbc.driver.OracleDriver"/>
    <property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:ORCL"/>
    <property name="user" value="scott"/>
    <property name="password" value="qian"/>
    <property name="initialPoolSize" value="3"/>
</bean>     

2.4 将数据源的配置信息放入到 jdbc.properties中如下:

jdbc.properties

jdbc.driver=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@localhost:1521:ORCL
jdbc.userName=scott
jdbc.password=qian
jdbc.initialPoolSize=3

2.5 应用spring的PropertyPlaceholderConfigurer将上下文(配置文件) 中的属性值在 xml 文件中以 ${key} 形式读入,

第一种方式:

<bean id="" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location">
            <value>classpath:jdbc.properties</value>
        </property>
</bean>

如要读取读个配置文件 使用方法setLocations:

<bean id="" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:jdbc.properties</value>
                <value>classpath:infors.properties</value>
            </list>
        </property>
</bean>

第二种方式:为了简化上述这样的配置,Spring 给出了<context: property-placeholder/> 元素,因此上式可以简化为:

<context:property-placeholder location="classpath:jdbc.properties"/>
<context:property-placeholder location="classpath:jdbc.properties,classpath:infors.properties"/>

此时,数据源配置如下:

<bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="${jdbc.driver}"/>
    <property name="jdbcUrl" value="${jdbc.url}"/>
    <property name="user" value="${jdbc.userName}"/>
    <property name="password" value="${jdbc.password}"/>
    <property name="initialPoolSize" value="${jdbc.initialPoolSize}"/>
</bean>

如要改动相关信息,我们只需要去改properties文件即可;



  • 事务传播特性介绍

事务:是程序中一系列严密的操作,所有操作执行必须成功完成,否则在每个操作所做的更改将会被撤销,(要么成功,要么失败)。
事务特性分为四个:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持续性(Durability)简称ACID。
Spring事务对数据库事务操作的一次封装,相当于把使用JDBC代码开启、提交、回滚事务进行了封装。

事务的传播特性: 是保证事务是否开启,业务逻辑是否使用同一个事务的保证。

  • PROPAGATION_REQUIRED:
    • 如果存在一个事务,则支持当前事务。如果没有事务则开启
  • PROPAGATION_REQUIRES_NEW:
    • 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
  • PROPAGATION_SUPPORTS:
    • 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行
    • PROPAGATION_NOT_SUPPORTED: 总是非事务地执行,并挂起任何存在的事务。
  • PROPAGATION_MANDATORY:
    • 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
  • PROPAGATION_NEVER:
    • 总是非事务地执行,如果存在一个活动事务,则抛出异常

这里写图片描述


  • Spring 和 Hibernate 集成声明式事务的四大步骤;

4.1 配置SessionFactory, 将hibernate.cfg.xml 文件交给Spring,通过Spring提供的 LocalSessionFactoryBean配置Hibernate的SessionFactory

<!-- Step 0 配置自己的dataSource, 早期我们在hibernate.cfg.xml中配置的直接 -->
<!-- 把hibernate.cfg.xml给了spring就好,现在我们在spring applicationContext.xml配置数据源 -->
<!-- ** myDataSource ** -->
<bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="${jdbc.driver}"/>
    <property name="jdbcUrl" value="${jdbc.url}"/>
    <property name="user" value="${jdbc.userName}"/>
    <property name="password" value="${jdbc.password}"/>
    <property name="initialPoolSize" value="${jdbc.initialPoolSize}"/>
</bean>
<!--Step 1:  配置Hibernate的SessionFactory,通过spring提供的 LocalSessionFactoryBean配置-->
<!-- ** mySessionFactory** -->
<bean id="mySessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
    <property name="dataSource">
        <!-- step 0中的数据源名称 -->
        <ref bean="myDataSource"/>
    </property>
    <property name="configLocation">
        <value>classpath:hibernate.cfg.xml</value>
    </property>
    <!-- 如果我们不在hibernate.cfg.xml中配置User.hbm.xml和Log.hbm。xml我们也可以在这配置 -->
    <!--  
    <property name="mappingLocations">
        <value>classpath:com/spring/trans/domain/*.hbm.xml</value>
    </property>
    -->
</bean>

4.2 配置事务管理器,将spring生产的sessionFactory注入到事务管理器上,

<!--Step 2:  配置 Spring 的声明式事物 -->  
<!-- ** myTransactionManager -->
<bean id="myTransactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
    <property name="sessionFactory">
        <!-- step 1中的SessionFactory名称 -->
        <ref bean="mySessionFactory"/>
    </property>
</bean>

4.3 配置事务的转播特性

<!--Step 3:  配置事物属性 ,需要事物管理器--> 
<!-- **myTxAdvice** -->
<!-- step 2 中的myTransactionManager名称 -->
<tx:advice id="myTxAdvice" transaction-manager="myTransactionManager">
    <tx:attributes>
        <tx:method name="add*" propagation="REQUIRED"/>
        <tx:method name="delete*" propagation="REQUIRED"/>
        <tx:method name="modify*" propagation="REQUIRED"/>
        <tx:method name="find*" propagation="REQUIRED" read-only="true"/>
    </tx:attributes>
</tx:advice>

4.4 配置事务的切点,并将事务属性和切点管理起来

<!--Step 4:  配置事物切点,并把事物属性和切点关联起来 --> 
<aop:config>
    <aop:pointcut expression="execution(* com.spring.trans.manager.*.*(..))" id="myPointcut"/>
    <!-- step 3: myTxAdvice -->
    <aop:advisor advice-ref="myTxAdvice" pointcut-ref="myPointcut"/>
</aop:config>

注意:
最后将将spring生成的SessionFactory注入给IOC容器中的实体bean,要不它找不到SessionFactory。

 <bean id="userManagerImpl" class="com.spring.trans.manager.UserManagerImpl">
    <!-- ** 将spring生成的sessionFactory注入到Ioc需要使用的类中; -->
        <property name="sessionFactory">
            <ref bean="mySessionFactory"/>
        </property>
        <property name="logManager">
            <ref bean="logManagerImpl"/>
        </property>
 </bean>


  • Spring 和 Hibernate 集成过程中版本冲突问题;

    5.1 在继承 Spring 和Hibernate即使都配置对了,还是会报各种异常,大致为,类找不到,这个好解决,加入相应的架包就可以解决; BeanCreateException等异常,我原来用的Spring 4.3 和Hibernate 5.1.10 一直出现异常,后将Hibernate 换成最新的Hibernate5.2.11就好了;所以很多时候,我们要关注是不是版本冲突导致的;



  • 项目代码示例:

这里就展示下项目结构和用到的架包,如果有需要源码的朋友,可以留邮箱或者在这里下载;

项目结构:
这里写图片描述

Spring4.3.0架包:
这里写图片描述

Hibernate5.2.11架包:
这里写图片描述

applicationContext-commons.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:tx="http://www.springframework.org/schema/tx"
     xmlns:aop="http://www.springframework.org/schema/aop"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="
     http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-4.3.xsd
     http://www.springframework.org/schema/tx
     http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">


    <!--  
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="oracle.jdbc.driver.OracleDriver"/>
        <property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:ORCL"/>
        <property name="user" value="scott"/>
        <property name="password" value="qian"/>
        <property name="initialPoolSize" value="2"/>
    </bean>     
     -->
     <!-- Step 1 -->
     <!--  
    <bean id="" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location">
            <value>classpath:jdbc.properties</value>
        </property>
    </bean>
    -->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!-- Step 2 -->
    <bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.userName}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="initialPoolSize" value="${jdbc.initialPoolSize}"/>
    </bean>

    <!-- ========================================================================================================= -->


    <!--Step 1:  配置Hibernate的SessionFactory,通过spring提供的 LocalSessionFactoryBean配置-->

    <bean id="mySessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name="dataSource">
            <ref bean="myDataSource"/>
        </property>
        <property name="configLocation">
            <value>classpath:hibernate.cfg.xml</value>
        </property>
        <!-- 如果我们不在hibernate.cfg.xml中配置User.hbm.xml和Log.hbm。xml我们也可以在这配置 -->
        <!--  
        <property name="mappingLocations">
            <value>classpath:com/spring/trans/domain/*.hbm.xml</value>
        </property>
        -->
    </bean>

    <!--Step 2:  配置 Spring 的声明式事物 -->  

    <bean id="myTransactionManager"  class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory">
            <ref bean="mySessionFactory"/>
        </property>
    </bean>


    <!--Step 3:  配置事物属性 ,需要事物管理器--> 

    <tx:advice id="myTxAdvice" transaction-manager="myTransactionManager">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="modify*" propagation="REQUIRED"/>
            <tx:method name="find*" propagation="REQUIRED" read-only="true"/>
        </tx:attributes>
    </tx:advice>


    <!--Step 4:  配置事物切点,并把事物属性和切点关联起来 --> 

    <aop:config>
        <aop:pointcut expression="execution(* com.spring.trans.manager.*.*(..))" id="myPointcut"/>
        <aop:advisor advice-ref="myTxAdvice" pointcut-ref="myPointcut"/>
    </aop:config>



<!--  
    <bean id="userManagerImpl" class="com.spring.trans.manager.UserManagerImpl">
        <property name="sessionFactory">
            <ref bean="mySessionFactory"/>
        </property>
        <property name="logManager">
            <ref bean="logManagerImpl"/>
        </property>
     </bean>

     <bean id="logManagerImpl" class="com.spring.trans.manager.LogManagerImpl">
        <property name="sessionFactory">
            <ref bean="mySessionFactory"/>
        </property>
     </bean>

-->
</beans>


补充一点:怎么在Eclipse下配置 Hibernate 和 Spring相应标签的快捷键;

  1. hibernate.cfg.xml 和 *.hbm.xml 快捷键

这里写图片描述

这里写图片描述

在下载的hibernate的文件中,找打这两个文件,
这里写图片描述

在Eclipse–》Preference–》XML–>xml catalog下如下添加:
这里写图片描述

  1. spring的 applicationContext.xml文件快捷键配置类似;
    这里写图片描述


Okay, okay. That is all, maybe too long. If you want to get the source code of this program in the blog, you can download here , or leave your email in the comments for free.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值