MyBatis+Spring简单整合

659 阅读7分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24天,点击查看活动详情

怎么说呢?现在的大家是不是都是面向Spring开发和面向搜索开发了,不会的搜并且很多框架都是和Spring进行了整合,没有办法,主流啊主流。

  • 使用Spring IoC可以有效管理各类Java资源,达到即插即拔功能;
  • 通过AOP框架,数据库事务可以委托给Spring处理,消除很大一部分的事务代码,配合MyBatis的高灵活、可配置、可优化SQL等特性,完全可以构建高性能的大型网站。 MyBatis和Spring两大框架已经成了Java互联网技术主流框架组合,它们经受住了大数据量和大批量请求的考验,在互联网系统中得到了广泛的应用。使用MyBatis-Spring使得业务层和模型层得到了更好的分离,与此同时,在Spring环境中使用MyBatis也更加简单,节省了不少代码,甚至可以不用SqlSessionFactory、SqlSession等对象。因为MyBatis-Spring为我们封装了它们。

配置MyBatis-Spring项目需要这么几步:

  • 配置数据源
  • 配置SqlSessionFactory。
  • 可以选择的配置有SqlSessionTemplate,在同时配置SqlSessionTemplate和SqlSessionFactory的情况下,优先采用SqlSessionTemplate
  • 配置Mapper,可以配置单个Mapper,也可以通过扫描的方法生成Mapper,比较灵活。

此时Spring IoC会生成对应接口的实例,这样就可以通过注入的方式来获取资源了。


01.配置SqlSessionFactoryBean

从MyBatis的介绍中,可以知道SqlSessionFactory是产生SqlSession的基础,因此配置SqlSessionFactory十分关键。在MyBatis-Spring项目中提供了SqlSessionFactoryBean去支持SqlSessionFactory的配置几乎可以配置所有关于MyBatis的组件,并且它也提供了对应的setter方法让Spring设置它们,所以完全可以通过Spring IoC容器的规则去配置它们,有注解版本和XML配置。 代码清单:配置SqlSessionFactoryBean

<!--配置SqlSessionFactoryBean-->
<bean id="SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 数据源,需要单独实例化,ref是注入数据源 -->
    <property name="dataSource" ref="dataSource"/>
    <!-- 加载核心配置文件 -->
    <property name="configLocation" value="classpath:config/mybatis-config.xml"/>
    <!-- 加载映射文件等等 -->
</bean>

这里配置了SqlSesionFactoryBean,但是只是配置了数据源,然后引入一个MyBatis配置文件,当然如果你所配置的内容很简单,是可以完全不引入MyBatis配置文件的,只需要通过Spring IoC容器注入即可,但是一般而言,较为复杂的配置,笔者还是推荐你使用MyBatis的配置文件,这样的好处在于不至于使得SqlSessionFactoryBean的配置全部依赖于Spring提供的规则,导致配置的复杂性。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <!-- 这个配置使全局的映射器启用或禁用缓存 -->
        <setting name="cacheEnabled" value="true"/>
        <!-- 允许 JDBC 支持生成的键。需要适当的驱动。如果设置为 true,则这个设置强制生成的键被使用,尽管一些驱动拒绝兼容但仍然有效(比如 Derby) -->
        <setting name="useGeneratedKeys" value="true"/>
        <!-- 配置默认的执行器。SIMPLE 执行器没有什么特别之处。REUSE 执行器重用预处理语句。BATCH 执行器重用语句和批量更新  -->
        <setting name="defaultExecutorType" value="REUSE"/>
        <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 设置超时时间,它决定驱动等待一个数据库响应的时间  -->
        <setting name="defaultStatementTimeout" value="25000"/>
    </settings>
</configuration>

02.SqlSessionTemplate组件

严格来说,SqlSessionTemplate并不是一个必须配置的组件,但是它也存在一定的价值。首先,它是线程安全的类,也就是确保每个线程使用的SqlSession唯一且不互相冲突。其次,它提供了一系列的功能,比如增、删、查、改等常用功能。

<!--配置SqlSessionTemplate-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg ref="SqlSessionFactory"/>
    <!-- <constructor-arg value="BATCH"/>  -->
</bean>

