Spring学习笔记【part05】事务管理基础

导读:本篇文章讲解 Spring学习笔记【part05】事务管理基础,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

Spring 学习笔记 Part05

1. 前置知识 —— 线程与连接绑定来方便实现事务

​ 默认情况下,每一个数据访问语句都会建立一个连接,各个连接间相互独立,其中某个连接出错就会导致错误的数据修改结果。

在这里插入图片描述

解决思路:

​ 一个事务都使用同一个 Connection 的话,要么一起成功,要么一起失败,可以实现事务。在 Spring 中,使用 TheadLocal 对象把 Connection 和当前线程绑定,从而使一个线程中只能有一个能控制事务的对象。

1.1 工具类的编写(连接管理、事务管理)

//连接工具类

@Component("connectionUtil")
public class ConnectionUtil {

    private ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<Connection>();
    @Autowired
    private DataSource dataSource;

    public Connection getConnection(){
        Connection conn = connectionThreadLocal.get();
        try {
            if (conn == null){
                conn = dataSource.getConnection();
                connectionThreadLocal.set(conn);
            }
            return conn;
        } catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public void removeConnection(){
        connectionThreadLocal.remove();  //与当前线程解绑
    }
}

//事务管理类

@Component("transactionManager")
public class TransactionManager {

    private ConnectionUtils connectionUtils;

    public void begin(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    public void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    public void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    public void release(){
        try {
            connectionUtils.getThreadConnection().close();
            connectionUtils.removeConnection();  //与当前线程解绑
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

​ 需要注意的是关闭连接时需要和线程进行解绑操作。连接 close( ) 关闭时会还回连接池中,线程关闭时也会还回线程池中,但它们间的绑定关系还是存在的。当我们第二次使用这个线程时,get( ) 方法会拿出来之前绑定的那个连接,但是这个连接之前被关闭了(加回到池里去了),使用不了。所以我们需要线程在使用好连接后调用一个 remove( ) 方法来解绑连接,以便下次获取的连接是新的、能用的。

1.2 业务层的编写

​ Service层需要加入事务管理,分为 6 个步骤:


@Service("service")
public class AccountServiceImpl implements AccountService {

    @Override
    public boolean transfer(String sourceName, String targetName, double money) {
        try {
            transactionManager.begin(); //第1步:开启事务
            Account source = dao.findByName(sourceName); //第2步:执行操作
            Account target = dao.findByName(targetName);
            source.setMoney(source.getMoney() - money);
            target.setMoney(target.getMoney() + money);
            dao.update(source);
            //double a = 4/0;  有了事务管理,出错了将会回滚,不会造成数据错误了~!
            dao.update(target);
            transactionManager.commit(); //第3步:提交事务
            return true; //第4步:返回结果
        } catch (Exception e) {
            transactionManager.rollback(); //第5步:回滚操作
            throw new RuntimeException(e);
        } finally {
            transactionManager.release(); //第6步:释放连接
        }
    }
}

1.3 持久层的编写

​ Dao层需要注入ConnectionUtils对象,用ConnectionUtils获得当前线程对应的连接,并作为QueryRunner的query( )方法中的第一个参数传入,为本次数据操作指定一个特定的连接。


@Repository("dao")
public class AccountDaoImpl implements AccountDao {
    
	@Autowired
    private QueryRunner runner;

    @Autowired
    private ConnectionUtils connectionUtils;

    @Override
    public Account findByName(String username) {
        List<Account> accounts = null;
        try {
            accounts = runner.query(connectionUtils.getThreadConnection(),"select * from account where name = ?", 
                                    new BeanListHandler<Account>(Account.class), username);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if(accounts==null||accounts.size()==0){
            return null;
        }
        if(accounts.size()>1){
            throw new RuntimeException("结果集不唯一,数据有问题");
        }
        return accounts.get(0);
    }

    @Override
    public void update(Account account) {
        try {
            runner.update(connectionUtils.getThreadConnection(),
                          "update account set money = ? where id = ?",account.getMoney(),account.getId());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/84500.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!