美文网首页
Spring的事务控制

Spring的事务控制

作者: 咸鱼有梦想呀 | 来源:发表于2018-12-18 20:08 被阅读0次

一、Spring的事务控制

  • 分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案。
  • Spring提供了一组事务控制的接口。这组接口是在spring-tx-4.2.4.RELEASE.jar中。
  • Spring的事务都是基于AOP的,既可以使用配置的方法实现,也可以使用注解的方法实现。

二、Spring事务控制的API介绍

  • PlatformTransactionManager
    这个接口是spring的事务管理器,它里面提供了常用的操作事务的方法。

    • 获取事务状态信息
      TransactionStatus getTransaction(TransactionDefinition definition)
    • 提交事务
      void commit(TransactionStatus status)
    • 回滚事务
      void rollback(TransactionStatus status)
    • 一般都会用它的实现类
      使用SpringJDBC或iBatis进行持久化数据时使用
      DataSourceTransactionManage
  • TransactionDefinition
    事务的定义信息对象,方法如下:

    • 获取事务对象名称
      Spring getName()
    • 获取事务隔离级
      int getIsolationLevel()
    • 获取事务传播行为
      int getPropagationBehavior()
    • 获取事务超时时间
      int getTimeout()
    • 获取事务是否只读
      boolean isReadOnly()
    • 事务的传播行为
      REQUIRED
      如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
      SUPPORTS
      支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
      MANDATORY
      使用当前的事务,如果当前没有事务,就抛出异常
      REQUERS_NEW
      新建事务,如果当前在事务中,把当前事务挂起。
      NOT_SUPPORTED
      以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
      NEVER
      以非事务方式运行,如果当前存在事务,抛出异常
      NESTED
      如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED
      类似的操作。
    • 超时时间
      默认值是-1,没有超时限制。如果有,以秒为单位进行设置
    • 是否是只读事务
      建议查询时设置为只读
  • TransactionStatus
    这个接口提供的是事务具体的运行状态

  • 刷新事务
    void flush()

  • 获取是否存在存储点
    boolean hasSavepoint()

  • 获取事务是否为新的事务
    boolean isNewTransaction()

  • 获取事务是否回滚
    boolean isRollbackOnly()

  • 设置事务回滚
    void setRollbackOnly()

三、XML的声明式事务控制配置方式

1.所需jar包

jar包

2.数据库

account数据库

并创建对应的实体类

3.编写业务层接口和实现类
IStuService.java

package com.edu.service;

/**
 * 业务层接口
 */
public interface IStuService {

    /**
     * 转账
     * @param sourceName
     * @param targetName
     * @param money
     */
    void transfer(String sourceName,String targetName,Float money);
}

StuServiceImpl.java

package com.edu.service.impl;

import com.edu.dao.IStuDao;
import com.edu.domain.Account;
import com.edu.service.IStuService;
/**
 * 业务层实现类
 */
public class StuServiceImpl implements IStuService {

    private IStuDao stuDao;

    public void setStuDao(IStuDao stuDao) {
        this.stuDao = stuDao;
    }

    @Override
    public void transfer(String sourceName, String targetName, Float money) {
        //1.根据名称查询账户信息
        Account account = stuDao.findAccountByName(sourceName);
        Account target = stuDao.findAccountByName(targetName);
        //2.转出账户减钱,转入账户加钱
        account.setMoney(account.getMoney()-money);
        target.setMoney(target.getMoney()+money);
        //3.更新账户信息
        stuDao.updateAccount(account);
        int i=1/0;//异常,检验事务的作用
        stuDao.updateAccount(target);
    }
}

4.编写dao接口和实现类

package com.edu.dao;

import com.edu.domain.Account;
/**
  * 持久层接口
  */
public interface IStuDao {
    /**
     * 根据名称查询账户信息
     * @param targetName
     * @return
     */
    Account findAccountByName(String targetName);

    /**
     * 更新账户信息
     * @param account
     */
    void updateAccount(Account account);
}
package com.edu.dao.Impl;

import com.edu.dao.IStuDao;
import com.edu.domain.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import java.util.List;
/**
  * 持久层实现类
  */
public class StuImpl extends JdbcDaoSupport implements IStuDao {
    

    @Override
    public Account findAccountByName(String targetName) {
        List<Account> accountList = getJdbcTemplate().query("select * from account where name = ?", new BeanPropertyRowMapper<Account>(Account.class), targetName);
        if (accountList.isEmpty()) {
            return null; //没有名称的账户
        }
        if (accountList.size() > 1) {
            //结果集不唯一,不要
            throw new RuntimeException("结果集不唯一,不要");
        }
        return accountList.get(0);
    }

    @Override
    public void updateAccount(Account account) {
        getJdbcTemplate().update("update account set name = ?,money = ? where cid = ?",account.getName(),account.getMoney(),account.getCid());
    }
}

5.配置配置文件
bean.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: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.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 配置service -->
<bean id="stuService" class="com.edu.service.impl.StuServiceImpl">
    <property name="stuDao" ref="stuDao"></property>
</bean>

<!-- 配置dao -->
<bean id="stuDao" class="com.edu.dao.Impl.StuImpl">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 配置Spring内置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/goods"></property>
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
</bean>

