Spring-事务管理

一、事务回顾

事务:事务指数据库中多个操作合并在一起形成的操作序列。

事务的作用:
在这里插入图片描述
在这里插入图片描述

二、事务管理

1、Spring事务核心对象

在这里插入图片描述

3个API:

1)、PlatformTransactionManager:平台事务管理器

为接口,最终要操作的是他的实现类,它的实现类非常多:
在这里插入图片描述
在这里插入图片描述
此接口定义了事务的基本操作

获取事务:
TransactionStatus getTransaction(TransactionDefinition definition)
// 参数为事务定义对象,返回的是事务的状态
提交事务:
void commit(TransactionStatus status)
回滚事务:
void rollback(TransactionStatus status)
2)、TransactionDefinition:事务定义对象

TransactionDefinition,此接口定义了事务的基本信息

获取事务定义名称: String getName()

获取事务的读取属性: boolean isReadOnly()

获取事务隔离级别: int getIsolationLevel()

​ 值有:int ISOLATION_DEFAULT=-1, int ISOLATION_READ_UNCOMMITTED=1…

获取事务超时时间: int getTimeout() 默认:int TIMEOUT_DEFAULT=-1 (永不超时)

获取事务传播行为特征: int getPropagationBehavior()

3)、TransactionStatus:事务状态

此接口定义了事务在执行过程中某个时间点上的状态信息及对应的操作状态

1、获取事务是否处于新开启事务状态

boolean isNewTransaction()

2、获取事务是否处于已完成状态

boolean isCompleted()

3、获取事务是否处于回滚状态

boolean isRollbackOnly()

4、刷新事务状态

void flush()

5、获取事务是否具有回滚存储点

boolean hasSavepoint()

6、设置事务处于回滚状态

void setRollbackOnly()

2、编程式事务

ApplictionContext.xml 业务层需要注入dataSource

<?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:p="http://www.springframework.org/schema/p"
       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/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd">
    <!--加载配置文件,1导入约束content,2通过context标签设置,3,定义成bean对象-->
    <context:property-placeholder location="classpath:*.properties"/>
    <!--    加载druid数据源-->
    <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>
    <!--accoutDao的bean不许定义,使用代理自动生成,需要在AccountServiceImpl定义accountDao,dataSource的Set方法-->
    <bean id="accountService" class="com.lvmanba.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>
    <!--    spring整合myhabtis后控制的创建连接的对象-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean" >
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.lvmanba.domain" />
    </bean>
    <!--    加载myhabtis映射配置的扫描,将其作为spring的bean进行管理-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.lvmanba.dao"/>
    </bean>
<!--    设置AOP-->
    <bean id="txAdvice" class="com.lvmanba.aop.TxAdvice">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <aop:config>
        <!-- 切入点-->
        <aop:pointcut id="pt" expression="execution(* *..transfer(..))"/>
        <aop:aspect ref="txAdvice">
            <aop:around method="transactionManager" pointcut-ref="pt" />
        </aop:aspect>
    </aop:config>

AccountServiceImpl

public void transfer(String outName, String inName, Double money) {
    //原始代码: 如果在没有出现异常的情况下,可以正常运行,如果出现异常,将会导致金额出错
    /*accountDao.inMoney(outName,money);
    accountDao.outMoney(inName,money);*/
    //代码改进,使用事务
    //1,开启事务(使用接口)为事务设置与数据层相同的数据源
    PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource); //使用构造方法进入数据源,或者可以使用下面方法.
    /*
    DataSourceTransactionManager ptm = new DataSourceTransactionManager();
    ptm.setDataSource(dataSource);
    */
    
    //事务定义
    TransactionDefinition td = new DefaultTransactionDefinition();
    //2、事务的状态
    TransactionStatus ts = ptm.getTransaction(td); //保持状态一致
    accountDao.inMoney(outName,money);
    int sum = 1/0;
    accountDao.outMoney(inName,money);
    //提交事务
    ptm.commit(ts);  //参数为事务状态
}

如果使用多个事务,将会产生多重复的代码,这时候就应该想到AOP,将业务层的事务处理功能抽出来制作成AOP通知,利用环绕通知运行期动态织入。

使用AOP控制事务

1、在pom.xml导入依赖

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.8.RC1</version>
    <scope>runtime</scope>
