1. 程式人生 > >No.3 Spring boot 整合Mybatis

No.3 Spring boot 整合Mybatis

一、Mybatis是什麼:

mybatis是一個優秀的基於java的持久層框架,它內部封裝了jdbc,使開發者只需要關注sql語句本身,而不需要花費精力去處理載入驅動、建立連線、建立statement等繁雜的過程。同時,mybatis提供了基於XML或者基於註解的動態SQL的方式,使得我們可以控制和優化SQL。

二、Springboot整合Mybatis兩種方式:

A. Spring Boot中引入了自動配置,讓開發者利用起來更加的快捷,當我們引入mybatis-spring-boot-starter的時候,springboot會按照預設直接配置好mybatis的相關元件,使得我們可以直接使用mybatis。方法如下所示:

1. 在pom.xml中引入相關依賴:(主要是阿里的druid資料庫連線池和mybatis-spring-boot-starter)

<?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.springboot.yanming</groupId>
	<artifactId>mybatis</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>mybatis</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.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.1.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>

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

	</dependencies>

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


</project>

2. 在application.yml(或者是.properties格式)中,設定資料庫和mybatis引數:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/springboot
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    filters: stat
    maxActive: 20
    initialSize: 1
    maxWait: 60000
    minIdle: 1
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: select 'x'
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    maxOpenPreparedStatements: 20

mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml
  type-aliases-package: com.springboot.yanming.mybatis.model



Mybatis部分,我們聲明瞭Mapper類對應的.xml檔案的路徑(resources根路徑下的mapper資料夾),和mybatis的POJO位置

到此為止,mybatis的配置部分已經結束了,我們只需要編寫POJO,Mapper和Dao即可

3.編寫User的Mapper類:

package com.springboot.yanming.mybatis.mapper;

import com.springboot.yanming.mybatis.model.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

/**
 * @Author: YanMing
 * @Description:
 * @Date: Created in 12:20 2017/11/27
 */
@Mapper
public interface UserMapper {

    //@Select("SELECT username,password,sex FROM user WHERE username = #{username}")
    User findUserByName(@Param("username")String username);

    void insertUser(User user);
}

Mapper類就是我們用來訪問資料庫的主要工具了。我們可以使用@Select等註解,直接在方法上邊寫動態SQL,也可以給Mapper編寫一個UserMapper.xml,在XML檔案中宣告statement。(idea 有Mybatis 的外掛,可以高亮Mapper中沒有新增statement的方法,同時alt+enter生成Mapper.xml。也可以在上邊提到的resources/mapper中直接新增以下UserMapper.xml)

另外,還要在SpringBoot啟動函式上添加註解@MapperScan("com.springboot.yanming.mybatis.mapper"),告知主函式Mapper的掃描路徑,使@Mapper註解生效。

4. 編寫UserMapper.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.springboot.yanming.mybatis.mapper.UserMapper">
    <sql id="ALL_COLUMN">
        username,password,sex
    </sql>
    <insert id="insertUser" parameterType="com.springboot.yanming.mybatis.model.User">
        INSERT INTO user(username, password, sex) VALUES (
        #{username},
        #{password},
        #{sex}
        )
    </insert>
    <select id="findUserByName" resultType="com.springboot.yanming.mybatis.model.User">
        SELECT
        <include refid="ALL_COLUMN"></include>
        FROM user WHERE username = #{username};
    </select>
</mapper>

到此, 我們的Mapper就宣告為Bean了,可以直接使用@Autowired注入到 Dao中。Dao部分的編寫比較簡單,直接上程式碼

5. 編寫DAO

package com.springboot.yanming.mybatis.dao;

import com.springboot.yanming.mybatis.model.User;
import org.springframework.stereotype.Repository;

/**
 * @Author: YanMing
 * @Description:
 * @Date: Created in 12:24 2017/11/27
 */

public interface UserDao {

    User findUserByName(String username);

    void insertUser(User user);
}
package com.springboot.yanming.mybatis.dao.impl;