SqlSessionTemplate要通过带有参数的构造方法去创建对象,常用的参数是SqlSessionFactory和MyBatis执行器(Ex-ecutor)类型,取值范围是SIMPLE、REUSE、BATCH,这是我们之前论述过的执行器的3种类型。 代码清单:SqlSessionTemplate的应用

//假设的示例,没有什么用处了
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-cfg.xml");//ctx为Spring IoC容器

SqlSessionTemplate sqlSessionTemplate = ctx.getBean(SqlSessionTemplate.class);
Role role = new Role();
role.setRoleName("超级管理员");
role.setNote("测试执行SQL语句");
sqlSessionTemplate.insert("com.hanpang.mapper.RoleMapper.insertRole", role);
Long id = role.getId();
sqlSessionTemplate.selectOne("com.hanpang.mapper.RoleMapper.getRole", id);
role.setNote("update更新啦");
sqlSessionTemplate.update("com.hanpang.mapper.RoleMapper.updateRole", role);
sqlSessionTemplate.delete("com.hanpang.RoleMapper.deleteRole", id);

当运行一个SqlSessionTemplate时,它就会重新获取一个新的SqlSession,也就是说每一个SqlSession-Template运行的时候会产生新的SqlSession,所以每一个方法都是独立的SqlSession,这意味着它是安全的线程。
关于SqlSessionTemplate,目前运用已经不多,正如代码清单一样所示,它需要使用字符串表明运行哪个SQL,字符串不包含业务含义,只是功能性代码,并不符合面向对象的规范。与此同时,使用字符串时,IDE无法检查代码逻辑的正确性,所以这样的用法渐渐被人们抛弃了。注意,SqlSessionTemplate允许配置执行器的类型,当同时配置SqlSessionFactory和SqlSessionTemplate的时候,SqlSessionTemplate的优先级大于SqlSessionFactory。

03.配置MapperFactoryBean

MyBatis的运行只需要提供类似于RoleMapper.java的接口,而无须提供一个实现类。通过学习MyBatis运行原理,可以知道它是由MyBatis体系创建的动态代理对象运行的,所以Spring也没有办法为其生成实现类。为了解决这个问题,MyBatis-Spring团队提供了一个MapperFactoryBean类作为中介,我们可以通过配置它来实现我们想要的Mapper。使用了Mapper接口编程方式可以有效地在你的逻辑代码中擦除SqlSessionTemplate,这样代码就按照面向对象的规范进行编写了,这是人们乐于采用的形式。

代码清单:配置RoleMapper对象

<!--配置RoleMapper对象-->
<bean id="roleMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <!--RoleMapper接口将被扫描为Mapper -->
    <property name="mapperInterface" value="com.hanpang.mapper.RoleMapper"/>
    <property name="SqlSessionFactory" ref="SqlSessionFactory"/>
    <!--如果同时注入 sqlSessionTemplate 和 SqlSessionFactory,则只会启用sqlSessionTemplate -->
    <!-- <property name="sqlSessionTemplate"  ref="sqlSessionTemplate"/> -->
</bean>

这里可以看到MapperFactoryBean存在3个属性可以配置,分别是mapperInterface、sqlSessionTemplate和SqlSessionFac-tory,其中: 

  • mapperInterface是映射器的接口。 
  • 如果同时配置sqlSessionTemplate和SqlSessionFactory,那么它就会启用sqlSessionTemplate,而SqlSessionFac-tory作废。当我们配置这样的一个Bean,那么我们就可以使用下面的代码去获取映射器了。
RoleMapper roleMapper = ctx.getBean(RoleMapper.class);

04.配置MapperScannerConfigurer

这是一个通过扫描的形式进行配置Mapper的类,如果一个个去配置Mapper,显然工作量大,并且导致配置泛滥,有了它只需要给予一些简单的配置,它就能够生成大量的Mapper,从而减少工作量。首先我们需要知道它能够配置哪些属性,对于MapperScannerConfigurer它的主要配置项有以下几个: 

  • basePackage,指定让Spring自动扫描什么包,它会逐层深入扫描,如果遇到多个包可以使用半角逗号分隔。 
  • annotationClass,表示如果类被这个注解标识的时候,才进行扫描。对于开发而言,笔者建议使用这个方式进行注册对应的Mapper。在Spring中往往使用注解@Repository表示数据访问层(DAO,Data Access Object),所以本书的例子也是以此方式为主进行介绍的。 
  • SqlSessionFactoryBeanName,指定在Spring中定义SqlSessionFactory的Bean名称。如果sqlSessionTemplateBeanName被定义,那么它将失去作用。
  • markerInterface,指定实现了什么接口就认为它是Mapper。我们需要提供一个公共的接口去标记。 代码清单:通过扫描的方式配置RoleMapper
