JavaWeb中对数据库事务的操作

 对于用JDBC操作数据库,使用Connection类的setAutoCommit(false)方法可以开启事务,用commit()方法可以提交事务。

  由于三层架构的设计模型,事务的逻辑必须在service层,而dao层只是提供简单的CRUD操作,所以必须由service获取Connection并开启事务,将这个Connection传给dao层进行操作后,在service层将事务提交。

  将Connection传给dao层可以在调用dao层方法(或构造函数)时以参数形式传入,但是这样不但会污染dao层的方法签名,并且对于多层的参数传递会使方法调用看起来又长又臭,而下面用ThreadLocal容器进行参数传递的方法就显得优雅得多了:

  ①对于Tomcat服务器,每个http请求分配到一个线程去处理,处理的线程可以是新建线程,也可以是从线程池中获取。而单个的请求是在同一个线程里完成的。

  ②ThreadLocal可以以当前线程为key存储一个对象,并以当前线程为key取出对应的对象。

  在同一个线程中我们在service层将一个Connection开启事务并存到ThreadLocal中,代码运行到dao层的update方法时将ThreadLocal中的Connection获取到(其引用),用这个Connection操作数据库,方法返回到service层后我们从ThreadLocal中获取到Connection,提交事务,并清除ThreadLocal容器

  代码如下:

  1.dao层代码:

package com.hao.utils;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

public class DaoUtils {
    private static DataSource ds;
    private static ThreadLocal connectionThreadLocal = new ThreadLocal<>();

    static {
        try {
            Context initCtx = new InitialContext();
            Context envCtx = (Context) initCtx.lookup("java:comp/env");
            ds = (DataSource) envCtx.lookup("jdbc/EmployeeDB");
        } catch (NamingException e) {
            throw new ExceptionInInitializerError(e);
        }
    }
//开启事务,将一个开启事务的链接绑定到线程上,这是给service层调用的
    public static void startTransaction() {
        try {
            Connection conn = ds.getConnection();
            conn.setAutoCommit(false);
            connectionThreadLocal.set(conn);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    //获取事务链接,从线程上取出链接,这是给dao层用的
    public static Connection getTransactionConnection() {
        return connectionThreadLocal.get();
    }

    //提交事务,将Connection与线程解除绑定,这是给service层用的
    public static void commitTransaction() {
        try {
            Connection conn = connectionThreadLocal.get();
            conn.commit();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            //由于connectionThreadLocal是静态的,在服务器内加载后会一直存在,其中装载的东西一定要自己清空
            connectionThreadLocal.remove();
        }
    }

}
package com.hao.dao;

import com.hao.domain.Account;
import com.hao.utils.DaoUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import java.sql.SQLException;

public class AccountDao {
    private QueryRunner qr = new QueryRunner();

    public void add(Account account) {
        String sql = "insert into account(id,name,money) values(?,?,?)";
        try {
            qr.update(sql, account.getId(), account.getName(), account.getMoney());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void delete(String id) {
        String sql = "delete from account where id = ?";
        try {
            qr.update(sql, id);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void update(Account account) {
        String sql = "update account set name=?,money=? where id=?";
        try {
            //更新时使用开启事务的链接
            qr.update(DaoUtils.getTransactionConnection(), sql, account.getName(), account.getMoney(), account.getId());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public Account find(String id) {
        String sql = "select name,money from account where id=?";
        try {
            Account account = qr.query(sql, new BeanHandler(Account.class), id);
            account.setId(id);
            return account;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

 2.service层代码:

package com.hao.service;

import com.hao.dao.AccountDao;
import com.hao.domain.Account;
import com.hao.utils.DaoUtils;

public class BusinessService {
    public void transfer(String sourceId, String targetId, double money) {
        //开启事务
        DaoUtils.startTransaction();
        //开始转账
        AccountDao dao = new AccountDao();
        Account sourceAccount = dao.find(sourceId);
        sourceAccount.setMoney(sourceAccount.getMoney() - money);
        dao.update(sourceAccount);
        Account targetAccount = dao.find(targetId);
        targetAccount.setMoney(targetAccount.getMoney() + money);
        dao.update(targetAccount);
        //提交事务
        DaoUtils.commitTransaction();
        //注意以上操作是在同一个线程中的
    }
}


你可能感兴趣的:(JavaWeb中对数据库事务的操作)