MyBatis 之 SqlSessionTemplate 源码分析

1.SqlSessionTemplate 是如何保证线程安全

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
    PersistenceExceptionTranslator exceptionTranslator) {
  notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
  notNull(executorType, "Property 'executorType' is required");

  this.sqlSessionFactory = sqlSessionFactory;
  this.executorType = executorType;
  this.exceptionTranslator = exceptionTranslator;
  this.sqlSessionProxy = (SqlSession) newProxyInstance(
      SqlSessionFactory.class.getClassLoader(),
      new Class[] { SqlSession.class },
      new SqlSessionInterceptor());
}

以上代码sqlSession通过动态代理的方式创建,当实际调用sqlSession中的接口,该调用则被导向SqlSessionInterceptor的invoke方法。

 private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  SqlSession sqlSession = getSqlSession(
      SqlSessionTemplate.this.sqlSessionFactory,
      SqlSessionTemplate.this.executorType,
      SqlSessionTemplate.this.exceptionTranslator);
  try {
    Object result = method.invoke(sqlSession, args);
    if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
      // force commit even on non-dirty sessions because some databases require
      // a commit/rollback before calling close()
      sqlSession.commit(true);
    }
    return result;
  } catch (Throwable t) {
    Throwable unwrapped = unwrapThrowable(t);
    if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
      // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
      closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
      sqlSession = null;
      Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
      if (translated != null) {
        unwrapped = translated;
      }
    }
    throw unwrapped;
  } finally {
    if (sqlSession != null) {
      closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
    }
  }
}
}

可以在invoke方法中看到sqlSession 通过getSession产生。也就是说每次调用的sqlSession中的接口,sqlSession都是当前线程独有的。如果不是很明白动态代理的话请看下面

2.动态代理demo

car

public interface Car {
    void describe();
}

BMW

public class BMW implements Car {
    public void describe() {
      System.out.println("宝马 " + UUID.randomUUID());
    }
}     

Jeep

public class Jeep implements Car {
    public void describe() {
      System.out.println("jeep " + UUID.randomUUID());
    }
}     

CarProxy

public class CarProxy implements InvocationHandler {
private Random random = new Random();

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    int randomInt = random.nextInt(10);
    Car car;
    if (randomInt < 5) {  //模拟调用失败情况
        car = new BMW();
    } else {
        car = new Jeep();
    }
    method.invoke(car, args);
    return null;
}
}   

Xlient

public class Xlient {
private Car car;

public static void main(String[] args) {
    Xlient xlient = new Xlient();
    xlient.test();
}

private void test() {
    Car car = (Car) Proxy.newProxyInstance(Car.class.getClassLoader(), new Class[]{Car.class}, new CarProxy());
    for (int i = 0; i < 100; i++) {
        car.describe();
    }

}
}  

运行以上代码结果

jeep 8870e1f0-e5fa-4a42-a1dc-4b8bfa07789c
jeep 01977d67-1faa-49ed-aec1-c9c41b767fed
jeep 15aa16d5-8c5f-478b-aaac-804627f77057
宝马 ee38f7e6-b2df-4385-b57f-9ded3384642a
jeep 8870e1f0-e5fa-4a42-a1dc-4b8bfa07789c
jeep 01977d67-1faa-49ed-aec1-c9c41b767fed
jeep 15aa16d5-8c5f-478b-aaac-804627f77057
宝马 ee38f7e6-b2df-4385-b57f-9ded3384642a

虽然car是一个全局变量但是通过动态代理产生的car每次都是一个全新的对象,这个就和sqlSession的产生是一样的效果。所以保证了线程安全,sqlSession通过SqlSessionInterceptor产生、释放等操作

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,484评论 11 349
  • 单独使用mybatis是有很多限制的(比如无法实现跨越多个session的事务),而且很多业务系统本来就是使用sp...
    七寸知架构阅读 8,816评论 0 53
  • 前面的章节主要讲mybatis如何解析配置文件,这些都是一次性的初始化过程。从本章开始讲解动态的过程,它们跟应用程...
    七寸知架构阅读 10,378评论 2 55
  • 1. 简介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的...
    笨鸟慢飞阅读 11,165评论 0 4
  • 炙热的阳光, 火辣辣的照射在满是灰尘的脸上。 额间的汗水, 顺着脸颊缓缓流淌。 / 为了加快速度, 手臂被铁丝划伤...
    南英子阅读 3,538评论 57 68

友情链接更多精彩内容