Spring-TX声明式事务控制

不管哪种框架,都是实现Spring中提供的PlatformTransactionManager接口。让你的数据库操作交给Spring事务管理

Spring中事务控制的API介绍
PlatformTransactionManager接口
  • 获取事务状态信息 TransactionStatus getTransaction(TransactionDifinition difinition)
  • 提交事务 void commit(TransactionStatus status)
  • 回滚事务 void rollback(TransactionDifinition difinition)
    TransactionDifinition

事务定义的信息对象

  • 获取事务对象的名称 String getName()
  • 获取事务的隔离级别 int getIsolationLevel()
  • 获取事务的传播行为 int getPropagationBehavior();
  • 获取事务的超时时间 int getTimeout()
  • 获取事务是否只读 boolean idReadOnly()
    TransactionStatus
    事务的传播行为:

主要解决的问题是在业务方法进行调试(嵌套)的时候,事务应该如何进行处理

  • 保证在同一事务中
    - PROPAGATION_REQUIRED 支持当前事务, 如果不存在 就新建一个(默认)事务
    - PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务。
    - PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常
  • 保证不在同一事务中
    - PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务
    - PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
    - PROPAGATION_NEVER 以非事务方式运 行,如果有事务存在,抛出异常
    - PROPAGATION_NESTED 如果当前事务存 在,则嵌套事务执行
    超时时间
    如果没有设定超时时间,默认是没有超时时间限制。默认值为-1。 如果设置了,以秒为单位的。
    是否是只读事务
    对于增删改设定为false,对于查询业务设定为true。默认值为false。
基于JDBC原生的事务控制演示

代码如下:

/*dao层接口代码如下:*/
//dao接口
public interface UserDao {
    //CRUD
    //添加用户
    void addUser(User user);
}
/*dao层接口实现类代码如下*/