</dependency>

2、通知类 com.lvmanba.aop.TxAdvice.java

//AOP around 控制事务
public class TxAdvice {
    //注入dataSource
    private DataSource dataSource;
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    public Object transactionManager(ProceedingJoinPoint jpj) throws Throwable {
        //创建事务管理器
        PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource);
        //定义事务对象
        TransactionDefinition td = new DefaultTransactionDefinition();
        //获取事务状态
        TransactionStatus ts = ptm.getTransaction(td); //需要事务定义对象
        Object proceed = jpj.proceed(jpj.getArgs());  //原始方法是有参数的,这里写传递参数,如果不写,也会自动传参
         //提交事务
        ptm.commit(ts);  //需要事务状态参数
        return proceed;
    }
}

3、配置AOP

//加入声明
<beans
...
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="...
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

!--    设置AOP-->
    <bean id="txAdvice" class="com.lvmanba.aop.TxAdvice">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <aop:config>
        <!-- 切入点-->
        <aop:pointcut id="pt" expression="execution(* *..transfer(..))"/>
        <aop:aspect ref="txAdvice">
            <aop:around method="transactionManager" pointcut-ref="pt" />
        </aop:aspect>
    </aop:config>
</beans>    
3、声明式事务(XML)

目的是将通知类放入XML文件中。

1、导入tx命名空间

<beans xmlns="...
       xmlns:tx="http://www.springframework.org/schema/tx"
       ...
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd">

2、使用tx配置事务

<!--为tx提供事务管理器,并携带数据源-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!-- 定义事务管理的通知: tx 是事务专用通知标签-->
    <tx:advice id="txAdvice" transaction-manager="txManager">   <!--相当于<bean id="txAdvice">,需要一个事务管理器...-->
    <tx:attributes>
    <!--这里必须重写进行限制,否则事务不起作用-->
        <tx:method name="*" read-only="false"/>
        <tx:method name="get*" read-only="true" />
        <tx:method name="find*" read-only="true"/>
        <tx:method name="transfer" read-only="false" />
    </tx:attributes>
    </tx:advice>
    <aop:config>
    <!--在实际开发中一定要注意,不能所有的方法都加事务,这样会影响效率,使用execution(* com.lvmanba.service.*transfer.*(..))-->
        <aop:pointcut id="pt" expression="execution(* com.lvmanba.service.*Service.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt" />
    </aop:config>

3、可以删除com.lvmanba.aop.TxAdvice.java

tx标签说明

<tx:advice id="txAdvice" transaction-manager="txManager">   <!--相当于<bean id="txAdvice">,需要一个事务管理器...-->
<tx:attributes>
    <tx:method 
    	name=""
    	read-only="true"
    	timeout="-1"     //超时
    	isolation="DEFAULT"  //
    	no-rollback-for=""  //排除掉不回滚的异常
    	rollback-for=""    //排除掉回滚的异常
    	propagation=""
    />
</tx:attributes>
</tx:advice>

在这里插入图片描述

4、声明式事务(注解)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

目标是把下列代码替换掉

<!--为tx提供事务管理器,并携带数据源-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    
    
    <!-- 定义事务管理的通知: tx 是事务专用通知标签-->
    <tx:advice id="txAdvice" transaction-manager="txManager">   <!--相当于<bean id="txAdvice">,需要一个事务管理器...-->
    <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>
    <!--在实际开发中一定要注意,不能所有的方法都加事务,这样会影响效率,使用execution(* com.lvmanba.service.*transfer.*(..))-->
        <aop:pointcut id="pt" expression="execution(* com.lvmanba.service.*Service.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt" />
    </aop:config>

1、在需要的事务的方法上面使用@Transactional

@Transactional 替换下面内容

 <aop:config>
    <!--在实际开发中一定要注意,不能所有的方法都加事务,这样会影响效率,使用execution(* com.lvmanba.service.*transfer.*(..))-->
        <aop:pointcut id="pt" expression="execution(* com.lvmanba.service.*Service.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt" />
    </aop:config>

2、开启注解驱动

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

替换下列内容