import com.springboot.yanming.mybatis.dao.UserDao;
import com.springboot.yanming.mybatis.mapper.UserMapper;
import com.springboot.yanming.mybatis.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

/**
 * @Author: YanMing
 * @Description:
 * @Date: Created in 19:15 2017/11/27
 */
@Repository
public class UserDaoImpl implements UserDao{
    @Autowired
    UserMapper userMapper;

    @Override
    public User findUserByName(String username) {
        return userMapper.findUserByName(username);
    }

    @Override
    public void insertUser(User user) {
        userMapper.insertUser(user);
    }
}

6. 編寫Spirngboot Junit測試用例

package com.springboot.yanming.mybatis.mapper;

import com.springboot.yanming.mybatis.MybatisApplication;
import com.springboot.yanming.mybatis.dao.UserDao;
import com.springboot.yanming.mybatis.model.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.junit.Assert.*;

/**
 * @Author: YanMing
 * @Description:
 * @Date: Created in 12:28 2017/11/27
 */
@RunWith(SpringJUnit4ClassRunner.class)
//@WebAppConfiguration
@SpringBootTest(classes = MybatisApplication.class)
@SpringBootConfiguration
public class UserMapperTest {
    @Autowired
    UserDao userDao;

    @Test
    public void insertAUser(){
        User user = new User();
        user.setSex("man");
        user.setUsername("yanming");
        user.setPassword("123456");
        userDao.insertUser(user);
        User res = userDao.findUserByName("xiaoming");
        System.out.println(res.getPassword());
    }
}

注意,由於我findUserByName()返回值是 User,當資料庫中存在多個名字相同的使用者的時候,呼叫此方法出錯。可以將該方法返回值更改為List<User> ,其餘不用更改。
 

B. 同時,我們也可以自定義MybatisConfig來配置Mybatis,自定義配置Mybatis有兩個必需的類:

DataSourceConfig.class:配置資料庫(這裡使用druid)

MybatisConfig.class:配置Mybatis的sqlSessionFactory等

1. pom.xml中所需依賴(主要是mybatis,mybatis-spring,和spring-boot-starter-jdbc以及Druid)

<?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.springboot.yanming</groupId>
	<artifactId>mybatisconfig</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>mybatisconfig</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.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</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>

		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.4.1</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>1.3.0</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.0.18</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
			<version>1.5.8.RELEASE</version>
		</dependency>

	</dependencies>

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


</project>

2. 在application.yml中 配置資料庫相關資訊:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/springboot
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    filters: stat
    maxActive: 20
    initialSize: 1
    maxWait: 60000
    minIdle: 1
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: select 'x'
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    maxOpenPreparedStatements: 20


3. DataSourceConfig.class

package com.springboot.yanming.mybatisconfig.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.sql.SQLException;

/**
 * Created by arnold.zhu on 6/13/2017.
 */
@Configuration
public class DataSourceConfig {


    @Value("${spring.datasource.url}")
    private String dbUrl;

    @Value("${spring.datasource.username}")
    private String username;

    @Value("${spring.datasource.password}")
    private String password;

    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;

    @Value("${spring.datasource.initialSize}")
    private int initialSize;

    @Value("${spring.datasource.minIdle}")
    private int minIdle;

    @Value("${spring.datasource.maxActive}")
    private int maxActive;

    @Value("${spring.datasource.maxWait}")
    private int maxWait;

    @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
    private int timeBetweenEvictionRunsMillis;

    @Value("${spring.datasource.minEvictableIdleTimeMillis}")
    private int minEvictableIdleTimeMillis;

    @Value("${spring.datasource.validationQuery}")
    private String validationQuery;

    @Value("${spring.datasource.testWhileIdle}")
    private boolean testWhileIdle;

    @Value("${spring.datasource.testOnBorrow}")
    private boolean testOnBorrow;

    @Value("${spring.datasource.testOnReturn}")
    private boolean testOnReturn;

