当前位置 : 首页 » 文章分类 :  开发  »  Spring-MyBatis

Spring-MyBatis

Spring 整合 MyBatis 笔记

mybatis-spring 官方中文文档
http://www.mybatis.org/spring/zh/index.html

MyBatis-Spring-Boot-Starter官方文档
http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/index.html


MyBatis 自动配置过程

mybatis-spring-boot-starter 中依赖 mybatis-spring-boot-autoconfigure,
mybatis-spring-boot-autoconfigure 中有个 spring.factories 文件:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

用于自动化配置,其中的配置类 MybatisAutoConfiguration 会被 Spring 自动加载。

MybatisAutoConfiguration 中,会读取Spring的MyBatis相关配置项来创建 SqlSessionFactory Bean,条件是没有其他 SqlSessionFactory 的实例存在时,也就是用户没有手动配置这个Bean时

@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
  SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
  factory.setDataSource(dataSource);
  factory.setVfs(SpringBootVFS.class);
  if (StringUtils.hasText(this.properties.getConfigLocation())) {
    factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
  }
  applyConfiguration(factory);
  if (this.properties.getConfigurationProperties() != null) {
    factory.setConfigurationProperties(this.properties.getConfigurationProperties());
  }
  if (!ObjectUtils.isEmpty(this.interceptors)) {
    factory.setPlugins(this.interceptors);
  }
  if (this.databaseIdProvider != null) {
    factory.setDatabaseIdProvider(this.databaseIdProvider);
  }
  if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
    factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
  }
  if (this.properties.getTypeAliasesSuperType() != null) {
    factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
  }
  if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
    factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
  }
  if (!ObjectUtils.isEmpty(this.typeHandlers)) {
    factory.setTypeHandlers(this.typeHandlers);
  }
  if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
    factory.setMapperLocations(this.properties.resolveMapperLocations());
  }
  Set<String> factoryPropertyNames = Stream
      .of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName)
      .collect(Collectors.toSet());
  Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
  if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
    // Need to mybatis-spring 2.0.2+
    factory.setScriptingLanguageDrivers(this.languageDrivers);
    if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
      defaultLanguageDriver = this.languageDrivers[0].getClass();
    }
  }
  if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
    // Need to mybatis-spring 2.0.2+
    factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
  }

  return factory.getObject();
}

也会创建 SqlSessionTemplate,条件是没有其他 SqlSessionTemplate 的实例存在时,也就是用户没有手动配置这个Bean时

@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
  ExecutorType executorType = this.properties.getExecutorType();
  if (executorType != null) {
    return new SqlSessionTemplate(sqlSessionFactory, executorType);
  } else {
    return new SqlSessionTemplate(sqlSessionFactory);
  }
}

开启下划线到驼峰的自动转换

通过 mybatis Generator 自动生成的mapper可以自动将 数据库字段的下划线分隔转为 java bean中的驼峰命名
但自己写的sql mapper无法自动转换
例如将如下的查询结果放到

@Select("select count(*) as 'day_pv', count(distinct ip) as 'day_uv' from page_view_transaction "
        + "where create_time >= #{start_time} and create_time < #{end_time};")
PageViewSum countPvAndUvByTime(@Param("start_time") LocalDateTime startTime, @Param("end_time") LocalDateTime endTime);

PageViewSum 这个bean中

@Data
public class PageViewSum {
    private Long dayPv;
    private Long dayUv;
}

默认是无法自动转换的
增加如下配置即可

mybatis:
  configuration:
    # 开启下划线到驼峰的自动转换. 作用:将数据库字段根据驼峰规则自动注入到对象属性
    map-underscore-to-camel-case: true

事务内切换数据源失败问题

@Transactional 注解的方法内 切换数据源会失败。

解决方法:
1、在事务外切换数据源,比如想切换到从库查询数据,在事务外切换到从库查到数据后再切换回原来的数据源,然后把查到的数据作为一个参数传入到事务方法中。
2、我们总监说的,在使用的时候,标注上
@Transactional(propagation=NOT_SUPPORTED)
把现有的事务挂起就好了


Spring整合MyBatis实例

mybatis-spring 第二章 入门
http://www.mybatis.org/spring/zh/getting-started.html

添加maven依赖

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>x.x.x</version>
</dependency>

配置sqlSessionFactory Bean

