目录
- public class MyBatisUtil {
- //工具类构造方法私有化
- private void MyBatisUtil() {
- }
- //方法一
- public static SqlSession getSqlSession(){
- try {
- SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
- SqlSessionFactory build = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("UserMapper.xml"));
- SqlSession sqlSession = build.openSession();
- return sqlSession;
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
弊端:每次调用都会创建sqlSessionFactoryBuilder、SqlSessionFactory等大量对象,创建的对象太多,造成资源浪费
- //方法二
- private static SqlSession sqlSession = null;
- static {
- try {
- sqlSession = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("UserMapper.xml")).openSession();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- // 弊端:调用一次mybatisutil就创建好了sqlsession,此后的每次调用会公用一个sqlsession对象;
弊端:由于sqlsession对象的创建是在static静态代码块中的,所以第一次调用mybatisutil就创建好了sqlsession,此后的每次调用会公用一个sqlsession对象;这样也是不行的事物这一块会有问题,然后你每次创建会话的目的就是区分和记录不同的会话,他要一样岂不是不符合初期的设计了;
- //方法三
- private static SqlSessionFactory sqlSessionFactory = null;
- static {
- try {
- sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("UserMapper.xml"));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- public SqlSession getSqlSession() {
- return sqlSessionFactory.openSession();
- //每调用一次方法,创建一个sqlsession对象,不共用
- }
之前的这些工具类,刚刚开始学习mybatis时足以使用,但是随着学习的增进,代码量的增大,业务的变多,使用以上工具类就会出现很多的事务问题,例如下面这串代码:
- public class AccountServiceImpl implements AccountService {
- private AccountDao accountDao = new AccountDaoImpl();
- @Override
- public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, AppException {
- // 查询转出账户的余额
- Account fromAct = accountDao.selectByActno(fromActno);
- if (fromAct.getBalance() < money) {
- throw new MoneyNotEnoughException("对不起,您的余额不⾜。");
- }
- try {
- // 程序如果执⾏到这⾥说明余额充⾜
- // 修改账户余额
- Account toAct = accountDao.selectByActno(toActno);
- fromAct.setBalance(fromAct.getBalance() - money);
- toAct.setBalance(toAct.getBalance() + money);
- // 更新数据库(添加事务)
- SqlSession sqlSession = SqlSessionUtil.openSession();
- accountDao.update(fromAct);
- // 模拟异常
- String s = null;
- s.toString();
- accountDao.update(toAct);
- sqlSession.commit();
- sqlSession.close();
- } catch (Exception e) {
- throw new AppException("转账失败,未知原因!");
- }
- }
- }
很明显,这串代码会报错。虽然我们在代码中开启了事务管理,但是代码中使用了大量的sqlsession对象,所以导致sqlsession的重复出现导致事务管理出错;错误原因在于空指针异常,第一个update事务提交了,然而第二个update的事务没有提交,最开始开启的sqlsession事务对象并不能控制整个转账过程,原因就是每个update都有属于自己的sqlsession,也就是说他们各自使用的sqlsession都不一样,所以我们如何解决?
那就是让它们使用同一个sqlsession,如何做到呢?使用threadlocal;
修改工具类;
- public class SqlSessionUtil {
- private static SqlSessionFactory sqlSessionFactory;
- /**
- * 类加载时初始化sqlSessionFactory对象
- */
- static {
- try {
- SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
- sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- private static ThreadLocal
local = new ThreadLocal<>(); - /**
- * 每调⽤⼀次openSession()可获取⼀个新的会话,该会话⽀持⾃动提交。
- *
- * @return 新的会话对象
- */
- public static SqlSession openSession() {
- SqlSession sqlSession = local.get();
- if (sqlSession == null) {
- sqlSession = sqlSessionFactory.openSession();
- local.set(sqlSession);
- }
- return sqlSession;
- }
- /**
- * 关闭SqlSession对象
- * @param sqlSession
- */
- public static void close(SqlSession sqlSession){
- if (sqlSession != null) {
- sqlSession.close();
- }
- local.remove();
- }
- }