<tx:advice id="txAdvice" transaction-manager="txManager">   <!--相当于<bean id="txAdvice">,需要一个事务管理器...-->
    <tx:attributes>
    <!--这里必须重写进行限制,否则事务不起作用-->
        <tx:method
                name="transfer"
                read-only="false"
                timeout="-1"
                isolation="DEFAULT"
                no-rollback-for=""
                rollback-for=""
                propagation="REQUIRED"
        />
    </tx:attributes>
    </tx:advice>

替换后的要使用事务的方法代码如下:

@Transactional(
        readOnly = true,
        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) {

    accountDao.inMoney(outName,money);
    //int a = 1/0;
    accountDao.outMoney(inName,money);
}

现在是在业务层方法上,应该配置在接口上,这样实现的时候直接使用

public interface AccountService {

    /*
    @param outName 出账用户名
    @param inName 入账用户名
    @param money  转出金额
    * */
    @Transactional(
            readOnly = true,
            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);
}

我们放在接口里面的方法,也可以直接放在接口上面,这样下面的方法都具有了事务。

纯注解开发事务
在这里插入图片描述

1、替换Mybatis映射配置文件

原始:dao.AccountDao.java

public interface AccountDao {
    void inMoney(@Param("name") String name, @Param("money") Double money);
    void outMoney(@Param("name") String name,@Param("money")Double money);
}

对应的Mybatis 映射配置文件

<?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="com.lvmanba.dao.AccountDao">
    <!--    收入-->
    <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>

去掉映射配置文件,将sql加入到:dao.AccountDao.java,后为:

public interface AccountDao {
    @Update("update account set money=money + #{ money } where name = #{ name }")
    void inMoney(@Param("name") String name, @Param("money") Double money);
    @Update("update account set money=money - #{ money } where name = #{ name }")
    void outMoney(@Param("name") String name,@Param("money")Double money);
}
5、事务传播行为

就是tx中的propagation属性。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、模板对象

Spring集成了很多模板,大大提高了书写效率,常见的模板对象有以下几种。
在这里插入图片描述

JdbcTemplate为例:
注册JdbcTemplate,需要提供数据源
public class JDBCConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean("dataSource")
    public DataSource getDataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }
    //注册JdbcTempate模板对象bean
    @Bean("jdbcTemplate")
    public JdbcTemplate getJdbcTemplate(@Autowired DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }
}
数据层调用JdbcTemplaite
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
    //注入模板对象
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Override
    public void save(Account account) {
        String sql="insert into account(name,money)values(?,?)";
        jdbcTemplate.update(sql,account.getName(),account.getMoney());
    }

    @Override
    public void delete(Integer id) {
        String sql="delete from account where id=?";
        jdbcTemplate.update(sql,id);
    }

    @Override
    public void update(Account account) {
        String sql="update account set name=?,money=? where id=?";
        jdbcTemplate.update(sql,account.getName(),account.getMoney(),account.getId());
    }

    @Override
    public String findNameById(Integer id) {
        String sql="select name from account where id=?";
        //单个字段查询可以使用专用的查询方法,必须制定查询出数据的类型,例如name为String类型
        return jdbcTemplate.queryForObject(sql,String.class,id);
    }

    @Override
    public Account findById(Integer id) {
        String sql="select * from account where id=?";
        //支持自定义行映射解析器
        RowMapper<Account> rm = (rs, rowNum) -> {
            Account account = new Account();
            account.setId(rs.getInt("id"));
            account.setName(rs.getString("name"));
            account.setMoney(rs.getDouble("money"));
            return account;
        };
        return jdbcTemplate.queryForObject(sql,rm,id);
    }

    @Override
    public List<Account> findAll() {
        String sql="select *  from account";
        //使用spring自带的行映射解析器,要求必须是标准封装
        return jdbcTemplate.query(sql,new BeanPropertyRowMapper<Account>(Account.class));
    }

    @Override
    public List<Account> findAll(int pageNum, int preNum) {
        String sql="select *  from account limit ?,?";
        //分页数据通过查询参数赋值
        return jdbcTemplate.query(sql,new BeanPropertyRowMapper<Account>(Account.class),(pageNum-1)*preNum,preNum);
    }

    @Override
    public Long getCount() {
        String sql="select count(id) from account";
        //单个字段查询可以使用专用的查询方法,必须制定查询出的数据类型,例如数据总量为Long类型。
        return jdbcTemplate.queryForObject(sql,Long.class);
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值