mybatis中其他数据源也使用XML进行操作(SqlSessionFactory.openSession(Connection connection)方法)

1. 前言

  1. 当前在使用springboot+mybatis的时候,通常会先在配置文件中配置好数据源,并在Mapper.xml文件编写好相关SQL,使用mybatis进行对数据库进行所谓的crud操作。

  2. 有时候会出现一个项目需要跟多个数据源进行相关操作,通常是使用JdbcTemplate对另一个数据源进行crud相关操作:
    在这里插入图片描述

  3. 虽也可以实现功能,但将SQL写在类里面,与本数据源将SQL写到了XML文件中,总是感觉格格不入,为了统一项目SQL的存放位置,进而研究了SqlSessionFactory.openSession(Connection connection)方法。

2. 先说结论

  1. 在mybatis中SqlSessionFactory中有个方法是 SqlSessionFactory.openSession(Connection connection)方法,可以直接给个数据库连接创建SqlSession

  2. 当拥有了SqlSession,可以使用SqlSession.getMapper(Class clazz) 来获取所谓的Mapper对应的dao层。

  3. 但遗憾的是,无法使用springboot帮我们创建的bean对象SqlSessionFactory,因为其默认是使用SpringManagedTransaction事务,在SqlSessionFactory.openSession(Connection connection)会报错:New spring transactions require a DataSource.

  4. 所以只能自己创建如下:

      @org.junit.Test
        public void selectAll() throws Exception{
            // 其他数据源
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
            dataSource.setUsername("root");
            dataSource.setPassword("HelloWorld");
    
            // 生成SqlSessionFactoryBean对象,为了创建SqlSessionFactory
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            // 数据源
            sqlSessionFactoryBean.setDataSource(dataSource);
            // 接口对应mapper的路径
            sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/*.xml"));
            // 设置事务工厂类,不使用SpringManagedTransaction,原因思考过程已说
            sqlSessionFactoryBean.setTransactionFactory(new JdbcTransactionFactory());
            
            // 获取SqlSessionFactory对象,为了创建SqlSession对象
            final SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBean.getObject();
            // 创建SqlSession
            final SqlSession sqlSession = sqlSessionFactory.openSession(dataSource.getConnection());
            // 获取mapper对应的dao层接口
            final UserDao userDao = sqlSession.getMapper(UserDao.class);
            final List<User> users = userDao.selectAll();
            System.out.println(users);
        }
    
  5. 封装为工具类

     private <T> T getMapper(DataSource dataSource, Class<T> clazz) throws Exception {
            SqlSession sqlSession = createSqlSession(dataSource);
            return sqlSession.getMapper(clazz);
        }
    
        private SqlSession createSqlSession(DataSource dataSource) throws Exception {
            // 生成SqlSessionFactoryBean对象,为了创建SqlSessionFactory
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            // 数据源
            sqlSessionFactoryBean.setDataSource(dataSource);
            // 接口对应mapper的路径
            sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/*.xml"));
            // 设置事务工厂类,不使用SpringManagedTransaction,原因思考过程已说
            sqlSessionFactoryBean.setTransactionFactory(new JdbcTransactionFactory());
    
            // 获取SqlSessionFactory对象,为了创建SqlSession对象
            final SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBean.getObject();
            // 创建SqlSession
            return sqlSessionFactory.openSession(dataSource.getConnection());
        }
    

3. 例子

1. 准备数据

  1. 准备一张表如下:
    在这里插入图片描述

  2. 准备好项目并写好对应的Mapper,如下:

    // dao层
    @Mapper
    public interface UserDao {
        List<User> selectAll();
    }
    
    <!--UserDao.xml -->
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.example.csdn2.mybatis_sqlSession_test.dao.UserDao">
        <select id="selectAll" resultType="com.example.csdn2.mybatis_sqlSession_test.User">
            select * from user2
        </select>
    </mapper>
    

    在这里插入图片描述

2. 思考过程

  1. 一般我们使用mybatis会如下:
    先将yml配置文件配置数据源,mybatis的Mapper位置

    mybatis:
      mapper-locations: classpath:mappers/*.xml
      type-aliases-package: com.example.csdn2
    
    spring:
      datasource:
        username: root
        password: HelloWorld
        url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    
    

    然后在需要的service层注入dao层的bean对象,直接使用:

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class Test {
    
        @Autowired
        private UserDao userDao;
    
        @org.junit.Test
        public void selectAll() throws Exception{
            System.out.println(userDao.selectAll());
        }
    }
    

    在这里插入图片描述

  2. 上述过程中,是在springboot启动的时候,扫描启动类下的包带有@Mapper的接口,生成其代理的bean对象放入spring容器中,当我们需要使用相关dao层的crud的时候,直接注入即可使用。

  3. 而发现,spring启动帮忙生成的dao层的bean对象已经是跟本项目配置文件中配置的数据源绑定起来了,而我们最开始的目的是将其他数据源也可以使用mybatis的xml方式(sql写入其内),这里固然是无法直接使用spring提供好的dao层bean对象啦,毕竟数据源都来自不一样的内容。

  4. 所以现在的问题:我们要如何创建dao层接口的代理bean对象呢?

  5. mybatis的核心重要类之一:SqlSession,当点进去看该类(接口)的时候,我们惊奇发现mybatis中还可以允许手动创建dao层的bean对象,即在SqlSession接口中有这样子的一个方法:

     /**
       * Retrieves a mapper.
       * @param <T> the mapper type
       * @param type Mapper interface class 
       * @return a mapper bound to this SqlSession 
       */
       // 根据注释推理:
       // 1. Mapper interface class 一个接口且是mapper接口
       // 2. 返回一个mapper对象且绑定这次的sql连接
      <T> T getMapper(Class<T> type);
    

    具体用法如下:

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class Test {
        @Autowired
        private SqlSession sqlSession;
    
        @org.junit.Test
        public void selectAll() throws Exception{
        	// 创建dao层的对象
            final UserDao userDao = sqlSession.getMapper(UserDao.class);
            // 使用方法,查询出内容
            System.out.println(userDao.selectAll());
        }
    }
    

    在这里插入图片描述
    (温馨小提示: 这里的内容是涉及mybatis部分内容,若不熟悉可以先自行百度,或者先认为有这个结论)

  6. 这里我们先小结一下,上述主要是讲了:

    1. 在springboot中一般使用mybatis的方式:知道spring启动会帮我们生成对应dao层的bean对象提供给我们使用。
    2. mybatis可以允许我们手动创建dao层的bean对象,并且使用
  7. 到这里,有的人可能会提问,你怎么知道上述代码中getMapper()方法出来的userDao是新的bean对象,而不是spring容器里面的呢?好问题,来证明一下:

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class Test {
    
        @Autowired
        private UserDao userDao;
    
        @Autowired
        private SqlSession sqlSession;
    
    
        @org.junit.Test
        public void selectAll() throws Exception{
            final UserDao userDao = sqlSession.getMapper(UserDao.class);
            System.out.println("get出来的对象:" + userDao);
            System.out.println("注入出来的对象:" + this.userDao);
        }
    }
    

    在这里插入图片描述

  8. 根据上述使用得知,mybatis可以允许我们手动创建dao层的bean对象,并且使用,但仍然发现,似乎还是使用与本项目配置文件中的数据源,固然还是有问题。这是时候我们知道在spring容器中的SqlSession对象也是用配置文件数据源,那么也就是说明该对象我们无法使用了,得自己创建该对象

  9. 现在的问题则抛到了:如何自己创建SqlSession对象呢?

  10. 当搜索源码的时候,无意中发现SqlSessionFactory类,一看到Factory,这是什么,工厂呀!!!,固然是创建SqlSession的东西,经过一番寻找,发现其方法中有openSession()方法
    在这里插入图片描述

  11. 看我们发现了什么:为一个连接创建 SqlSession

    1. 参数是一个连接:我们可以得到,可以输入其他数据库连接,然后返回SqlSession。
    2. 如果我们得到了SqlSession,岂不是可以直接生成dao层的代理bean对象,卧槽,直接可以使用mapper了。
  12. 迅速编写代码测试一波:

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class Test {
    
        @Autowired
        private SqlSessionFactory sqlSessionFactory;
    
        @org.junit.Test
        public void selectAll() throws Exception{
            // 其他数据源
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setUrl("jdbc:mysql://localhost:3308/mybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
            dataSource.setUsername("root");
            dataSource.setPassword("root_pwd");
    
            // 创建SqlSession
            final SqlSession sqlSession = sqlSessionFactory.openSession(dataSource.getConnection());
            final UserDao userDao = sqlSession.getMapper(UserDao.class);
            final List<User> users = userDao.selectAll();
            System.out.println(users);
        }
    }
    

    在这里插入图片描述

  13. 可恶呀,这里会报错,报错内容是spring的事务要求一个数据源,引起我们思考,我们传入的是一个connection,难道是这里传错内容了?但是根据SqlSessionFactory.openSession()确实可以给个连接?奇怪,完全不懂,没办法了,只能使用绝招,跟源码。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  14. 当时我看到这里就有点懵了,为啥这里会跟事务扯上关系了呢?难道这个是必须的吗?没办法,后来查阅资源后,得到这一句话:

    1. mybatis操作的时候跟数据库的每一次连接,都需要创建一个会话,我们用openSession()方法来创建。这个会话里面需要包含一个Executor用来执行 SQL。
    2. Executor又要指定事务类型和执行器的类型
  15. 很好,非常好,看来是无法绕过这个事务了,当我们点击TransactionFactory接口(创建Transaction事务的接口):
    在这里插入图片描述
    可以看到有三种实现类,我们知道上述源码跟踪发现,刚刚抛异常的是 第三个工厂创建出来的springManagedTransaction事务,点击其他类型工厂发现,其他两种不会报错:
    在这里插入图片描述

  16. 现在的问题就到了:springboot启动的时候,是如何指定这个事务工厂类型呢,如果我们让它使用其他两种类型,岂不是可以实现为一个连接创建 SqlSession,得到了SqlSession,可以直接生成dao层的代理bean对象,直接可以使用mapper了

  17. 很遗憾,我翻阅资料发现,如果是使用了springboot整合mybatis的话,那还真没办法,如果是单独使用mybatis就可以,在单独使用mybatis的时候,可以从配置文件入手,这是我觉得比较好的一篇文章:https://blog.csdn.net/niujifei/article/details/125519028。

  18. 其实到了这里就是有关于springboot整合mybatis的相关知识点了,这里还是不详细说明了,只是看个结果:
    在这里插入图片描述

    在这里插入图片描述

  19. 也就是说 spring自带生成的SqlSessionFactory工厂里面的事务就是用SpringManagedTransaction工厂创建的,固然没办法按照我们一开始预想的实现方案,可恶啊。

  20. 要放弃了吗?不,既然spring帮我们生成的bean对象无法实现,我们直接自己new 需要的对象出来,给我冲。

3. 结论

  1. 根据上述思考过程,spring生成的无论是SqlSessionFactoryBean,SqlSessionFactory,SqlSession都无法现实我们需要的,那么我们就自己构建

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class Test {
        
        @org.junit.Test
        public void selectAll() throws Exception{
            // 其他数据源
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
            dataSource.setUsername("root");
            dataSource.setPassword("HelloWorld");
    
            // 生成SqlSessionFactoryBean对象,为了创建SqlSessionFactory
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            // 数据源
            sqlSessionFactoryBean.setDataSource(dataSource);
            // 接口对应mapper的路径
            sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/*.xml"));
            // 设置事务工厂类,不使用SpringManagedTransaction,原因思考过程已说
            sqlSessionFactoryBean.setTransactionFactory(new JdbcTransactionFactory());
    
            // 获取SqlSessionFactory对象,为了创建SqlSession对象
            final SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBean.getObject();
            // 创建SqlSession
            final SqlSession sqlSession = sqlSessionFactory.openSession(dataSource.getConnection());
            // 获取mapper对应的dao层接口
            final UserDao userDao = sqlSession.getMapper(UserDao.class);
            final List<User> users = userDao.selectAll();
            System.out.println(users);
        }
    }
    

    在这里插入图片描述

  2. 我们只要稍微封装一下该方法,就可以作为工具类了,如下:

    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class Test {
    
        @org.junit.Test
        public void selectAll() throws Exception {
            // 其他数据源
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
            dataSource.setUsername("root");
            dataSource.setPassword("HelloWorld");
    
            // 获取mapper对应的dao层接口
            final UserDao userDao = getMapper(dataSource, UserDao.class);
            final List<User> users = userDao.selectAll();
            System.out.println(users);
        }
    
        private <T> T getMapper(DataSource dataSource, Class<T> clazz) throws Exception {
            SqlSession sqlSession = createSqlSession(dataSource);
            return sqlSession.getMapper(clazz);
        }
    
        private SqlSession createSqlSession(DataSource dataSource) throws Exception {
            // 生成SqlSessionFactoryBean对象,为了创建SqlSessionFactory
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            // 数据源
            sqlSessionFactoryBean.setDataSource(dataSource);
            // 接口对应mapper的路径
            sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/*.xml"));
            // 设置事务工厂类,不使用SpringManagedTransaction,原因思考过程已说
            sqlSessionFactoryBean.setTransactionFactory(new JdbcTransactionFactory());
    
            // 获取SqlSessionFactory对象,为了创建SqlSession对象
            final SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBean.getObject();
            // 创建SqlSession
            return sqlSessionFactory.openSession(dataSource.getConnection());
        }
    }
    
    
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值