<!-- Spring基于XML的声明式事务控制 -->
<!-- 1.配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据源 -->
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 2.配置事务的通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED" read-only="false"/>
        <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
    </tx:attributes>
</tx:advice>

<!-- 3.配置AOP
        配置切入点表达式
        和
        通知和切入点表达式的关联
-->
<aop:config>
    <!-- 配置切入点表达式 -->
    <aop:pointcut id="pt1" expression="execution(* com.edu.service.impl.*.*(..))"/>
    <!-- 配置事务通知和切入点表达式的关联 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
</aop:config>

</beans>

6.测试类

package com.edu.springTemplate;

import com.edu.service.IStuService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        IStuService stuService = (IStuService) ac.getBean("stuService");
        /*Stu stu = stuService.findStuById(1);
        System.out.println(stu);*/
        stuService.transfer("aaa","bbb",100f);
    }
}

如果没有事务,若异常,账户信息无法回滚,也就是会造成一个账户的钱转了出去,但另一个账户却没有受到。但是有了事务,若有异常就会回滚,账户操作就会回到之前。

四、基于XML和注解组合使用的声明式事务配置

在上述案例基础上只需要修改

1.业务层实现类

package com.edu.service.impl;

import com.edu.dao.IStuDao;
import com.edu.domain.Account;
import com.edu.service.IStuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service("stuService")
@Transactional
public class StuServiceImpl implements IStuService {

    @Autowired
    private IStuDao stuDao;

    @Override
    public void transfer(String sourceName, String targetName, Float money) {
        //1.根据名称查询账户信息
        Account account = stuDao.findAccountByName(sourceName);
        Account target = stuDao.findAccountByName(targetName);
        //2.转出账户减钱,转入账户加钱
        account.setMoney(account.getMoney()-money);
        target.setMoney(target.getMoney()+money);
        //3.更新账户信息
        stuDao.updateAccount(account);
        int i=1/0;
        stuDao.updateAccount(target);
    }
}

2.Dao层实现类

package com.edu.dao.Impl;

import com.edu.dao.IStuDao;
import com.edu.domain.Account;
import com.edu.domain.Stu;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository("stuDao")
public class StuImpl implements IStuDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public Stu findStuById(Integer sid) {
        List<Stu> list = jdbcTemplate.query("select * from stu where sid = ?", new BeanPropertyRowMapper<Stu>(Stu.class), 1);
        return list.isEmpty() ? null : list.get(0);
    }

    @Override
    public Account findAccountByName(String targetName) {
        List<Account> accountList = jdbcTemplate.query("select * from account where name = ?", new BeanPropertyRowMapper<Account>(Account.class), targetName);
        if (accountList.isEmpty()) {
            return null; //没有名称的账户
        }
        if (accountList.size() > 1) {
            //结果集不唯一,不要
            throw new RuntimeException("结果集不唯一,不要");
        }
        return accountList.get(0);
    }

    @Override
    public void updateAccount(Account account) {
        jdbcTemplate.update("update account set name = ?,money = ? where cid = ?",account.getName(),account.getMoney(),account.getCid());
    }
}

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

    <!-- 配置Spring创建容器时要扫描的包 -->
    <context:component-scan base-package="com.edu"></context:component-scan>
    <!-- 配置JdbcTemplate -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 配置Spring内置数据源 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/goods"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <!-- Spring基于XML和注解组合的声明式事务控制 -->
    <!-- 1.配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 2.配置spring开启注解事务的支持 -->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

    <!-- 3.在需要使用事务的地方使用@Transactional
        这个注解可以写在接口,类上和方法上
        写在接口上,表明接口的所有实现类都有事务
        写在类上,表示该类的所有方法都有事务
        写在方法上,表示该方法有事务
        优先级:就近原则
    -->
</beans>

测试类如上
效果也如上

五、基于纯注解的声明式事务配置

在基于XML和注解组合使用的声明式事务配置的基础上将xml文件种的配置转为注解

1.使用注解配置数据源

jdbcConfig.java

package com.edu.config;

import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.sql.DataSource;

/**
 * 使用注解
 *      配置JdbcTemplate
 *      配置数据源
 */
public class JdbcConfig {

    //配置JdbcTemplate
    @Bean(name = "jdbcTemplate")
    public JdbcTemplate createJdbcTemplate(DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

    //配置数据源
    @Bean(name = "dataSource")
    public DataSource createDataSource(){
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/goods");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}

2.配置事务管理器
TransactionManager.java

package com.edu.config;

import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

/**
 * 事务控制配置类
 */
public class TransactionManager {

    @Bean(name = "transactionManager")
    public PlatformTransactionManager createTransactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
}

3.创建一个类用于加载spring的配置并指定要扫描的包
SpringConfig.java

package com.edu.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * Spring的配置类,作用就是当bean.xml用
 */
@Configuration
@ComponentScan("com.edu")
@Import({JdbcConfig.class,TransactionManager.class})
@EnableTransactionManagement
public class SpringConfig {

}

4.测试类

package com.edu.springTemplate;

import com.edu.config.SpringConfig;
import com.edu.service.IStuService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;


public class Test {
    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
        IStuService stuService = (IStuService) ac.getBean("stuService");
        stuService.transfer("aaa","bbb",100f);
    }
}

相关文章

网友评论

      本文标题:Spring的事务控制

      本文链接:https://www.haomeiwen.com/subject/fmchkqtx.html