```java
/**
 * UserDao接口实现类
 */
@Repository("userDao")
public class UserDaoImpl implements UserDao {
    @Autowired//前提是Spring容器中含有该对象
    private JdbcTemplate jdbcTemplate;

    //Connection
    @Autowired/*引入数据源*/
    private DataSource dataSource;

    @Override//对于数据库来说  增删改  都是更新
    public void addUser(User user) {
        Connection connection = null;
        //开启事务
        try {
            connection = dataSource.getConnection();
            //开启事务
            connection.setAutoCommit(false);
            PreparedStatement ps = connection.prepareStatement("insert into user values(null,?,?,?)");
            ps.setString(1, user.getU_name());
            ps.setInt(2, user.getU_age());
            ps.setDouble(3, user.getU_salary());
            int num = ps.executeUpdate();

            //添加用户
            //int num = jdbcTemplate.update("insert into user values(null,?,?,?)", user.getU_name(), user.getU_age(), user.getU_salary());
            //进行事务的提交
            //int  i = 1/0;
            connection.commit();
            if (num > 0) {
                System.out.println("数据添加成功!");
            } else {
                System.out.println("数据添加失败!");
            }
        } catch (Exception throwables) {
            //有异常信息发生
            System.out.println("此时添加过程中有异常信息。。。数据回滚。。。");
            try {
                //进行事务的回滚
                connection.rollback();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

Spring核心主配置文件代码如下:

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--组件扫描-->
    <context:component-scan base-package="com.zhiyou100"/>
    <!--context:property-->
    <!--引入db.properties文件到spring容器中-->
    <context:property-placeholder location="db.properties"/>
    <!--配置数据源-->
    <!--采用数据库连接池  依赖于数据源DataSource    ComboPooledDataSource-->
    <!--把ComboPooledDataSource该对象注入到Spring到容器中-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"/>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!--配置JdbcTemplate对象-->
    <!--该对象也依赖于数据源-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--属性注入   采用set方式-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

</beans>

db.properties数据库连接配置信息代码如下:

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/db_spring?CharacterEncoding=UTF-8
jdbc.user=root
jdbc.password=admin

log4j配置文件如下:

# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
基于XML配置的声明式事务控制[xml配置]
开发步骤:
  • 添加Spring事务需要的jar包依赖
  • 拷贝SpringTx_jdbc工程代码
  • 书写service层的业务接口和实现类
  • 配置事务管理器
  • 配置事务通知 关联事务管理器
  • 配置事务的通知属性
  • 配置AOP切入点和事务通知,进行绑定

备注:把可能出现异常的代码放进try catch语句块中。当事务中出现异常,使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(),进行手动回滚
当用try-catch包括dao层接口,如果有异常数据无法完成回滚因为我们把异常捕获了,所以spring事务就不回滚

代码示例如下:
实体类的代码如下:

@Component("user")
public class User implements Serializable {

    @Value("1")
    private Integer u_id;
    @Value("小孙")
    private String u_name;
    @Value("20")
    private Integer u_age;
    @Value("10000")
    private Double u_salary;

    public Integer getU_id() {
        return u_id;
    }

    public void setU_id(Integer u_id) {
        this.u_id = u_id;
    }

    public String getU_name() {
        return u_name;
    }

    public void setU_name(String u_name) {
        this.u_name = u_name;
    }

    public Integer getU_age() {
        return u_age;
    }

    public void setU_age(Integer u_age) {
        this.u_age = u_age;
    }

    public Double getU_salary() {
        return u_salary;
    }

    public void setU_salary(Double u_salary) {
        this.u_salary = u_salary;
    }

    public User(Integer u_id, String u_name, Integer u_age, Double u_salary) {
        this.u_id = u_id;
        this.u_name = u_name;
        this.u_age = u_age;
        this.u_salary = u_salary;
    }

    public User() {
    }

    @Override
    public String toString() {
        return "User{" +
                "u_id=" + u_id +
                ", u_name='" + u_name + '\'' +
                ", u_age=" + u_age +
                ", u_salary=" + u_salary +
                '}';
    }
}

/*dao层*/
public interface UserDao {
 // 根据用户名称查询用户信息
    User findUserByUsername(String username);
}
/*dao 层接口实现类代码如下:*/
@Repository("userDao")
public class UserDaoImpl implements UserDao {

    @Autowired //前提是Spring容器含有该对象
    private JdbcTemplate jdbcTemplate;

    // Connection 引入数据源
    @Autowired
    private DataSource dataSource;

  @Override
    public User findUserByUsername(String username) {
        // 根据用户名值查询一条记录  queryForObject方法
        User user = null;
        try {
            user = jdbcTemplate.queryForObject("select * from user where u_name = ?", new BeanPropertyRowMapper<>(User.class), username);
        }catch (Exception e) {
            e.printStackTrace();
        }
        return user;
    }
}

service层接口代码如下:

// 业务层接口
public interface UserService {
    // 工资变更  把一个人工资的降一降 把另一个工资升一升
    boolean changeSalary(String descUsername ,String ascUsername,double money);// 修改  写的动作 readOnly--->false
}
/*service层接口实现类代码如下*/
@Service("userService")
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    @Override
    public boolean setSalary(String ascSalary, String descSalary, double money) {
        User asc = userDao.queryByUserName(ascSalary);
        User desc = userDao.queryByUserName(descSalary);
        asc.setU_salary(asc.getU_salary()+money);
        desc.setU_salary(desc.getU_salary()-money);
        boolean flan = true;
        try {
            userDao.updateUser(asc);
            int i = 1 / 0;//此处为人为手动异常  为验证遇到异常是否自动回滚
            userDao.updateUser(desc);
        } catch (Exception e) {
            flan = false;
            System.out.println("出现异常。。。。事务回滚");
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return flan;
    }
}

Spring的主配置文件配置如下:

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

    <!--组件扫描-->
    <context:component-scan base-package="com.zhiyou100"/>

    <!--content:property-->
    <!--引入db.properties文件到Spring容器-->
    <context:property-placeholder location="classpath:db.properties"/>

    <!--配置数据源-->
    <!--采用数据库连接池 C3p0 依赖于数据源DataSource   ComboPooledDataSource  -->
    <!--把ComboPooledDataSource该对象注入到Spring的容器中-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"/>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
        <property name="user"  value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--配置JdbcTemplate对象-->
    <!--该对象也依赖于数据源DataSource-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--属性注入 采用set方式-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--依赖于数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--配置事务通知 关联事务管理器-->
    <ts:advice  id="txAdvice" transaction-manager="transactionManager">
        <ts:attributes>
            <!--* 此时代表所有的方法-->
            <ts:method name="*" propagation="REQUIRED" isolation="DEFAULT"/>
            <ts:method name="findUserById" propagation="REQUIRED" isolation="DEFAULT"/>
            <ts:method name="changeSalary" propagation="REQUIRED" isolation="DEFAULT"/>
        </ts:attributes>
    </ts:advice>

    <!--配置AOP切入点-->
    <aop:config >
        <aop:pointcut id="pct" expression="execution(* com.zhiyou100.service..*.*(..))"/>
        <!--配置aop通知 可以关联到事务通知和切入点-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pct"/>
    </aop:config>
</beans>

db.properties的配置信息如下

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/db_spring?CharacterEncoding=UTF-8
jdbc.user=root
jdbc.password=admin

测试类代码如下

@RunWith(SpringJUnit4ClassRunner.class)
// @Configuration  配置Spring 一个主配置类
// @ContextConfiguration 读取applicationContext.xml
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcTemplateTest {
    @Autowired
    private UserService userService;
    /**
     *
     * 测试 工资变更的事务
     */
   @Test
   public void testChangSalary(){
       boolean flag = userService.changeSalary("李四", "张三", 1000);
       System.out.println(flag);

   }
}

验证后代码无异议,测试正常,这里就不展示测试结果了。。。。。。。

基于XML配置的声明式事务控制[全问注解配置]

此处实体类代码与dao层代码同上
service代码如下:

// 业务层接口
public interface UserService {
    // 工资变更  把一个人工资的降一降 把另一个工资升一升
    boolean changeSalary(String descUsername ,String ascUsername,double money);// 修改  写的动作 readOnly--->false
}
/*service层接口实现类代码如下:*/
@Service("userService")// 把UserServiceImpl类对象交给Spring管理
@Transactional(propagation = Propagation.REQUIRED, readOnly = true, isolation = Isolation.DEFAULT)//
public class UserServiceImpl implements UserService {

    // 属性注入
    @Autowired
    private UserDao userDao;
    
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    @Override
    public boolean changeSalary(String descUsername, String ascUsername, double money) {
        // descUsername 等待降工资的人
        // ascUsername  等待升工资的人
        // 把降工资的人信息查询出来
        User descUser = userDao.findUserByUsername(descUsername);
        // 把升工资的人信息查询出来
        User ascUser = userDao.findUserByUsername(ascUsername);
        // 在当前工资的基础上减
        descUser.setU_salary(descUser.getU_salary() - money);
        //  在当前工资的基础上加
        ascUser.setU_salary(ascUser.getU_salary() + money);

        // 定义一个开关
        boolean flag = false;
        // 更新两个人的信息
        try {
            userDao.updateUserByUsername(descUser);
            //int i = 1 / 0; // 算术异常
            userDao.updateUserByUsername(ascUser);
        } catch (Exception e) {
            flag = true;
            //当出现异常时进行手动回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return flag;
    }
}

数据库工具类配置如下:

/**
 * 配置数据源
 */
public class DBUtil {
    //引入数据源的四大组件
    @Value("${jdbc.driverClass}")
    private String driverClass;
    @Value("${jdbc.jdbcUrl}")
    private String jdbcUrl;
    @Value("${jdbc.user}")
    private String user;
    @Value("${jdbc.password}")
    private String password;

    //构建数据源对象
    @Bean(name = "dataSource")
    public DataSource getDataSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass(driverClass);
        dataSource.setJdbcUrl(jdbcUrl);
        dataSource.setUser(user);
        dataSource.setPassword(password);
        return dataSource;
    }
    //构建Jdbc模版类,JdbcTemplate对象
    @Bean(name = "jdbcTemplate")
    public JdbcTemplate createJdbcTemplate(@Autowired DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        return jdbcTemplate;
    }
}

Spring核心住配置类代码如下:

@Configuration//标记当前类为Spring的主配置类
@ComponentScan("com.zhiyou100")//把com.zhiyou100当前包的类以及子包中的类扫描到Spring容器中
@PropertySource("classpath:db.properties")//数据的四大组件
@Import({DBUtil.class,TransactionConfig.class})//把其他的配置类引入到当前主配置类中
@EnableTransactionManagement//标记当前的类具有事务管理职能
public class SpringConfiguration {
}

配置事务管理器的类的代码如下

/**
 * 配置事务管理器
 */
public class TransactionConfig {
    @Bean(name = "transactionManager")
    public PlatformTransactionManager createTransactionManager(@Autowired DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
}

数据库连接配置信息如下(db.properties):

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/db_spring?CharacterEncoding=UTF-8
jdbc.user=root
jdbc.password=admin
@RunWith(SpringJUnit4ClassRunner.class)
// @Configuration  配置Spring 一个主配置类
// @ContextConfiguration 读取applicationContext.xml
@ContextConfiguration(classes = SpringConfiguration.class)
public class JdbcTemplateTest {
    @Autowired
    private UserService userService;
    /**
     *
     * 测试 工资变更的事务
     */
   @Test
   public void testChangSalary(){
       boolean flag = userService.changeSalary("李四", "张三", 1000);
       System.out.println(flag);

   }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值