SpringBoot系列(五)Mybatis整合完整详细版

SpringBoot 系列(五)Mybatis 整合

1. Mybatis 简介

 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object, 普通的 Java 对象) 映射成数据库中的记录。

 换句话说,我觉得利用 mybatis 整合持久层要方便很多,比起以前编写 jdbc 代码操作数据库的一些连接,简直不要太爽。

2. 项目创建

 创建一个简单的具有 start-web 依赖的 SpringBoot 项目,然后添加 mybatis 相关的依赖。

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

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

 依赖下载完之后,在 yml 文件,也可以是 properties 文件里面配置连接数据库的相关配置。

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=GMT%2B8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

 然后我们在数据库 mybatis 下面创建一个 student 表

CREATE TABLE `student`(
  `id` int(10) NOT NULL AUTO_INCREMENT COMMENT '唯一标识 id',
  `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '姓名',
  `age` int(3) NOT NULL COMMENT '年龄',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

完成项目初始配置。

3. entity 实体类代码

/**
 * (Student) 实体类
 *
 * @author 全栈学习笔记
 * @since 2020-04-14 11:39:10
 */
public class Student  {
    private static final long serialVersionUID = -91969758749726312L;
    /**
    * 唯一标识 id
    */
    private Integer id;
    /**
    * 姓名
    */
    private String name;
    /**
    * 年龄
    */
    private Integer age;
}

以上省略了 get,以及 set 方法。

4. dao 层代码

package com.example.demomybatis.dao;

import com.example.demomybatis.entity.Student;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;

/**

  • (Student) 表数据库访问层

  • @author 全栈学习笔记

  • @since 2020-04-14 11:39:18
    */
    @Mapper
    @Repository
    public interface StudentDao {

    /**

    • 通过 ID 查询单条数据
    • @param id 主键
    • @return 实例对象
      */
      Student queryById(Integer id);

    /**

    • 查询指定行数据
    • @param offset 查询起始位置
    • @param limit 查询条数
    • @return 对象列表
      */
      List<Student> queryAllByLimit(@Param("offset") int offset, @Param("limit") int limit);

    /**

    • 通过实体作为筛选条件查询
    • @param student 实例对象
    • @return 对象列表
      */
      List<Student> queryAll(Student student);

    /**

    • 新增数据
    • @param student 实例对象
    • @return 影响行数
      */
      int insert(Student student);

    /**

    • 修改数据
    • @param student 实例对象
    • @return 影响行数
      */
      int update(Student student);

    /**

    • 通过主键删除数据
    • @param id 主键
    • @return 影响行数
      */
      int deleteById(Integer id);

}


代码说明:dao 层属于数据访问层,与 mybatis 的 xml 文件相互映射,实现 SQL 语句的功能。

注解说明:在dao层的类需要加上 @Mapper的注解,这个注解是mybatis提供的,标识这个类是一个数据访问层的 bean,并交给 spring 容器管理。并且可以省去之前的 xml 映射文件。在编译的时候,添加了这个类也会相应的生成这个类的实现类。

 如果你是用的 idea,在serviceImpl中使用 @Autowired注入 bean 的时候,idea 会报错,但是不影响运行,报错是因为 @mapper不是 spring 提供的,当需要自动注入这个 bean 的时候 idea 不能 预检测到这个 bean 是否可以注入到容器中,不知道新版的 idea 会不会有这种问题。如果想消除这个报错,你可以在 dao 层的类上面加上一个 @Repository,这个注解是 spring 提供的,这样就可以预检测到 mapper 的 bean 是可以注册到 spring 容器里面的。

 你会发现在代码中,有的接口的参数是带了 @Param这个注解的,有的参数是没有这个注解的。如果你只有一个参数,这个注解可要可不要。当你有两个及其以上的注解时,你就需要用这个注解了,不然在对应的 xml 文件,它分辨不出来这个参数是哪一个就会报错,用这个注解的意思就是说标识这个参数的名称,以便让接受参数的一方更好的找到并利用这个值。

5. service 层代码

package com.example.demomybatis.service;

import com.example.demomybatis.entity.Student;
import java.util.List;

/**

  • (Student) 表服务接口

  • @author 全栈学习笔记

  • @since 2020-04-14 11:39:19
    */
    public interface StudentService {

    /**

    • 通过 ID 查询单条数据
    • @param id 主键
    • @return 实例对象
      */
      Student queryById(Integer id);

    /**

    • 查询多条数据
    • @param offset 查询起始位置
    • @param limit 查询条数
    • @return 对象列表
      */
      List<Student> queryAllByLimit(int offset, int limit);

    /**

    • 新增数据
    • @param student 实例对象
    • @return 实例对象
      */
      Student insert(Student student);

    /**

    • 修改数据
    • @param student 实例对象
    • @return 实例对象
      */
      Student update(Student student);

    /**

    • 通过主键删除数据
    • @param id 主键
    • @return 是否成功
      */
      boolean deleteById(Integer id);

}

 代码说明:这是服务层的接口,serviceImpl 对应服务层接口的实现。

6. serviceImpl 层代码

package com.example.demomybatis.service.impl;

import com.example.demomybatis.entity.Student;
import com.example.demomybatis.dao.StudentDao;
import com.example.demomybatis.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/**

  • (Student) 表服务实现类

  • @author 全栈学习笔记

  • @since 2020-04-14 11:39:19
    */
    @Service("studentService")
    public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentDao studentDao;

    /**

    • 通过 ID 查询单条数据
    • @param id 主键
    • @return 实例对象
      */
      @Override
      public Student queryById(Integer id) {
      return this.studentDao.queryById(id);
      }

    /**

    • 查询多条数据
    • @param offset 查询起始位置
    • @param limit 查询条数
    • @return 对象列表
      */
      @Override
      public List<Student> queryAllByLimit(int offset, int limit) {
      return this.studentDao.queryAllByLimit(offset, limit);
      }

    /**

    • 新增数据
    • @param student 实例对象
    • @return 实例对象
      */
      @Override
      public Student insert(Student student) {
      this.studentDao.insert(student);
      return student;
      }

    /**

    • 修改数据
    • @param student 实例对象
    • @return 实例对象
      */
      @Override
      public Student update(Student student) {
      this.studentDao.update(student);
      return this.queryById(student.getId());
      }

    /**

    • 通过主键删除数据
    • @param id 主键
    • @return 是否成功
      */
      @Override
      public boolean deleteById(Integer id) {
      return this.studentDao.deleteById(id) > 0;
      }
      }

 代码说明:@Service标识这个 bean 是 service 层的,也就是服务层,并交给 spring 容器管理。参数的 value 属性是这个 bean 的名称,也可以不写,默认为类名。

 这里我们可以说一下,@Resource@Autowired, 前面我们在serviceImpl里面需要用到dao层的方法的时候,不是直接 new 一个对象,在哪需要就在哪 new,而是利用注解,实现自定注入装配,利用 spring 容器管理这些 bean,这样写出来的代码是松耦合的,类之间的耦合度更低,维护性就相对提高了。
@Resource@Autowired是可以起到一个相同的作用。根据包名就可以看到,他们不是一个包里面的。区别如下:

  1. @Autowired默认按类型装配, 默认情况下必须要求依赖对象必须存在,如果要允许 null 值,可以设置它的 required 属性为 false,如:@Autowired(required=false) ,这个注解是属于spring的,如果我们想使用名称装配可以结合 @Qualifier 注解进行使用。
  2. @Resource默认按照名称进行装配,名称可以通过 name 属性进行指定,如果没有指定 name 属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在 setter 方法上默认取属性名进行装配。当找不到与名称匹配的 bean 时才按照类型进行装配。但是需要注意的是,如果 name 属性一旦指定,就只会按照名称进行装配。这个注解属于 J2EE 的

7. mapper 层代码

 所谓的 mapper 层,就是 xml 文件,与 dao 层对应的。

<?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.demomybatis.dao.StudentDao">
    <resultMap type="com.example.demomybatis.entity.Student" id="StudentMap">
        <result property="id" column="id" jdbcType="INTEGER"/>
        <result property="name" column="name" jdbcType="VARCHAR"/>
        <result property="age" column="age" jdbcType="INTEGER"/>
    </resultMap>
&lt;!--查询单个--&gt;
&lt;select id=<span class="hljs-string">"queryById"</span> resultMap=<span class="hljs-string">"StudentMap"</span>&gt;
    select
      id, name, age
    from mybatis.student
    <span class="hljs-type">where</span> <span class="hljs-variable">id</span> <span class="hljs-operator">=</span> #{id}
&lt;/select&gt;

&lt;!--查询指定行数据--&gt;
&lt;select id=<span class="hljs-string">"queryAllByLimit"</span> resultMap=<span class="hljs-string">"StudentMap"</span>&gt;
    select
      id, name, age
    from mybatis.student
    limit #{offset}, #{limit}
&lt;/select&gt;

&lt;!--通过实体作为筛选条件查询--&gt;
&lt;select id=<span class="hljs-string">"queryAll"</span> resultMap=<span class="hljs-string">"StudentMap"</span>&gt;
    select
      id, name, age
    from mybatis.student
    &lt;where&gt;
        &lt;<span class="hljs-keyword">if</span> test=<span class="hljs-string">"id != null"</span>&gt;
            <span class="hljs-type">and</span> <span class="hljs-variable">id</span> <span class="hljs-operator">=</span> #{id}
        &lt;/<span class="hljs-keyword">if</span>&gt;
        &lt;<span class="hljs-keyword">if</span> test=<span class="hljs-string">"name != null and name != ''"</span>&gt;
            <span class="hljs-type">and</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> #{name}
        &lt;/<span class="hljs-keyword">if</span>&gt;
        &lt;<span class="hljs-keyword">if</span> test=<span class="hljs-string">"age != null"</span>&gt;
            <span class="hljs-type">and</span> <span class="hljs-variable">age</span> <span class="hljs-operator">=</span> #{age}
        &lt;/<span class="hljs-keyword">if</span>&gt;
    &lt;/where&gt;
&lt;/select&gt;

&lt;!--新增所有列--&gt;
&lt;insert id=<span class="hljs-string">"insert"</span> keyProperty=<span class="hljs-string">"id"</span> useGeneratedKeys=<span class="hljs-string">"true"</span>&gt;
    insert into mybatis.student(name, age)
    values (#{name}, #{age})
&lt;/insert&gt;

&lt;!--通过主键修改数据--&gt;
&lt;update id=<span class="hljs-string">"update"</span>&gt;
    update mybatis.student
    &lt;set&gt;
        &lt;<span class="hljs-keyword">if</span> test=<span class="hljs-string">"name != null and name != ''"</span>&gt;
            name = #{name},
        &lt;/<span class="hljs-keyword">if</span>&gt;
        &lt;<span class="hljs-keyword">if</span> test=<span class="hljs-string">"age != null"</span>&gt;
            age = #{age},
        &lt;/<span class="hljs-keyword">if</span>&gt;
    &lt;/set&gt;
    <span class="hljs-type">where</span> <span class="hljs-variable">id</span> <span class="hljs-operator">=</span> #{id}
&lt;/update&gt;

&lt;!--通过主键删除--&gt;
&lt;delete id=<span class="hljs-string">"deleteById"</span>&gt;
    delete from mybatis.student <span class="hljs-type">where</span> <span class="hljs-variable">id</span> <span class="hljs-operator">=</span> #{id}
&lt;/delete&gt;

</mapper>

 这里面对应了 SQL 的增删改查语句,然后在 dao 层的方法,对应了每一个 SQL 语句,这里面 SQL 语句的 id,对应 dao 层的每一个接口方法。
默认的配置是检测不到这个 xml 文件的,然后我们需要做以下的配置。
我把 xml 文件放在resources 文件夹下面的 dao文件夹下面。
然后我们在 yml 里面加上以下配置。

mybatis:
  type-aliases-package: com.example.demomybatis.entity
  mapper-locations: classpath:dao/*Mapper.xml

8. controller 层代码

 controller 层的代码我们是用来测试的,一般也是返回数据给前端的地方。

package com.example.demomybatis.controller;

import com.example.demomybatis.entity.Student;
import com.example.demomybatis.service.StudentService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

/**

  • (Student) 表控制层

  • @author 全栈学习笔记

  • @since 2020-04-14 11:39:20
    /
    @RestController
    @RequestMapping("student")
    public class StudentController {
    /
    *

    • 服务对象
      */
      @Resource
      private StudentService studentService;

    /**

    • 通过主键查询单条数据
    • @param id 主键
    • @return 单条数据
      */
      @GetMapping("selectOne")
      public Student selectOne(Integer id) {
      return this.studentService.queryById(id);
      }

}

 代码说明:@RestController 这个注解等效于 @Controller加上 @ResponseBody, 添加了这个注解就是让这个类返回 json 串,这是 spring 内部提供的 json 解析。@RequesMapping 注解是一个地址映射的注解。就是根据这个地址,可以找到这个方法,这个类,注解到类上,就相当于方法的父类地址。

 测试一下。我们先在数据库里面添加一条数据。

insert into student(id,name,age) VALUES(2,'全栈学习笔记',22)

 然后在浏览器输入:localhost:8088/student/selectOne?id=2 就可以看到我们拿到的数据了。端口配置根据自己项目而定。

 好了,这一期的 mybatis 整合就到这里,有兴趣的可以 wx search 全栈学习笔记 ,给个关注,精彩美文每天推送到你的手中!