<!--通过扫描的方式配置RoleMapper-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.ssm.chapter12.mapper"/>
    <property name="SqlSessionFactoryBeanName" value="SqlSessionFactory"/>
    <!--使用sqlSessionTemplateBeanName将覆盖SqlSessionFactoryBeanName的配置-->
    <!--<property name="sqlSessionTemplateBeanName" value="SqlSessionFactory"/>-->
    <!--指定标注才扫描成为Mapper-->
    <property name="annotationClass" value="org.springframework.stereotype.Repository"/>
</bean>

05.MyBatis开启事务

/**
 * @Transactional 的参数
 * value                   |String                        | 可选的限定描述符,指定使用的事务管理器
 * propagation             |Enum: Propagation             | 可选的事务传播行为设置
 * isolation               |Enum: Isolation               | 可选的事务隔离级别设置
 * readOnly                |boolean                       | 读写或只读事务,默认读写
 * timeout                 |int (seconds)                 | 事务超时时间设置
 * rollbackFor             |Class<? extends Throwable>[]  | 导致事务回滚的异常类数组
 * rollbackForClassName    |String[]                      | 导致事务回滚的异常类名字数组
 * noRollbackFor           |Class<? extends Throwable>[]  | 不会导致事务回滚的异常类数组
 * noRollbackForClassName  |String[]                      | 不会导致事务回滚的异常类名字数组
 */
@Transactional(timeout=4)
public void testTransactional() {
        // dosomething..    
}

如果想使用手动提交事务,可以使用该方法。需要注入两个Bean,最后记得提交事务。

@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;

@Autowired
private TransactionDefinition transactionDefinition;

public void testHandleCommitTS(boolean exceptionFlag) {
//        DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
//        transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
    // 开启事务
    TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
    try {
        // dosomething            // 提交事务
        dataSourceTransactionManager.commit(transactionStatus);
    } catch (Exception e) {
        e.printStackTrace();
        // 回滚事务
        dataSourceTransactionManager.rollback(transactionStatus);
    }
}

现在的主流配置还是JavaConfig方式了,所有想想写一下这种方式:

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.alibaba.druid.pool.DruidDataSource;
import com.springdemo.bean.UserMapper;
//启动事务管理器
@EnableTransactionManagement
public class AppConfig1 {
    /**
    * 配置数据源
    */
    @Bean
    public DruidDataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=true");
        ds.setUsername("root");
        ds.setPassword("123456@#");
        ds.setInitialSize(5);
        return ds;
    }
    /**
    * 创建SqlSession示例
    */
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        //set方式注入数据源
        sessionFactory.setDataSource(dataSource);
        //设置配置信息
        org.apache.ibatis.session.Configuration cfg = new org.apache.ibatis.session.Configuration();//configuration
        cfg.setDefaultStatementTimeout(30);//设置相关参数,我这里就只用了一个
   
        //加载MyBatis的核心配置文件mybatis-config.xml
        //sqlSessionFactoryBean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:config/mybatis-config.xml"));
        sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("config/mybatis-config.xml"));
        //加载MyBatis映射文件,我们需要一次加载很多

        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/**/*Mapper.xml"));

        return sessionFactory.getObject();
    }
    /**
    * 获取Session示例
    */
    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
    /*
    @Bean
    public UserMapper userMapper(SqlSessionTemplate sqlSessionTemplate) throws Exception {
      return sqlSessionTemplate.getMapper(UserMapper.class);
    }
    */
    @Bean
    public  MapperScannerConfigurer mapperScanner() {
        MapperScannerConfigurer mapperScanner = new MapperScannerConfigurer();
        mapperScanner.setBasePackage("com.hanpang.**.mapper");
        mapperScanner.setSqlSessionFactoryBeanName("sqlSessionFactory");
        return mapperScanner;
    }
    
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

扫描多个包的情况:blog.csdn.net/qq_28597959…

数据源配置:segmentfault.com/a/119000002…