Mybatis 系列 6:MybatisUtil 提取,作用域(scope)和生命周期

回顾一下,之前我们在 Mybatis 中进行查询操作的时候,查询单个对象、查询列表信息等的时候,我们需要每次都重复写创建 SqlSessionFactory、SqlSession 等相关代码,如:

// 根据主键 id 查询单个用户
@Test
void testGet() throws Exception 
{
    // 从classpath 路径去加载mybatis 全局配置文件
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    // 创建SQLSessionFactory对象,好比是DataSource
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
    // 创建SQLSession对象,好比是Connection
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 具体操作(增删改查)
    User user = sqlSession.selectOne("com.mxz.mybatis.mapper.UserMapper.get"1L);
    // 关闭会话连接
    sqlSession.close();
    System.out.println(user);
}
// 查询所有用户
@Test
void testListAll() throws Exception 
{
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    List<User> userList = sqlSession.selectList("com.mxz.mybatis.mapper.UserMapper.ListAll");
    sqlSession.close();
    for (User user : userList){
        System.out.println(user);
    }
}

这样一来,如果后面有多个操作的话,代码就显得很冗余,所以我们需要提取共同的代码,创建 MybatisUtil 类。

这里边共同的代码主要是,加载主配置文件、创建 SqlSessionFactory 、打开 SqlSession 的代码部分,因此我们主要抽取该部分代码。

在 Mybatis 官方文档「入门」模块中说到,如何构建 SqlSessionFactory 和获取 SqlSession,以及作用域(Scope)和生命周期问题。

不同作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题。

SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。

SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次。因此 SqlSessionFactory 的最佳作用域是应用作用域。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

所以,我们之前写的测试用中,无论是查询单个还是多个对象的时候,每次都要加载主配置文件,创建新的 SqlSessionFactory,是很没有必要的,在应用开发中,只要有一个 SqlSessionFactory 就够了,保持单例。

SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。

所以,要记住的是,SqlSessionFactory 是线程安全的,和数据库连接池 dataSource 一样,整个应用中有一份就够了;SqlSession 和数据库连接对象 Connection 是一样的,是线程不安全的,每次都要创建新的连接,用完了都要去关闭它,这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。

下面的示例就是一个确保 SqlSession 关闭的标准模式:

try (SqlSession session = sqlSessionFactory.openSession()) {
  // 你的应用逻辑代码
}

在你的所有的代码中一致地使用这种模式来保证所有数据库资源都能被正确地关闭。

提取相关代码,创建工具类如下:

public class MybatisUtil {
     private static SqlSessionFactory factory;

     static {
         InputStream in = null;
         try {
             in = Resources.getResourceAsStream("mybatis-config.xml");
         } catch (IOException e) {
             e.printStackTrace();
        }
        factory = new SqlSessionFactoryBuilder().build(in);
    }

    // 返回一个SqlSession对象
    public static SqlSession getSession(){
        return factory.openSession();
    }
}

然后,改造一下我们之前写的查询单个的方法:

 @Test
 void testGet() throws Exception {
     SqlSession sqlSession = null;
     try {
         sqlSession = MybatisUtil.getSession();
         // 具体操作(增删改查)
         User user = sqlSession.selectOne("com.mxz.mybatis.mapper.UserMapper.get"1L);
         System.out.println(user);
     } finally {
         // 正常关闭 sqlSession,所以无论怎么样,最后一定要关闭 sqlSession
         sqlSession.close();
     }
}

为了方便,我们可以使用 Java 7 的自动资源关闭语法:在 try 后面加上(),在里边打开资源:

@Test
void testGet() throws Exception {
    try (SqlSession sqlSession = MybatisUtil.getSession();) {
        // 具体操作(增删改查)
        User user = sqlSession.selectOne("com.mxz.mybatis.mapper.UserMapper.get"1L);
        System.out.println(user);
    }
}

更方便的方法是使用 lombook 工具的 @Cleanup 注解

@Test
void testGet() throws Exception {
    @Cleanup
    SqlSession sqlSession = MybatisUtil.getSession();
    User user = sqlSession.selectOne("com.mxz.mybatis.mapper.UserMapper.get"1L);
    System.out.println(user);
}

使用@Cleanup 注解编译后生成的代码如下:底层帮忙把资源关闭了

@Test
void testGet() throws Exception 
{
    SqlSession sqlSession = MybatisUtil.getSession();
    try {
        User user = (User)sqlSession.selectOne("com.mxz.mybatis.mapper.UserMapper.get"1L);
        System.out.println(user);
    } finally {
        if (Collections.singletonList(sqlSession).get(0) != null) {
            sqlSession.close();
        }
    }
}

tips:查看官方文档是学习一门新语言最有效的方法。

系列预告:Mybatis 系列 7:Mybatis 的 update、delete、insert 操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值