本文导读
- 本文承接《Spring Boot 自定义数据源 DruidDataSource》
- 本文将新建一个 Web 项目,介绍 Spring Boot 整合 MyBatis 进行项目开发
环境准备
新建项目
pom. xml 默认结构
<?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>www.wmx.com</groupId>
<artifactId>hippo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>hippo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.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>
<!-- 引入 Spring Boot 封装的 jdbc,它会提供默认的数据源以及 JdbcTemplate -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 引入web项目html页面的模板引擎 thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 引入的是 web 项目的启动器,web 项目必备-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入 myBatis,这是 MyBatis官方提供的适配 Spring Boot 的,而不是Spring Boot自己的-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 引入 Mysql 数据库连接驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 引入 Spring Boot 官方测试模块-->
<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>
Mysql 数据库
- 这是本地的 Mysql 数据库,新建数据库 hippo,建表 user,默认内容如下,初始化一些数据:
全局配置文件
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/hippo?characterEncoding=UTF-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
- 因为 Spring Boot 已经默认配置好了数据源,所以此时就可以测试一下获取数据源
package com.lct.wmx;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@RunWith(SpringRunner.class)
@SpringBootTest
public class HippoApplicationTests {
/**
* Spring Boot 默认已经配置好了数据源,程序员可以直接 DI 注入然后使用即可
* 数据源获取正常,能正常拿到数据库连接,则说明数据库连接成功
*/
@Resource
DataSource dataSource;
@Test
public void contextLoads() throws SQLException {
System.out.println("数据源>>>>>>" + dataSource.getClass());
Connection connection = dataSource.getConnection();
System.out.println("连接>>>>>>>>>" + connection);
System.out.println("连接地址>>>>>" + connection.getMetaData().getURL());
connection.close();
}
}
数据源>>>>>>class com.zaxxer.hikari.HikariDataSource
2018-08-22 15:44:38.520 INFO 11944 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2018-08-22 15:44:39.246 INFO 11944 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
连接>>>>>>>>>HikariProxyConnection@2125903542 wrapping com.mysql.jdbc.JDBC4Connection@7ed9499e
连接地址>>>>>jdbc:mysql://localhost:3306/hippo?characterEncoding=UTF-8
- 注意本文的重点是介绍如何整合 MyBatis ,所以想要使用 DruidDataSource 数据源的,可以参考《Spring Boot 自定义数据源 DruidDataSource》,本文就使用默认的数据源 HikariDataSource
Domain
- 创建 User 的 Java Bean 实体来封装数据,到此环境准备完毕,之后就使用 MyBatis 操作数据库即可
package com.lct.wmx.domain;
import java.util.Date;
/**
* Created by Administrator on 2018/8/22 0022.
* 用户实体类
*/
public class User {
private Integer uid;
private String name;
private Integer age;
private Date birthday;
private Float salary;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getSalary() {
return salary;
}
public void setSalary(Float salary) {
this.salary = salary;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", uid=" + uid +
", name='" + name + '\'' +
", birthday=" + birthday +
", salary=" + salary +
'}';
}
}
myBatis 注解版 CRUD
Mapper
- User 对应的 MyBatis 的操作数据库的 Mapper,具体作用以及写在注释上了
package com.lct.wmx.mapper;
import com.lct.wmx.domain.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
/**
* Created by Administrator on 2018/8/22 0022.
* User 对应的 MyBatis 的操作数据库的 Mapper
*
* @Mapper : 表示本类是一个 MyBatis 的 Mapper,等价于以前 Spring 整合 MyBatis 时的 Mapper 接口
* <p/>
* Spring 整合 mybatis 时,一个 POJO 对应 MyBatis一个操作数据库的xml文件,每个xml文件又指向一个 mapper 接口
* Spring Boot 则省略了 xml 文件这一环节,直接将以前xml文件中的sql写在了本类接口上
* 以前在xml文件中写sql,现在使用注解直接写在接口上,所有sql注解都在:org.apache.ibatis.annotations 包中
*/
@Mapper
public interface UserMapper {
/**
* 根据用户 id 查询
*
* @param uid
* @return :返回查询结果,不存在时返回 null
* @Select :等价于以前 xml 形式时的 <select 标签,sql写法仍然和以前一样
*/
@Select(value = {"select * from user where uid=#{uid}"})
public User findUserById(Integer uid);
/**
* 查询所有用户
*
* @return :返回查询结果
* @Select :等价于以前 xml 形式时的 <select 标签,sql写法仍然和以前一样
*/
@Select(value = {"select * from user"})
public List<User> findAllUsers();
/**
* 根据用户id删除用户
*
* @return :返回操作的行数,也可以不返回
*/
@Delete("delete from user where uid = #{uid}")
public Integer deleteUserById(Integer uid);
/**
* 添加用户
*
* @param user :因为主键 uid 自增,所以没设值
* @return
*/
@Insert("insert into user(name,age,birthday,salary) values(#{name},#{age},#{birthday},#{salary})")
public Integer adduser(User user);
/**
* 根据用户 uid 修改用户
*
* @param user
* @return
*/
@Update("update user set name=#{name},age=#{age},birthday=#{birthday},salary=#{salary} where uid=#{uid}")
public Integer updateUser(User user);
}
UserController
- 用于浏览器请求后台,在注释中已经详细说明
package com.lct.wmx.controller;
import com.lct.wmx.domain.User;
import com.lct.wmx.mapper.UserMapper;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.util.List;
/**
* Created by Administrator on 2018/8/22 0022.
* 用户控制层
* 因为主要是讲解 myBatis 操作数据库,所以省略 service 层与 dao 层
*/
@Controller
public class UserController {
@Resource
private UserMapper userMapper;
/**
* 根据用户 id 查询----直接将结果返回给用户页面
*
* @param uid
* @return
*/
@ResponseBody
@GetMapping("/user/{uid}")
public User findUserById(@PathVariable("uid") Integer uid) {
System.out.println("------------findUserById >>uid>" + uid);
User user = userMapper.findUserById(uid);
System.out.println("------------findUserById >>user>" + user);
return user;
}
/**
* 查询所有用户----直接将结果返回给用户页面
*
* @return
*/
@ResponseBody
@GetMapping("/user")
public List<User> findAllUsers() {
System.out.println("------------findAllUsers >>>>>>");
List<User> userList = userMapper.findAllUsers();
System.out.println("------------findAllUsers >>>>userList>>" + userList);
return userList;
}
/**
* 添加用户--直接采用浏览器地址带参数的方式进行添加
* localhost:8080/user/add?name=令狐冲&age=33&birthday=1897/08/25&salary=7000
* 添加之后,重定向到查询所有
*
* @param user
* @return
*/
@GetMapping("/user/add")
public String addUser(User user) {
System.out.println("------------addUser >>>>user>>" + user);
userMapper.adduser(user);
return "redirect:/user";
}
/**
* 修改用户----localhost:8080/user/update?uid=1&name=令狐冲&age=33&birthday=1897/08/25&salary=7000
*
* @param user
* @return
*/
@GetMapping("/user/update")
public String updateUser(User user) {
System.out.println("------------updateUser >>>>user>>" + user);
userMapper.updateUser(user);
return "redirect:/user";
}
/**
* 删除用户
*
* @param uid
* @return
*/
@GetMapping("/user/del/{uid}")
public String deleteUser(@PathVariable("uid") Integer uid) {
System.out.println("------------deleteUser >>>>uid>>" + uid);
userMapper.deleteUserById(uid);
return "redirect:/user";
}
}
CRUD 测试
- 查询测试
- 删除测试
- 添加测试:
- 修改测试:
MyBatis 自动配置原理
- MyBatis 自动配置在 org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration 类中
- 它会自动注入 数据源(DataSource) 创建 MyBatis 的 SqlSessionFactory,无需程序员插手
@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()));
}
org.apache.ibatis.session.Configuration configuration = this.properties.getConfiguration();
if(configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
configuration = new org.apache.ibatis.session.Configuration();
}
if(configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
Iterator var4 = this.configurationCustomizers.iterator();
while(var4.hasNext()) {
ConfigurationCustomizer customizer = (ConfigurationCustomizer)var4.next();
customizer.customize(configuration);
}
}
factory.setConfiguration(configuration);
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(StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if(!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
}
return factory.getObject();
}
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
return executorType != null?new SqlSessionTemplate(sqlSessionFactory, executorType):new SqlSessionTemplate(sqlSessionFactory);
}
自定义 MyBatis 配置
- 以前使用 MyBatis 时,因为有 xml 文件,所以可以直接在文件中配置,但是 Spring Boot 内置的 myBatis 注解版时,需要程序员自己来配置组件
- 上面的 org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration 类中创建 SqlSessionFactory 的时候使用了一个 ConfigurationCustomizers(配置定制器) ,我们自己如果想要修改 myBatis 配置时,只需要自己创建此组件然后添加到容器中即可
- 下面以一个需求为例进行说明,修改数据库的 uid 字段名称为 "u_id",然后 UserMapper 中根据 id 查询的 sql 语句中也对应改为 u_id,其余所有内容不做改变。
/**
* 根据用户 id 查询
*
* @param uid
* @return :返回查询结果,不存在时返回 null
* @Select :等价于以前 xml 形式时的 <select 标签,sql写法仍然和以前一样
*/
@Select(value = {"select * from user where u_id=#{uid}"})
public User findUserById(Integer uid);
- select * from user where u_id=#{uid} : "=" 号左边的 u_id 对应数据库字段,右边的 uid 对应 User 实体的属性,此时浏览器发送查询请求时结果如下:
- 可以看到 uid 字段 封装结果失败!如果是以前是可以在配置文件中指定 myBatis 的驼峰命名的,现在注解版则使用 ConfigurationCustomizers 配置即可
- ConfigurationCustomizers 配置定制器中还有其它许多配置项,如下所示只是其中一个
package com.lct.wmx.config;
import org.apache.ibatis.session.Configuration;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.springframework.context.annotation.Bean;
/**
* Created by Administrator on 2018/8/22 0022.
* MyBatis 配置类
*/
@org.springframework.context.annotation.Configuration
public class MyBatisConfig {
/**
* 将组建添加到容器中
*
* @return
*/
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return new ConfigurationCustomizer() {
@Override
public void customize(Configuration configuration) {
/**
* 设置驼峰命名规则
* 即数据库的字段,如 aBc 中间要是存在"_" 时,则会自动转换匹配
*/
configuration.setMapUnderscoreToCamelCase(true);
}
};
}
}
@MapperScan 批量扫描
- 一个 POJO 对应一个 Mapper,实际项目中如果有许多的 POJO 时,同理就会有许多的 Mapper,方法一是每个 Mapper 上都添加 @Mapper 注解
- 方法二是具体的 Mapper 上面可以不写 @Mapper ,使用 @MapperScan 注解来自动扫描
- @MapperScan 的位置通常放在应用启动类上,value是一个数组,可以扫描多个路径
package com.lct.wmx;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @MapperScan : 作用是扫描某些包下面所有的类作为 Mapper 类
* 1)value是一个数组,可以扫描多个路径
* 2)被扫描的包(com.lct.wmx.mapper)中的类可以不用再写@Mapper,因为全部会自动为它们添上
* 3) @MapperScan的位置通常放在应用启动类上
*/
@MapperScan(value = {"com.lct.wmx.mapper"})
@SpringBootApplication
public class HippoApplication {
public static void main(String[] args) {
SpringApplication.run(HippoApplication.class, args);
}
}