要和 Spring 一起使用 MyBatis,你需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器mapper类。

使用xml配置

在 MyBatis-Spring 中,SqlSessionFactoryBean 是用于创建 SqlSessionFactory 的。要 配置这个工厂 bean,放置下面的代码在 Spring 的 XML 配置文件中:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
</bean>

要注意 SqlSessionFactory 需要一个 DataSource(数据源,译者注) 。这可以是任意 的 DataSource,配置它就和配置其它 Spring 数据库连接一样。

使用JavaConfig配置

或者使用 JavaConfig 配置

@Configuration
public class MySQLConfig {
@Primary
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactoryBean() throws Exception {
    org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
    // 全局映射器启用缓存
    configuration.setCacheEnabled(true);
    // 查询时,关闭关联对象即时加载以提高性能
    configuration.setLazyLoadingEnabled(true);
    // 设置关联对象加载的形态,此处为按需加载字段(加载字段由SQL指 定),不会加载关联表的所有字段,以提高性能
    configuration.setAggressiveLazyLoading(false);
    // 对于未知的SQL查询,允许返回不同的结果集以达到通用的效果
    configuration.setMultipleResultSetsEnabled(true);
    // 允许使用列标签代替列名
    configuration.setUseColumnLabel(true);
    // 给予被嵌套的resultMap以字段-属性的映射支持
    configuration.setAutoMappingBehavior(AutoMappingBehavior.FULL);
    // 对于批量更新操作缓存SQL以提高性能
    configuration.setDefaultExecutorType(ExecutorType.SIMPLE);
    // 数据库超过25000秒仍未响应则超时
    configuration.setDefaultStatementTimeout(25000);
    configuration.setMapUnderscoreToCamelCase(true);
    // 允许使用自定义的主键值(比如由程序生成的UUID 32位编码作为键值),数据表的PK生成策略将被覆盖
    //configuration.setUseGeneratedKeys(true);

    Properties properties = new Properties();
    properties.setProperty("dialect", "mysql");

    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(dataSource());
    sqlSessionFactoryBean.setConfiguration(configuration);
    sqlSessionFactoryBean.setConfigurationProperties(properties);
    //sqlSessionFactoryBean.setConfigLocation(resolver.getResource("classpath:mybatis-config.xml"));
    sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:mapper/*.xml"));
    return sqlSessionFactoryBean.getObject();
}
}

定义Mapper接口

假设你定义了一个如下的数据 mapper 接口:

public interface UserMapper {
  @Select("SELECT * FROM users WHERE id = #{userId}")
  User getUser(@Param("userId") String userId);
}

配置Mapper Bean

mybatis-spring 第六章 注入映射器
http://www.mybatis.org/spring/zh/mappers.html

MyBatis-Spring 提供了一个动态代理的实现 MapperFactoryBean。这个类 可以让你直接注入数据映射器接口到你的 service 层 bean 中。当使用映射器时,你仅仅如调 用你的 DAO 一样调用它们就可以了,但是你不需要编写任何 DAO 实现的代码,因为 MyBatis-Spring 将会为你创建代理。

MapperFactoryBean 创建的代理控制开放和关闭 session,翻译任意的异常到 Spring 的 DataAccessException 异常中。此外,如果需要或参与到一个已经存在活动事务中,代理将 会开启一个新的 Spring 事务。

数据映射器接口可以按照如下做法加入到 Spring 中:

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

要注意,所指定的映射器类必须是一个接口,而不是具体的实现类。在这个示例中,注解被用来指定 SQL 语句,但是 MyBatis 的映射器 XML 文件也可以用。

MapperFactoryBean 创建的代理类实现了 UserMapper 接口,并且注入到应用程序中。 因为代理创建在运行时环境中(Runtime,译者注) ,那么指定的映射器必须是一个接口,而 不是一个具体的实现类。

如果 UserMapper 有一个对应的 MyBatis 的 XML 映射器文件, 如果 XML 文件在类路径的 位置和映射器类相同时, 它会被 MapperFactoryBean 自动解析。 没有必要在 MyBatis 配置文件中去指定映射器 , 除非映射器的 XML 文件在不同的类路径下 。 可以参考 SqlSessionFactoryBean 的 configLocation 属性(第三章)来获取更多信息。

一旦配置好,你可以用注入其它任意 Spring 的 bean 相同的方式直接注入映射器到你的 business/service 对象中。MapperFactoryBean 处理 SqlSession 的创建和关闭它。如果使用 了 Spring 的事务,那么当事务完成时,session 将会提交或回滚。最终,任何异常都会被翻 译成 Spring 的 DataAccessException 异常。

通过Mapper Bean执行SQL

调用 MyBatis 数据方法现在只需一行代码:

public class FooServiceImpl implements FooService {

private UserMapper userMapper;

public void setUserMapper(UserMapper userMapper) {
  this.userMapper = userMapper;
}

public User doSomeBusinessStuff(String userId) {
  return this.userMapper.getUser(userId);
}
}

Spring Boot整合MyBatis实例

在IDEA中创建Spring Initializr项目

添加maven依赖

1、引入整合MyBatis的核心依赖mybatis-spring-boot-starter
2、引入连接mysql的必要依赖mysql-connector-java
3、引入spring-boot-starter-test用来做单元测试验证数据访问

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.winterchen</groupId>
    <artifactId>springboot-mybatis-demo2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>springboot-mybatis-demo2</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.8.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

配置数据源

Spring Boot 默认配置文件为application.properties,在其中添加数据源配置:

spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

或者根据个人喜好改用更简洁的yml配置文件application.yml

spring:
  datasource:
     url: jdbc:mysql://127.0.0.1:3306/mytest
     username: root
     password: 123456
     driver-class-name: com.mysql.jdbc.Driver

spring boot自带的DataSourceAutoConfiguration会自动读取application.properties文件的spring.datasource.*属性并自动配置单数据源。
spring boot自动创建的默认数据源是tomcat jdbc connection pool,并将数据源自动注入到sqlSessionFactory中,sqlSessionFactory会自动注入到Mapper中,一切都不用管,直接拿起来使用就行了。


创建mysql数据表

创建数据库mytest,切换到mytest,并创建表t_user
sql语句如下:

CREATE DATABASE mytest;

USE mytest;

CREATE TABLE t_user(
  id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(255) NOT NULL ,
  password VARCHAR(255) NOT NULL ,
  phone VARCHAR(255) NOT NULL
) ENGINE=INNODB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8;

创建实体User

/**
 * User实体映射类
 * Created by Administrator on 2017/11/24.
 */

public class User {
    private Integer id;
    private String name;
    private String password;
    private String phone;
    //省略 get 和 set ...
}

创建Mapper接口

import com.xxx.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

/**
 * User映射类
 * Created by Administrator on 2017/11/24.
 */
@Mapper
public interface UserMapper {

    @Select("SELECT * FROM T_USER WHERE PHONE = #{phone}")
    User findUserByPhone(@Param("phone") String phone);

    @Insert("INSERT INTO T_USER(NAME, PASSWORD, PHONE) VALUES(#{name}, #{password}, #{phone})")
    int insert(@Param("name") String name, @Param("password") String password, @Param("phone") String phone);

}

可以在Mapper类上面添加注解@Mapper来使Spring将其自动扫描为bean,但每个Mapper类都要加比较麻烦,或者在启动类中添加@MapperScan实现对所有Mapper类的自动扫描。


SpringBoot启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootMybatisDemo2Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootMybatisDemo2Application.class, args);
    }
}

单元测试

import com.xxx.User;
import com.xxx.UserMapper;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootMybatisDemo2ApplicationTests {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void test(){
        userMapper.insert("winterchen", "123456", "12345678910");
        User u = userMapper.findUserByPhone("12345678910");
        Assert.assertEquals("winterchen", u.getName());
    }
}

Spring boot Mybatis 整合(注解版)
https://blog.csdn.net/Winter_chen001/article/details/78622141

Spring boot Mybatis 整合(完整版)
https://blog.csdn.net/winter_chen001/article/details/77249029

spring boot(六):如何优雅的使用mybatis(更详细)
https://www.cnblogs.com/ityouknow/p/6037431.html

Spring Boot整合MyBatis(《Spring Cloud微服务实战》作者翟永超)
http://blog.didispace.com/springbootmybatis/


上一篇 Spring-Context-Scheduling

下一篇 Java-Jar包

阅读
评论
2,677
阅读预计12分钟
创建日期 2018-06-26
修改日期 2018-12-20
类别

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论