Spring事务

回顾一下事务的特性:

事务:即多条对数据的操作组成的集序列

事务的四大特性(ACID)

原子性:将多条对数据进行操作的序列视为一个整体,要么全部执行成功,要么全部执行失败

持久性:事务一旦提交成功,数据的改变是永久的,即使数据库发生故障也不会影响数据!

一致性:事务的提交前后,数据完整必须保持一致

隔离性:数据库面对多个访问时候要为每个访问开启一个事务,且多条事务独立执行,不能相互影响

事务的隔离级别

 产生的问题

脏读:就是读取到事务没有提交的数据!

  解决方案:对当前表加上表级别的读锁

不可重复读:两次读取结果不一致:

    解决方案:对当前操作的表加上行级写锁(其实大多时候改变只是针对行的,必要时可以加表级写锁)

幻读:就是数据库表中数据操作时候,读取数据显示没有,但是插入该数据却显示存在!

  解决方案:加上表级别的写锁 :

注意:并不是隔离级别越高越好,串行化(表级写锁)是一个重量级操作,会影响数据库的执行效率!

Spring的事务管理

1 Spring提供的事务管理器: PlatformTransactionManager

主要用到就是该接口的实现类:

DataSourceTransactionManager 用于Spring JDBC或MyBatis

HibernateTransactionManager 适用于Hibernate,对这个框架不熟,暂时不做描述

接口中的三个主要方法:(提交事务,获取事务状态,回滚)

2 TransactionDefinition接口,主要定义事务的一些基本的信息

获取事务定义名称

String getName()

获取事务的读写属性

boolean isReadOnly()

获取事务隔离级别

int getIsolationLevel()

获事务超时时间

int getTimeout()

获取事务传播行为特征

int getPropagationBehavior()

3 TransactionStatus接口事务的状态:

boolean isNewTransaction();是否新开启

boolean hasSavepoint();是否具有回滚存储点

void setRollbackOnly();设置事务处于回滚状态

boolean isRollbackOnly();获取事务是否处于回滚状态

void flush();刷新事务

boolean isCompleted();获取事务是否处于完成状态

事务实现

编程式:

maven:

 <dependencies>
        <!--Spring基本-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!--Spring管理jdbc基本-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!-- MyBatis基本-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.3</version>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--Druid数据库连接控制池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>
        <!--Spring控制MyBatisd基本-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.0</version>
        </dependency>
        <!--支持AOP编程-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
    </dependencies>

主配置文件:

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--引入配置的properties文件-->
    <context:property-placeholder location="classpath:*.properties"/>

    <!--数据库连接池配置信息-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="service" class="service.imp.ServiceImp">
        <property name="dao" ref="dao"/>
        <!--不使用AOP创建事务管理器时候需要用到-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--javabean测试阶段没有构造-->
        <property name="typeAliasesPackage" value="domain"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="dao"/>
    </bean>


    <!--AOP-->
    <!--通知类-->
    <bean id="txAdvice" class="aop.Advice">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <aop:config>
        <aop:pointcut id="pt" expression="execution(public void service.imp.ServiceImp.addDate(..))"/>
        <aop:aspect ref="txAdvice">
            <aop:around method="AOP001" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>


</beans>

jdbc配置文件:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db
jdbc.username=root
jdbc.password=root

 mybatis的mapper:注意文件名字必须和接口,名字相同,否则找你不到映射文件,同时需要和接口文件在同一个目录下!也就是在idea的配置文件目录下创建和接口文件相同的包级别的文件目录!

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.Dao">
    <update id="addDate">
          update message set money = money + #{money} where  id= #{id}
    </update>
    <!--
        <update id="inMoney">
            update account set money = money + #{money} where name = #{name}
        </update>

        <update id="outMoney">
            update account set money = money - #{money} where name = #{name}
        </update>
    -->
</mapper>

AOP通知类(这块为了方便,其实可以不写)

public class Advice {
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public Object AOP001(ProceedingJoinPoint pjp) throws Throwable {
        //开启事务
        PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource);
        //事务定义
        TransactionDefinition td = new DefaultTransactionDefinition();
        //事务状态
        TransactionStatus ts = ptm.getTransaction(td);
        System.out.println("AOP......Runing");

        //执行原始方法!
        Object ret = pjp.proceed(pjp.getArgs());
        //提交事务
        ptm.commit(ts);
        return ret;
    }
}

 Dao:

public interface Dao {
    void addDate(@Param("id") String id, @Param("money") double money);
}

 Service:

public interface ServiceTest {

    void addDate(String id, double money);
    void addDateNoAOP(String id, double money);

}

 实现:

public class ServiceImp implements ServiceTest {
    private Dao dao;
    private DataSource dataSource;

    public void setDao(Dao dao) {
        this.dao = dao;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void addDate(String id, double money) {
        //使用Aop开启事务处理
        dao.addDate(id, money);
    }

    @Override
    public void addDateNoAOP(String id, double money) {
        //获取事务管理器
        PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource);
        //事务定义
        TransactionDefinition td = new DefaultTransactionDefinition();
        //事务状态
        TransactionStatus ts = ptm.getTransaction(td);
        //执行原始方法
        dao.addDate(id, money);
        //提交事务时候将事务的状态信息放入作为参数即可
        ptm.commit(ts);

    }
}

 主要加载类:

public class APP {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //强转向父类转,使用子类类型强转会出现类型适配错误
        ServiceTest serviceImp = (ServiceTest) ctx.getBean("service");
        //serviceImp.addDateNoAOP("001",200);
        serviceImp.addDate("001",200);
    }
}

 编程式基本就是这样!

声明式(xml)

参考这个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: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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
      <!--引入主要的配置文件-->
    <context:property-placeholder location="classpath:*.properties"/>
     <!--数据库连接池的基本配置-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
       <!--MyBatis的包扫描-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.domain"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.dao"/>
    </bean>

    <bean id="accountService" class="com.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!--引入事务的管理类-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--定义事务管理的通知类-->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <!--定义控制的事务-->
        <tx:attributes>
            <!--将所有的方法进行拦截-->
            <tx:method name="*" read-only="false"/>
            <!--将拦截后的方法进行再次的过滤,根据情况进行相关事务-->
            <!--1:以get/find开头的查询数据的方法,设置为只读-->
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <!--2:满足这类方法通配的执行事务为写(会改变数据)-->
            <tx:method name="a" read-only="false" propagation="REQUIRED"/>
            <tx:method name="b" read-only="false" propagation="NEVER"/>

            <!--准确锁定方法-->
            <!--解释:read-only          是否只读
                    timeout            超时时间,单位秒
                    isolation          事物的隔离级别
                    no-rollback-for    设置事务中不回滚的异常(多个之间用逗号隔开)
                    rollback-for       设置事务中必须回滚的异常
                     propagation       事物的传播方式(7种)
            -->

            <tx:method
                    name="transfer"
                    read-only="false"
                    timeout="-1"
                    isolation="DEFAULT"
                    no-rollback-for=""
                    rollback-for=""
                    propagation="REQUIRED"
            />

        </tx:attributes>
    </tx:advice>

    <aop:config>
        <!--主要业务涉及到综合业务的,应该在servicec层进行相关的操作,所以service和dao/mapper都应该进行监控-->
        <aop:pointcut id="pt" expression="execution(* com.service.*Service.*(..))"/>
        <aop:pointcut id="pt2" expression="execution(* com.dao.*.b(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt2"/>
    </aop:config>


</beans>

注意小点:

aop:advice与aop:advisor区别:

AOP:advice可以是任意的Java对象,但是advisor必须要实现advice的五个接口:

MethodBeforeAdvice

AfterReturningAdvice

ThrowsAdvice...

简言之:一个用于事务。一个就是普通的AOP编程。想了解更多,打开源码集合百度即可

 半注解形式实现: 

其实只是看xml文件,正常加载配置文件,改变的只是接口上加了注解标注(下面接口实现示例一部使用的就是注解标注),xml中开启事务注解,了解xml文件和注解关系的几乎秒懂,在Spring家族中注解和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: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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:property-placeholder location="classpath:*.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.itheima.domain"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.itheima.dao"/>
    </bean>

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!--                  TX格式                  -->
    <!--                  TX格式                  -->
    <!--                  TX格式                  -->

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <tx:annotation-driven transaction-manager="txManager"/>

    <!--<tx:advice id="txAdvice" transaction-manager="txManager">-->
        <!--<tx:attributes>-->
            <!--<tx:method-->
                <!--name="transfer"-->
                <!--read-only="false"-->
                <!--timeout="-1"-->
                <!--isolation="DEFAULT"-->
                <!--no-rollback-for=""-->
                <!--rollback-for=""-->
                <!--propagation="REQUIRED"-->
                <!--/>-->
        <!--</tx:attributes>-->
    <!--</tx:advice>-->

    <!--<aop:config>-->
        <!--<aop:pointcut id="pt" expression="execution(* com.itheima.service.*Service.*(..))"/>-->
        <!--<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>-->
    <!--</aop:config>-->


</beans>

声明式(注解)

service接口:

示例一:注解操作其实就是替换xml中的注释部分的配置

/对当前接口的所有方法添加事务
@Transactional(isolation = Isolation.DEFAULT)
public interface AccountService {

    /**
     * 转账操作
     * @param outName     出账用户名
     * @param inName      入账用户名
     * @param money       转账金额
     */
    //对当前方法添加事务,该配置将替换接口的配置
    @Transactional(
        readOnly = false,
        timeout = -1,
        isolation = Isolation.DEFAULT,
        rollbackFor = {},   //java.lang.ArithmeticException.class, IOException.class
        noRollbackFor = {},
        propagation = Propagation.REQUIRED
        )
    public void transfer(String outName, String inName, Double money);

}

示例二:

public interface AccountService {
    @Transactional
    public void transfer(String outName, String inName, Double money);

}

Spring的主配置文件:


@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JDBCConfig.class,MyBatisConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}

涉及到的核心注解:

 @Transactional 类注解方法注解,一般用于接口,标注该接口上开启事务

@EnableTransactionManagement 类注解,Spring的核心配置文件注解,开启事务!

到这基本上完成了Spring事务实现的总结!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值