SpringBoot 整合 MyBatis

MyBatis 简介

MyBatis 官网所述:

MyBatis 是一款优良的长久层框架,它反对自定义 SQL、存储过程以及高级映射。MyBatis 罢黜了简直所有的 JDBC 代码以及设置参数和获取后果集的工作。MyBatis 能够通过简略的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Ordinary Java Objects,简略 Java 对象)为数据库中的记录。

MyBatis 的劣势

MyBatis 作为一款优良的长久层框架,具备如下长处:

  1. 玲珑并且简略易学;
  2. 相比于 JDBC 缩小了大量冗余的代码;
  3. 将 SQL 语句与程序代码进行拆散,升高了耦合,便于管理;
  4. 提供 XML 标签,反对编写动静 SQL 语句;
  5. 提供映射标签,反对 Java 对象与数据库 ORM 字段的映射关系。

MyBatis 的毛病

  1. SQL 语句编写工作量较大,尤其是字段和关联表比拟多时。
  2. SQL 语句依赖于数据库,导致数据库移植性差,不能随便更换数据库。

MyBatis 实际

创立 SpringBoot 我的项目,整合 MyBatis,实现简略 CRUD。

1. 引入依赖

POM 文件如下:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>2.5.6</version>        <relativePath/> <!-- lookup parent from repository -->    </parent>    <groupId>com.example</groupId>    <artifactId>springboot-mybatis</artifactId>    <version>0.0.1-SNAPSHOT</version>    <name>springboot-mybatis</name>    <description>Demo project for Spring Boot</description>    <properties>        <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.mybatis.spring.boot</groupId>            <artifactId>mybatis-spring-boot-starter</artifactId>            <version>2.2.0</version>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-jdbc</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-devtools</artifactId>            <scope>runtime</scope>            <optional>true</optional>        </dependency>        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>            <optional>true</optional>        </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>                <configuration>                    <excludes>                        <exclude>                            <groupId>org.projectlombok</groupId>                            <artifactId>lombok</artifactId>                        </exclude>                    </excludes>                </configuration>            </plugin>        </plugins>    </build></project>

2. 配置 MySQL 和 MyBatis

application.yml 配置文件如下:

# 配置MySQLspring:  datasource:    url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC    username: root    password: 123456    driver-class-name: com.mysql.cj.jdbc.Driver# 配置MyBatismybatis:  mapper-locations: classpath:mapper/*  type-aliases-package: com.example.entity  configuration:    map-underscore-to-camel-case: true

上述配置文件中,mapper-locations 用来指定 mapper 文件的门路;type-aliases-package 用来设置别名,它的作用是让 MyBatis 扫描咱们自定义的实体类,即 'com.example.entity' 包下的所有类,这些类都会被注册到 TYPE_ALIASES 容器,这样在 mapper 文件中能够间接应用非限定类名来作为它的别名,如 'com.example.entity.User' 可应用别名 'User' 代替;map-underscore-to-camel-case 为 true 时示意开启驼峰命名主动映射,如将 user_name 映射为 userName。

3. 编写实体类

本文以 User 类为例:

package com.example.entity;import lombok.Data;import java.util.Date;/** * @Author john * @Date 2021/11/14 */@Datapublic class User {    private long id;    private String userName;    private int age;    private String address;    private Date createTime;    private Date updateTime;}

User 类中封装了用户的 id、姓名、年龄、地址、创立工夫以及批改工夫等信息。

4. 编写 Mapper 类和 mapper 文件

首先编写 UserMapper 接口:

package com.example.mapper;import com.example.entity.User;/** * @Author john * @Date 2021/11/16 */public interface UserMapper {        void insertUser(User user);        User findUserById(long id);}

接口中定义了两个办法,insertUser 用来向数据表中插入一条记录;findUserById 用来通过 id 查问 User。

上述操作实现后,咱们在 resources 文件夹中创立 mapper/user-mapper.xml 文件(文件门路在上述配置文件 application.yml 中设置)。mapper 文件的内容如下:

