zoukankan      html  css  js  c++  java
  • Spring 事务配置管理,简单易懂,详细 [声明式]

    Spring 事务配置说明

    Spring 如果没有特殊说明,一般指是跟数据存储有关的数据操作事务操作;对于数据持久操作的事务配置,一般有三个对象,数据源,事务管理器,以及事务代理机制;

    Spring 提供了多种的底层数据源实现,以及多种类型的事务管理器;所有的管理器都基于 PlatformTransactionManager 接口实现各自的事务策略;

    Spring 事务管理采用 AOP 切面代理技术实现,AOP 用于分隔关注点,保证事务的原子性,采用一定的技术 把该关注点 (weaving) 织入到 待完善的关注点上,实现单独组件无法实现的功能,以解决面向对象编程在某些方式下难于实现的操作,更好的支持面向对象的开关原则(扩展开放,修改关闭)。

    底层数据源配置

    首选加载 数据源配置 .properties 文件;

    <bean id="loadProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:META-INF/mybatis/mysql.properties</value>
                    <value>classpath:META-INF/spring/hibernate.properties</value>
                </list>
            </property>
    </bean>

    mysql.properties:

    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/springdb
    username=root
    password=xxxxx
    filters=stat
    initialSize=2
    maxActive=300
    maxWait=60000
    timeBetweenEvictionRunsMillis=60000
    minEvictableIdleTimeMillis=300000
    validationQuery=SELECT 1
    testWhileIdle=true
    testOnBorrow=false
    testOnReturn=false
    poolPreparedStatements=false
    maxPoolPreparedStatementPerConnectionSize=200

    hibernate.properties:

    # hibernate.X
    hibernate.connection.driverClass=org.gjt.mm.mysql.Driver
    hibernate.connection.url=jdbc:mysql://localhost:3306/springdb?useUnicode=true&amp;characterEncoding=utf-8
    hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
    hibernate.connection.username=root
    hibernate.connection.password=xxxxx
    hibernate.show_sql=true
    hibernate.hbm2ddl.auto=create-drop

    1. JDBC 方式:

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="${driver}"></property>
            <property name="url" value="${url}"></property>
            <property name="username" value="${username}"></property>
            <property name="password" value="${password}"></property>
    </bean>

    2. c3p0 方式:

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
            destroy-method="close">
            <!-- 指定连接数据库的驱动 -->
            <property name="driverClass" value="com.mysql.jdbc.Driver"/>
            <!-- 指定连接数据库的URL -->
            <property name="jdbcUrl" value="jdbc:mysql://localhost/springdb"/>
            <!-- 指定连接数据库的用户名 -->
            <property name="user" value="root"/>
            <!-- 指定连接数据库的密码 -->
            <property name="password" value="xxxxx"/>
            <!-- 指定连接数据库连接池的最大连接数 -->
            <property name="maxPoolSize" value="40"/>
            <!-- 指定连接数据库连接池的最小连接数 -->
            <property name="minPoolSize" value="1"/>
            <!-- 指定连接数据库连接池的初始化连接数 -->
            <property name="initialPoolSize" value="1"/>
            <!-- 指定连接数据库连接池的连接的最大空闲时间 -->
            <property name="maxIdleTime" value="20"/>
    </bean>

    3. dbcp 方式:

    <beans>
      <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
       <property name="driverClassName" value="${driver}"></property>
       <property name="url" value="${url}"></property>
       <property name="username" value="${username}"></property>
       <property name="password" value="${password}"></property>
    </bean>

    4. Alibaba Druid 方式:

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close" init-method="init" >
            <property name="url" value="${url}?useUnicode=true&amp;characterEncoding=utf-8"></property>
            <property name="driverClassName" value="${driver}"></property>
            <property name="username" value="${username}"></property>
            <property name="password" value="${password}"></property>
            <property name="filters" value="${filters}"></property>
            <property name="maxActive" value="${maxActive}"></property>
            <property name="maxWait" value="${maxWait}"></property>
            <property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}"></property>
            <property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}"></property>
            <property name="validationQuery" value="${validationQuery}"></property>
            <property name="testWhileIdle" value="${testWhileIdle}"></property>
            <property name="testOnBorrow" value="${testOnBorrow}"></property>
            <property name="testOnReturn" value="${testOnReturn}"></property>
            <property name="poolPreparedStatements" value="${poolPreparedStatements}"></property>
            <property name="maxPoolPreparedStatementPerConnectionSize" value="${maxPoolPreparedStatementPerConnectionSize}"></property>
    </bean>

    5. JNDI 全局配置方式:

    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">      
         <property name="jndiName" value="${jndiName}"></property> 
    </bean>

    6. 自定义 DataSource:

    <bean id="dataSource" class="me.study.hnmapper.utils.CustomDataSource">
       <property name="driverClass" value="oracle.jdbc.driver.OracleDriver"></property>
       <property name="driverUrl" value="jdbc:oracle:thin:@10.30.2.204:1527:sec"></property>
       <property name="username" value="apps"></property>
       <property name="password" value="secapp29"></property>
    </bean>

    CustomDataSource:

    package me.study.hnmapper.utils;
    
    import java.io.PrintWriter;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.sql.SQLFeatureNotSupportedException;
    import java.util.logging.Logger;
    
    import javax.sql.DataSource;
    
    public class CustomDataSource implements DataSource {
    
        private String driverClass;
        private String driverUrl;
        private String username;
        private String password;
    
        @Override
        public PrintWriter getLogWriter() throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }
    
        @Override
        public void setLogWriter(PrintWriter out) throws SQLException {
            // TODO Auto-generated method stub
    
        }
    
        @Override
        public void setLoginTimeout(int seconds) throws SQLException {
            // TODO Auto-generated method stub
    
        }
    
        @Override
        public int getLoginTimeout() throws SQLException {
            // TODO Auto-generated method stub
            return 0;
        }
    
        @Override
        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
            // TODO Auto-generated method stub
            return null;
        }
    
        @Override
        public <T> T unwrap(Class<T> iface) throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }
    
        @Override
        public boolean isWrapperFor(Class<?> iface) throws SQLException {
            // TODO Auto-generated method stub
            return false;
        }
    
        @Override
        public Connection getConnection() throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }
    
        @Override
        public Connection getConnection(String username, String password)
                throws SQLException {
            // TODO Auto-generated method stub
            // TODO Auto-generated method stub
            Connection conn = null;
            try {
                Class.forName(driverClass);
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
            conn = DriverManager.getConnection(driverUrl, username, password);
    
            return conn;
        }
    
        public String getDriverClass() {
            return driverClass;
        }
    
        public void setDriverClass(String driverClass) {
            this.driverClass = driverClass;
        }
    
        public String getDriverUrl() {
            return driverUrl;
        }
    
        public void setDriverUrl(String driverUrl) {
            this.driverUrl = driverUrl;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
    }

    6. Hibernate方式,利用的是SessionFactory作为数据源操作;

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <property name="mappingLocations" >
                <list>
                    <value>classpath*:model/*.hbm.xml</value>
                </list>
            </property>
            <!-- packagesToScan可以自动搜索某个package的全部标记@Entity class -->
            <!-- 
            <property name="packagesToScan">
                <list>
                    <value>hibernatelibs.model*</value>
                </list>
            </property>
             -->
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
                    <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                    <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                </props>
            </property>
    </bean>

    TransactionManager 事务管理器配置

    1. JDBC TransactionManager 配置;

    <bean id="transactionManager" 
            class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!-- 配置DataSourceTransactionManager时需要依注入DataSource的引用 -->
            <property name="dataSource" ref="dataSource"/>
    </bean>

    2. hibernate TransactionManager 配置:

    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

    3. Jta TransactionManager 配置: 

    <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

    Jta 在应用服务器依赖于 JNDI 查找,也可基于 (ObjectWeb)JOTM 或 Atomikos 这样的 userTransaction 来实现。

    PlatformTransactionManager 是 Spring 事务策略核心的接口,Spring 并不支持事务的实现,而是负责包装底层的事务,负责分离各种基于PlatformTransaction接口的具体实现。
    应用底层支持什么样的事务策略,Spring 就支持什么样的事务策略。
    Spring 声明式事务管理是基本 AOP 切面代理技术实现,是基本基于XML 或 Annotation 配置的一种实现,PlatformTransaction 为各种管理机制提供统一操作接口,各种类型的管理机制都得基于 PlatformTransaction 接口实现具体的事务处理,因此,Spring 事务配置可以很容易的在各种类型的事务管理机制间切换。

    PlatformTransaction 提供的接口如下:

    public interface PlatformTransactionManager {  
           TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;  
           void commit(TransactionStatus status) throws TransactionException;  
           void rollback(TransactionStatus status) throws TransactionException;  
    } 

    比较常用的事务机制还有:(Jdo)JdoTransactionManager,(Jpa)JpaTransactionManager。此外还有:WebSphereUowTransactionManager、WebLogicJtaTransactionManager等类型;

    事务代理机制

    1. 使用 pointcut 通知方式 (最常见的方式):

     首先配置 tx 命名空间:

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

    配置通知项 <tx:advice

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="*"  /> <!-- 匹配到类下方法的切入点, 比如 query* 表示匹配所有以 query开头的方法 , 这里可以配置事务行为 传播行为propagation, 隔离级别isolation, 超时(秒)timeout_xx, 自读read-only 等 -->
            </tx:attributes>
    </tx:advice>

    <tx:method 配置项:

    name: 用于匹配类中的方法,spring 只支持 类下的方法的形式,对于模式匹配,可以支持 perl 的正则表达式,也支持 aspectj ,默认支持 AspectJ;

    <tx:method name="*" ... /> <!-- 匹配所有方法 -->
    <tx:method name="get*" ... /> <!-- 匹配所有以 get 开头的方法名 -->
    <tx:method name="save*" ... /> <!-- 匹配所有以 save 开头的方法名 -->
    ...

    isolation: 

    1、Serializable:最严格的级别,事务串行执行,资源消耗最大;
    
    2、REPEATABLE_READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
    
    3、READ_COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。
    
    4、Read_Uncommitted:保证了读取过程中不会读取到非法数据。隔离级别在于处理多事务的并发问题。
    
    5、Default 默认。

    propagation:  Spring 支持七种传播行为,比 EJB 的 CMT 还多一种;

    1. Required=>PROPAGATION_REQUIRED: 有逻辑事务就加入执行,没有就创建一个新的;
    2. RequiresNew=>PROPAGATION_REQUIRES_NEW: 表示每次都会创建一个新事务,事务间不互相影响;
    3. Supports=>PROPAGATION_SUPPORTS: 有逻辑事务就加入,没有就以非事务方式执行;
    4. NotSupported=>PROPAGATION_NOT_SUPPORTED: 不支持事务,有事务时,暂停,以非事务方式执行;
    5. Mandatory=>PROPAGATION_MANDATORY: 以事务方式执行,没有事务时,抛出异常;
    6. Never=>PROPAGATION_NEVER: 不支持事务,有事务时,抛出异常;
    7. Nested=>PROPAGATION_NESTED: 有事务时,嵌入事务内执行,没有时创建新的事务;(内部事务异常不影响外部事务,外部事务异常会影响到内部的事务)

    timeout="5" 或 timeout_xx: 比如: timeout_5; 表示超时 5 秒;

    完整的一个 tx:method:

    <tx:method propagation="REQUIRED" isolation="REPEATABLE_READ" timeout="5" read-only="true" />

    另外一种配置方式,是配置在具体bean内的:

    <bean id="xxxxDao">
        <!-- ... -->
        <property name="transactionAttributes">  
                <props>  
                    <prop key="*">PROPAGATION_REQUIRED,REPEATABLE_READ,timeout_5,readOnly</prop>
                </props>  
        </property>
    </bean>

    配置 aop:config :

    <aop:config>
            <!-- 表示在所有切入点加注事务管理 -->
            <aop:pointcut expression="execution(* springlibs.service.*.*(..))" id="puintCutid" /> <!-- 需要配置 component-scan -->
            <aop:advisor advice-ref="txAdvice" pointcut-ref="puintCutid" />
    </aop:config>

    expression表达式 : execution(* springlibs.service.*.*(..))

    第一个 * : 表示所有的返回值类型;

    第二个 * : 表示所有的类;

    第三个 * : 表示所有的方法;

    切入点表达式例子:

    1>. execution(* springlibs.service.*.*(..)) 表示 springlibs.service 下所有的类跟所有的方法;

    2>. execution(* springlibs.service.*.save*(..)) 表示 springlibs.service 下所有的以 save 开头的方法;

    3>. execution(public * springlibs.service.*.*(..)) 表示 springlibs.service 下所有的公共方法;

    pointcut 表示的是一些连接点的集合,使用 expression 来表达这种集合;advisor 这里就当做连接 pointcut切入点 与 advice通知 的作用;

    2. 一个 bean 对应一个事务;

    配置 dao 实现 bean:

    <bean id="xxxDaotarget" class="springlibs.dao.xxxDaoImpl">
        <!-- 
        property name="sessionFactory" ref="sessionFactory" />
        -->
    </bean>

    在 Dao 上配置事务:

    <bean id="xxxDao"  
            class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">  
               <!-- 配置事务管理器 -->  
               <property name="transactionManager" ref="transactionManager" />     
            <property name="target" ref="xxxDaotarget" />  
            <!-- 配置事务属性 -->  
            <property name="transactionAttributes">  
                <props>  
                    <prop key="*">PROPAGATION_REQUIRED</prop>
                </props>  
            </property>  
    </bean>  

    3. 所有 bean 共享一个基类:

    <bean id="transactionBase"  
                class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"  
                lazy-init="true" abstract="true">  
            <!-- 配置事务管理器 -->  
            <property name="transactionManager" ref="transactionManager" />  
            <!-- 配置事务属性 -->  
            <property name="transactionAttributes">  
                <props>  
                    <prop key="*">PROPAGATION_REQUIRED</prop>  
                </props>  
            </property>  
    </bean>    

    在 Dao 上配置事务:

    <bean id="xxxDao" parent="transactionBase">
            <property name="target">
                <bean id="xxxDaoImpl" class="springlibs.dao.xxxDaoImpl"></bean>
            </property>
    </bean>

    4. 使用拦截器:

    <bean id="transactionInterceptor"  
            class="org.springframework.transaction.interceptor.TransactionInterceptor">  
            <property name="transactionManager" ref="transactionManager" />  
            <!-- 配置事务属性 -->  
            <property name="transactionAttributes">  
                <props>  
                    <prop key="*">PROPAGATION_REQUIRED</prop>  
                </props>  
            </property>  
    </bean>
          
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">  
            <property name="beanNames">  
                <list>  
                    <value>*Dao</value>
                </list>  
            </property>  
            <property name="interceptorNames">  
                <list>  
                    <value>transactionInterceptor</value>  
                </list>  
            </property>  
    </bean>

     在 Dao 上配置事务;

    <!-- 配置DAO实现 -->
    <bean id="xxxDao" class="springlibs.dao.xxxDaoImpl">
            <!--
            <property name="sessionFactory" ref="sessionFactory" />
            -->
    </bean>

    5. 使用 Annotation 方式:

    <tx:annotation-driven transaction-manager="transactionManager"  proxy-target-class="true"/>

    proxy-target-class 为 true: 使用 CGLib 创建增加的代理;为 false 就是使用 标准 JDK 将会被创建;

    可以在 类级别或方法 上使用 @Transaction 注入;

    加注 @Transaction 的类或方法,本身并没有事务行为,能被识别为事务的,其实是 tx:annotation-driven 的配置开启了事务;

  • 相关阅读:
    centos7.9安装mysql,远程无法连接的问题
    netcore 自定义脚手架
    mongodb查询出某个字段最大值
    解决Docker容器内不能使用vim命令的问题
    git 撤销修改和版本回退
    【转】一文读懂PCA算法的数学原理
    【转】Maximal Information Coefficient (MIC)最大互信息系数详解与实现
    【转】带约束的多目标优化进化算法综述
    论文快报-2021-10-Multi-task optimization and evolutionary multitasking
    【Vegas原创】SQL Server数据库备份、差异备份、日志备份脚本
  • 原文地址:https://www.cnblogs.com/editor/p/4054498.html
Copyright © 2011-2022 走看看