    @Value("${spring.datasource.filters}")
    private String filters;


    @Bean
    public ServletRegistrationBean druidServlet() {
        ServletRegistrationBean reg = new ServletRegistrationBean();
        reg.setServlet(new StatViewServlet());
        reg.addUrlMappings("/druid/*");
        reg.addInitParameter("loginUsername", username);
        reg.addInitParameter("loginPassword", password);
        return reg;
    }

    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new WebStatFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        filterRegistrationBean.addInitParameter("profileEnable", "true");
        return filterRegistrationBean;
    }

    @Bean
    public DataSource druidDataSource() {
        DruidDataSource datasource = new DruidDataSource();
        datasource.setUrl(dbUrl);
        datasource.setUsername(username);
        datasource.setPassword(password);
        datasource.setDriverClassName(driverClassName);
        datasource.setInitialSize(initialSize);
        datasource.setMinIdle(minIdle);
        datasource.setMaxActive(maxActive);
        datasource.setMaxWait(maxWait);
        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        datasource.setValidationQuery(validationQuery);
        datasource.setTestWhileIdle(testWhileIdle);
        datasource.setTestOnBorrow(testOnBorrow);
        datasource.setTestOnReturn(testOnReturn);
        try {
            datasource.setFilters(filters);
        } catch (SQLException e) {
           e.printStackTrace();
        }
        return datasource;
    }

}


4. MybatisConfig.class(在類中配置了我們A方法中在application.yml中配置的資訊)

package com.springboot.yanming.mybatisconfig.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import javax.sql.DataSource;


/**
 * @Author: YanMing
 * @Description:
 * @Date: Created in 14:29 2017/11/27
 */
@Configuration
@EnableTransactionManagement
public class MyBatisConfig implements TransactionManagementConfigurer {

    @Autowired
    DataSource druidDataSource;

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactoryBean() {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(druidDataSource);
        bean.setTypeAliasesPackage("com.springboot.yanming.mybatisconfig.entity");

        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        try {
            bean.setMapperLocations(resolver.getResources("classpath:mapping/*Mapper.xml"));
            return bean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

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

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


後面關於UserMapper,XML檔案和DAO部分省略,和A方法中一模一樣

執行我們的測試用例:

package com.springboot.yanming.mybatisconfig.dao;

import com.springboot.yanming.mybatisconfig.MybatisconfigApplication;
import com.springboot.yanming.mybatisconfig.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

import static org.junit.Assert.*;

/**
 * @Author: YanMing
 * @Description:
 * @Date: Created in 19:06 2017/11/27
 */
@RunWith(SpringJUnit4ClassRunner.class)
//@WebAppConfiguration
@SpringBootTest(classes = MybatisconfigApplication.class)
@SpringBootConfiguration
public class UserDaoTest {
    @Autowired
    UserDao userDao;

    @Test
    public void testUserDao(){

        List<User> users = userDao.findUserByName("yanming");
        System.out.println(users.size()+" user named yanming");

    }
}

結果是並不能執行。因為在我們自定義配置Mybatis的時候還必須給Application指明Mapper所在的路徑。解決這個問題可以

1. 直接在Application上加註註解@MapperScan("com.springboot.yanming.mybatisconfig.mapper")

2. 配置MyBatisMapperScannerConfig,配置掃描Mapper所在的路徑

package com.springboot.yanming.mybatisconfig.config;

import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
//@AutoConfigureAfter(MyBatisConfig.class)
public class MapperScannerConfig {

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
        mapperScannerConfigurer.setBasePackage("com.springboot.yanming.mybatisconfig.mapper");
        return mapperScannerConfigurer;
    }

}

在我進行學習的時候,我發現很多的資料都提到由於mapperScannerConfigurer的執行時間比較早,如果早於MyBatisConfig,那麼sqlSessionFactory是無法注入的。但是我通過試驗發現並沒有報錯,所以將@AutoConfigureAfter(MyBatisConfig.class)註釋掉。
 

P.S.文章不妥之處還望指正