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>
依赖下载完之后, 在 YAML 文件, 也可以是 properties 文件里面配置连接数据库的相关配置.
- spring:
- datasource:
- url: jdbc:MySQL://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=GMT+8
- 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 是可以起到一个相同的作用. 根据包名就可以看到, 他们不是一个包里面的. 区别如下:
@Autowired 默认按类型装配, 默认情况下必须要求依赖对象必须存在, 如果要允许 null 值, 可以设置它的 required 属性为 false, 如:@Autowired(required=false) , 这个注解是属于 spring 的, 如果我们想使用名称装配可以结合 @Qualifier 注解进行使用.
@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>
- <!-- 查询单个 -->
- <select id="queryById" resultMap="StudentMap">
- select
- id, name, age
- from mybatis.student
- where id = #{id}
- </select>
- <!-- 查询指定行数据 -->
- <select id="queryAllByLimit" resultMap="StudentMap">
- select
- id, name, age
- from mybatis.student
- limit #{offset}, #{limit}
- </select>
- <!-- 通过实体作为筛选条件查询 -->
- <select id="queryAll" resultMap="StudentMap">
- select
- id, name, age
- from mybatis.student
- <where>
- <if test="id != null">
- and id = #{id}
- </if>
- <if test="name != null and name !=''">
- and name = #{name}
- </if>
- <if test="age != null">
- and age = #{age}
- </if>
- </where>
- </select>
- <!-- 新增所有列 -->
- <insert id="insert" keyProperty="id" useGeneratedKeys="true">
- insert into mybatis.student(name, age)
- values (#{name}, #{age})
- </insert>
- <!-- 通过主键修改数据 -->
- <update id="update">
- update mybatis.student
- <set>
- <if test="name != null and name !=''">
- name = #{name},
- </if>
- <if test="age != null">
- age = #{age},
- </if>
- </set>
- where id = #{id}
- </update>
- <!-- 通过主键删除 -->
- <delete id="deleteById">
- delete from mybatis.student where id = #{id}
- </delete>
- </mapper>
这里面对应了 SQL 的增删改查语句, 然后在 dao 层的方法, 对应了每一个 SQL 语句, 这里面 SQL 语句的 id, 对应 dao 层的每一个接口方法.
默认的配置是检测不到这个 xml 文件的, 然后我们需要做以下的配置.
我把 xml 文件放在 resources 文件夹下面的 dao 文件夹下面.
然后我们在 YAML 里面加上以下配置.
- 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 全栈学习笔记 , 给个关注, 精彩美文每天推送到你的手中!
来源: https://www.cnblogs.com/swzx-1213/p/12698222.html