Spring Boot集成MyBatis

本文先介绍Spring Boot的数据源的配置, 然后介绍如何集成MyBatis(有两种方式)



配置数据源

Java的javax.sql.DataSource接口提供了标准的使用数据库连接的方法. 建立数据库连接只需要提供URL等相关参数, Spring Boot就可以创建数据源. 当然我们必须提供相应的包:spring-boot-starter-data-jpa, 这个包已经把spring-boot-starter-jdbc包含进来了.

Spring提供了内嵌数据库的支持(包括H2, HSQL, Derby), 当我们使用内嵌数据库的时候, 连URL等这些配置都不需要了, 直接导入相应的数据库依赖包就可以, 比如使用HSQL, 只需添加pom依赖:

<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <scope>runtime</scope>
</dependency>

如果不用内嵌数据库, 而是用Mysql的话, 就需要URL等配置, 然后在把Mysql相关的包依赖添加进来:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

连接池

线上使用数据库通常都会通过数据库连接池, Spring Boot会按照下面的策略选取一个:

  • Tomcat 连接池性能较高, tomcat-jdbc可用时优先选择(spring-boot-starter-jdbc里已经包含了tomcat-jdbc包)
  • 如果HikariCP可用则选择它
  • 如果Commons DBCP可用则使用它, 但不推荐
  • 最后, 如果Commons DBCP2可用, 则使用它

我们可以通过在应用配置的参数spring.datasource.type指定一个连接池, 比如使用阿里的德鲁伊(Druid):spring.datasource.type=com.alibaba.druid.pool.DruidDataSource, 这个需要添加依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.18</version>
</dependency>

关于数据源的配置, 可在应用配置文件application.properties中进行配置

spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driver-class-name=com.mysql.jdbc.Drive

对于不同连接池还有一些其他参数(spring.datasource.tomcat.*, spring.datasource.hikari.*), 比如:

# Number of ms to wait before throwing an exception if no connection is available.
spring.datasource.tomcat.max-wait=10000
# Maximum number of active connections that can be allocated from this pool at the same time.
spring.datasource.tomcat.max-active=50
# Validate the connection before borrowing it from the pool.
spring.datasource.tomcat.test-on-borrow=true

关于数据源, 我们仅需要配置一下参数, Spring Boot已经自动帮我们配置了, 使用的时候直接注入就可以.


集成MyBatis

我们可以使用传统的mybatis-spring的方式, 引入mybatismybatis-spring包, 手动配置MyBatis, 这种方式跟原来使用Spring框架时比较类似, 比较灵活, 可自行配置SqlSessionFactoryBean. 这是我们将用的第一种方式.

MyBatis官方也提供了一个starter形式的包(GitHub地址), 这个starter里已经包含了mybatismybatis-spring. 这是第二种方式, 这种方式更简单, 只需要在应用配置application.yml中添加配置即可.

不管哪种方式, 包依赖和数据源的配置少不了, 因此先把数据源配置(在application.yml中)贴出来:

# 数据库配置
spring:
    datasource:
        url: jdbc:mysql://10.90.187.120:3306/test?useUnicode=true&characterEncoding=UTF-8
        username: dbname
        password: dbpass
        driver-class-name: com.mysql.jdbc.Driver

包依赖(第一种方式只要包含mybatismybatis-spring即可):

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>

mybatis-spring方式

这种方式不再需要额外的配置, 主配置类该怎么写还是怎么写, 另外我们专门为MyBatis添加两个配置类:

// com.example.project.config.MyBatisConfig.java 这个类是MyBatis的配置类
@Configuration  // 不要忘记加配置注解
@EnableTransactionManagement  // 事务管理
public class MyBatisConfig implements TransactionManagementConfigurer {
    private static final Logger logger = LoggerFactory.getLogger(MyBatisConfig.class);

    @Resource
    DataSource dataSource;  // 这个数据源, Spring boot 会自动注入

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactoryBean() {
        SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
        // 设置数据源
        sqlSessionFactory.setDataSource(dataSource);
        // 自动重命名
        sqlSessionFactory.setTypeAliasesPackage("com.example.project.entity");
        // 设置 typeHandler
        sqlSessionFactory.setTypeHandlersPackage("com.example.project.typeHandler");
        // 添加拦截器插件 (如果有的话, 分页插件, 分表插件等)
//        sqlSessionFactory.setPlugins(new Interceptor[]{pageHelper});
        // 设置 mapper 文件
        try {
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            sqlSessionFactory.setMapperLocations(resolver.getResources("classpath:sqlmaps/*.xml"));
            return sqlSessionFactory.getObject();
        } catch (Exception e) {
            logger.error("初始化SqlSessionFactory失败", e);
            throw new RuntimeException(e);
        }
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    @Bean
    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return new DataSourceTransactionManager(dataSource);
    }
}