<?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.example.mapper.UserMapper">    <sql id="insertFields">        user_name, age, address, gmt_create, gmt_modified    </sql>    <sql id="selectFields">        id, user_name, age, address, gmt_create, gmt_modified    </sql>    <resultMap id="UserMap" type="User">        <result column="id" jdbcType="INTEGER" property="id"/>        <result column="user_name" jdbcType="VARCHAR" property="userName"/>        <result column="age" jdbcType="INTEGER" property="age"/>        <result column="address" jdbcType="VARCHAR" property="address"/>        <result column="gmt_create" jdbcType="DATE" property="createTime" />        <result column="gmt_modified" jdbcType="DATE" property="updateTime" />    </resultMap>    <select id="findUserById" parameterType="Long" resultMap="UserMap">        select        <include refid="selectFields"></include>        from user        where id = #{id}    </select>    <insert id="insertUser" parameterType="User" keyProperty="id">        insert into user (<include refid="insertFields"></include>)        values(#{userName}, #{age}, #{address}, UTC_TIMESTAMP(), UTC_TIMESTAMP())    </insert></mapper>

mapper 文件须要与 Mapper 接口对应:Mapper 接口的全限定名,对应 mapper 文件的 namespace 值;接口的办法名,对应 mapper 文件 Statement 的 id 值;办法的参数,就是传递给 SQL 的参数;办法的返回值就是 SQL 返回的数据。

parameterType 和 resultType(本试验中未应用)别离用来指定参数和返回值的类型,因为咱们在配置文件中启用了别名,所以 parameterType 和 resultType 能够间接设置为 'User' 或 'user',而非全限定名 'com.example.entity.User'。对于别名的设置还有其余几种办法,这个咱们在后文探讨。

MyBatis中别名不辨别大小写

mapper 文件中还有一个要害标签 resultMap,resultMap 次要用来创立自定义的映射关系。以查问为例,MyBatis 的映射规定是:依据查问到的列名找到 POJO 对应的属性名,而后调用该属性的 setter 办法进行赋值。本试验中,user 表的 id 会主动映射为 User 对象的 id,因为字段名和属性名是完全一致的;user 表的 user_name 也会主动映射为 User 对象的 userName,因为咱们开启了驼峰式命名映射。然而 gmt_create 不会映射到 createTime,因为字段名和属性名既不完全一致,也不合乎驼峰式命名映射的规定。所以这里咱们应用 resultMap 来创立新的映射关系,将 gmt_create 和 gmt_modified 别离映射到 createTime 和 updateTime,而后再将 resultType 替换为 resultMap(如果不须要自定义映射,本试验中的 resultMap='UserMap' 可替换为 resultType='User')。resultMap 还有一些其余性能,对于 resultMap 的应用办法能够参考我的另一篇博客

另外,本试验中,resultMap 标签也能够定义为:

<resultMap id="UserMap" type="User">    <result column="gmt_create" jdbcType="DATE" property="createTime" />    <result column="gmt_modified" jdbcType="DATE" property="updateTime" /></resultMap>

因为其余字段会主动映射,不须要额定书写。

TODO

5. 编写 Service

创立 UserService:

package com.example.service;import com.example.entity.User;import com.example.mapper.UserMapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;/** * @Author john * @Date 2021/11/16 */@Servicepublic class UserService {    @Autowired    private UserMapper userMapper;    public void insertUser(User user) {        userMapper.insertUser(user);    }    public User findUserById(long id) {        return userMapper.findUserById(id);    }}

在 UserService 中注入 UserMapper 对象,并调用 UserMapper 的 insertUser()/findUserById() 办法来增加/查问 User。

为了可能失常注入 UserMapper 对象,咱们还须要再启动类上增加 @MapperScan 注解,用来指定 Mapper 接口的门路:

package com.example;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication@MapperScan("com.example.mapper")public class SpringbootMybatisApplication {    public static void main(String[] args) {        SpringApplication.run(SpringbootMybatisApplication.class, args);    }}

上述代码中咱们指定 Mapper 接口的门路为 'com.example.mapper' 包,该包下的所有 Mapper 接口都会被扫描。

除了在启动类上增加 @MapperScan 注解外,还能够在 Mapper 接口上间接增加 @Mapper 注解,这种办法绝对比拟麻烦,因为理论中咱们可能会有多个 Mapper 接口,这样就须要增加多个注解。