// com.example.project.config.MyBatisMapperScannerConfig.java 这个类用来设置自动扫描的路径
@Configuration
// MapperScannerConfigurer 执行的比较早, 所以要加下面这个注释, 不然其他 bean 还没初始化
@AutoConfigureAfter(MyBatisConfig.class)  // 确保在 MyBatisConfig 初始化之后初始化
public class MyBatisMapperScannerConfig {

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        // 设置自动扫描包, 该包下的Mapper(Dao)将会被mybatis自动注册, 不用写实现类
        mapperScannerConfigurer.setBasePackage("com.example.project.dao");
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
        return mapperScannerConfigurer;
    }
}

这种方式, MyBatis就配置完了, 剩下的就是写实体和对应的Mapper(Dao)了.

// com.example.project.entity.User.java
@Data
public class UserCreditRecord {
    private Long id;
    private String name;
    private String age;
}
// com.example.project.dao.UserDao.java
@Repository
public interface UserDao {

   @Select("select * from user where id = #{id}")
    User getById(@Param("id") Long id);
}

当实体跟数据库中字段名字一致的时候没啥问题, 但实体字段跟数据库字段不一致时(比如一个驼峰, 一个用下划线)就比较麻烦了, 这种情况我们可以不使用注解形式, 而使用User.xml文件, 在文件中可以select user_name as userName也可以通过resultMap来制定数据库字段名column与实体属性名property的映射关系;

还有一个第三方Mapper插件, 可以完美解决这个问题, 参考这里

mybatis-spring-boot-starter方式

这种方式就更简单了, 仅仅需要在应用配置文件application.yml添加一些配置:

# mybatis 配置
mybatis:
    typeAliasesPackage: com.example.project.entity
    typeHandlersPackage: com.example.project.handler  # 如果有 typeHandler
    mapperLocations: classpath:sqlmaps/*.xml          # mapper 文件
    configLocation: classpath:mybatis-config.xml      # 可以不设置

然后从 main类 上添加注解:@MapperScan("com.example.project.dao"), 扫描Mapper(Dao)的位置即可. 其他的Spring Boot都帮我们做了, 接下来就专注于写Daoentity就可以了.

注意上面的mybatis的配置文件classpath:mybatis-config.xml, 如果没有这个文件就不要配置, 要是配置了就保证存在, 网上找了个例子:

<?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>
        <!-- 这个配置使全局的映射器启用或禁用缓存。系统默认值是true -->
        <setting name="cacheEnabled" value="true"/>
        <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。 系统默认值是true -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 允许或不允许多种结果集从一个单独的语句中返回(需要适合的驱动)。 系统默认值是true -->
        <setting name="multipleResultSetsEnabled" value="true"/>
        <!--使用列标签代替列名。不同的驱动在这方便表现不同。参考驱动文档或充分测试两种方法来决定所使用的驱动。 系统默认值是true -->
        <setting name="useColumnLabel" value="true"/>
        <!--允许 JDBC 支持生成的键。需要适合的驱动。如果设置为 true 则这个设置强制生成的键被使用,尽管一些驱动拒绝兼容但仍然有效(比如Derby)。 系统默认值是false -->
        <setting name="useGeneratedKeys" value="false"/>
        <!--配置默认的执行器。SIMPLE 执行器没有什么特别之处。REUSE 执行器重用预处理语句。BATCH 执行器重用语句和批量更新 系统默认值是SIMPLE -->
        <setting name="defaultExecutorType" value="SIMPLE"/>
        <!--设置超时时间,它决定驱动等待一个数据库响应的时间。 系统默认值是null -->
        <setting name="defaultStatementTimeout" value="25000"/>
    </settings>
    <!-- 设置插件 -->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper"> <!-- 这是一个第三方的分页插件 -->
            <property name="reasonable" value="true"/>
            <property name="offsetAsPageNum" value="true"/>
            <property name="rowBoundsWithCount" value="true"/>
            <property name="pageSizeZero" value="true"/>
        </plugin>
    </plugins>
</configuration>