6. 创立 user 表

user 表字段设计如下(设置 id 自增):

7. 测试

编写测试接口:

package com.example;import com.example.entity.User;import com.example.service.UserService;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;@SpringBootTestclass SpringbootMybatisApplicationTests {    @Autowired    private UserService service;    @Test    public void addUser(){        User user = new User();        user.setUserName("John");        user.setAge(24);        user.setAddress("BUPT");        service.insertUser(user);    }    @Test    public void findUser(){        System.out.println(service.findUserById(1));    }}

首先执行 addUser() 办法,执行胜利后查问数据表,失去如下信息:

而后执行 findUser() 办法,执行后果如下:

至此,SpringBoot 整合 MyBatis 测试胜利!

8. MyBatis 设置别名的其余形式

形式一:在配置文件 application.yml 中增加配置。

map-underscore-to-camel-case: true

本试验中就应用此种形式(简略不便),默认状况下实体类的别名为其类名(严格来说是首字母小写的非限定类名,但别名不辨别大小写,所以 User、user、uSer 的成果都是雷同的),能够自定义别名吗,比方设置别名为 'hello'?当然能够,形式二介绍结束后咱们再探讨。

形式二:应用 MyBatis 的配置文件 filename.xml。

首先在 yml 文件中设置 MyBatis 配置文件的门路:

# 配置MyBatismybatis:  mapper-locations: classpath:mapper/*  type-aliases-package: com.example.entity  config-location: classpath:mybatis/mybatis-config.xml #MyBatis配置文件

而后在 resource 文件夹下创立 MyBatis 的配置文件 mapper/mybatis-config.xml(门路和文件名在 config-location 中设置),配置文件内容如下:

<?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="mapUnderscoreToCamelCase" value="true"/>    </settings>    <typeAliases>        <package name="com.example.entity"/>    </typeAliases></configuration>

setting 标签中能够设置开启驼峰命名映射,其成果与在 yml 文件中配置 map-underscore-to-camel-case: true 是雷同的。

typeAliases 标签中,设置 package 能够让 MyBatis 扫描指定包下的实体类,其成果与在 yml 文件中配置 type-aliases-package: com.example.entity 是雷同的。

形式一和形式二实现成果是雷同的,本试验中实体类 'com.example.entity.User' 可应用其别名 'User' 或 'user' 代替,如果心愿应用其余别名,能够搭配 @Alias 注解一起应用,即在实体类上增加该注解:

package com.example.entity;import lombok.Data;import org.apache.ibatis.type.Alias;import java.util.Date;/** * @Author john * @Date 2021/11/14 */@Data@Alias("hello")public class User {    private long id;    private String userName;    private int age;    private String address;    private Date createTime;    private Date updateTime;}

这里咱们设置 User 类的别名为 'hello',这样在 user-mapper.xml 文件中,将 type、parameterType 以及 resultType 替换为 'hello',程序仍可失常运行,但此时如果持续应用 'User' 或者 'user',则会报别名解析谬误,因为 @Alias 注解会使默认的别名变得有效(形式一和形式二都可搭配 @Alias 注解一起应用)。

形式三:在 MyBatis 的配置文件中自定义别名。

与形式二类似,只不过这次咱们不应用 package 标签,而是应用 typeAlias 标签:

<?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="mapUnderscoreToCamelCase" value="true"/>    </settings>    <typeAliases>        <typeAlias type="com.example.entity.User" alias="User"/><!--        <package name="com.example.entity"/>-->    </typeAliases></configuration>

应用 typeAlias 标签能够帮忙咱们 DIY 别名,且不须要应用注解 @Alias。应用此种形式定义别名时,如果有多个实体类,那么就须要为每个类都设置一个别名,较为繁琐。

值得注意的是,如果咱们曾经实现了形式一或形式二,那么咱们仍能够应用形式三增加别名,也就是说如果没有配置 @Alias 注解,那么 User 类的别名能够是默认的 'User' 或形式三 DIY 的别名;如果配置了 @Alias 注解,那么 User 类的别名能够是 @Alias 指定的别名或形式三 DIY 的别名。

欢送批评指正!!!