SpringBoot2.X实战SQL数据库(MyBatis)
源代码仓库:https://github.com/zhshuixian/learn-spring-boot-2
在上一小节《实战 SQL 数据库(Spring Data JPA)》中,主要介绍了 Spring Data JPA 如何连接数据库,实现数据的增删改查等操作。这一小节,将实战 Spring Boot 整合 MyBatis,另一个常用的 Java 持久层框架。
考虑到 MyBatis 应用比较广泛,这里将会使用 MyBatis 作为主要的 Java 持久层框架,对 MyBatis Plus 感兴趣的读者可以对照本小节内容,参考其官网 https://mybatis.plus/ 和其 Spring Boot 启动器 mybatis-plus-boot-starter。
这一小节将以用户信息表的为例子实战 MyBatis 连接 SQL 数据库并读写数据,主要分为如下几个部分:
- MyBatis 的依赖引入
- MyBatis 连接 MySQL
- MyBatis 实体类
- MyBatis 写入、更新、删除、查询数据
- MyBatis 多笔查询、分页查询
- MyBatis 使用 Mapper.xml 方式
这里使用 MySQL,如果你想使用如:PostgreSQL 等其他的数据库,只需要更改相对应的依赖和指定 Driver 驱动包即可。这里需要你提前安装好 MySQL 或其他 SQL 数据库。
参考文章在 Linux 下安装 MySQL 8 : https://blog.csdn.net/u010974701/article/details/85625228
安装完成后运行如下命令:
create database spring;
1、什么是 MyBatis
MyBatis 官网:https://mybatis.org/mybatis-3/zh/index.html
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
上一小节讲到,Spring Data JPA 只需要按照规则编写接口的方法名,就可以直接进行相应的 SQL 操作,代码一目了然,开发者几乎不需要跳到 Repository 接口去了解此方法的用途。
相对于 Spring Data JPA 差不多可以不用写 SQL 的框架而言,MyBatis 在于开发者可以灵活的编写 SQL,但带来的麻烦就是项目中一堆 Mapper.xml 等一堆配置文件。即使通过 MyBatis 的代码生成器,自动生成实体类、相关配置文件减少了开发者的工作,但有时更改一个表字段,带来的结果可能是需要同时修改好几个 XML 和 Java 代码,使得开发者在 xml 的配置文件和 Java 代码之间经常切换。
后来,MyBatis 做了大量的升级优化,可以通过使用注解来减少相关的配置文件。在开篇介绍《什么是 Spring Boot》中提到,Spring Boot 一大特色就是自动配置(AutoConfiguration),为许多第三方开发库提供了几乎可以零配置的开箱即用的能力,如 MyBatis。而 MyBatis 开箱即用的启动器(Starter) 即 mybatis-spring-boot-starter,使得 Spring Boot 整合 MyBatis,可以做到几乎 0 配置开发。mybatis-spring-boot-starter 支持传统的 Mapper.xml 的配置方法;支持几乎没有配置的注解方式。本小节主要使用注解的方式,Mapper.xml 会在文末稍微提一提。
MyBatis integration with Spring Boot 官方 GitHub 仓库 https://github.com/mybatis/spring-boot-starter
MyBatis Spring-Boot-Starter will help you use MyBatis with Spring Boot。
2、MyBatis 的配置
新建项目 03-sql-mybatis,记得勾选 MyBatis 、MySQL 依赖,注意 Spring Boot 的版本要为 2.1.X 版本。
MyBatis 对 Spring Boot 版本支持
- master(2.1.x) : MyBatis 3.5+, MyBatis-Spring 2.0+(2.0.3+ recommended), Java 8+ and Spring Boot 2.1+
- 2.0.x : MyBatis 3.5+, MyBatis-Spring 2.0+, Java 8+ and Spring Boot 2.0/2.1.
- 1.3.x : MyBatis 3.4+, MyBatis-Spring 1.3+, Java 6+ and Spring Boot 1.5
2.1、分页插件 Pagehelper
对于 MyBatis 的分页,在本小节中,通过 Pagehelper 来实现。
Gradle 依赖
dependencies { implementation "org.springframework.boot:spring-boot-starter-web"
implementation "org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.1"
implementation "com.github.pagehelper:pagehelper-spring-boot-starter:1.2.13"
compileOnly "org.projectlombok:lombok"
runtimeOnly "mysql:mysql-connector-java"
annotationProcessor "org.projectlombok:lombok"
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude group: "org.junit.vintage", module: "junit-vintage-engine"
}
}
Maven 依赖
<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>2.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper-spring-boot-starter -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.13</version>
</dependency>
</dependencies>
2.2、MySQL、MyBatis、Pagehelper 配置
编辑 /src/main/resources/application.properties 文件,写入如下内容,对于使用注解的方式来说,除此之外没有其他配置了,至于 MyBatis 如何连接到数据库,如何管理连接,Mapper 类如何跟表映射,这些统统交给 mybatis-spring-boot-starter:
# 数据库 URL、用户名、密码、JDBC Driver更换数据库只需更改这些信息即可# MySQL 8 需要指定 serverTimezone 才能连接成功
spring.datasource.url=jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.password=xiaoxian
spring.datasource.username=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# MyBatis 驼峰命名转换
mybatis.configuration.map-underscore-to-camel-case=true
# 指定 Mapper 文件的地址
mybatis.mapper-locations=classpath:mapper/*.xml
## pagehelper
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql
在 Spring Boot 启动类 *Application 中,添加 @MapperScan("org.xian.boot.mapper"),自动扫描 org.xian.boot.mapper 下的 Mapper 类,使用此注解,无需再所有的 Mapper 类里添加 @Mapper 注解
@SpringBootApplication@MapperScan("org.xian.boot.mapper")
public class BootApplication {
public static void main(String[] args) {
SpringApplication.run(BootApplication.class, args);
}
}
2.3、通用消息类 MyResponse
MyResponse:通用消息返回类,增加、删除、修改操作是否成功和信息返回的类:
@Getter@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class MyResponse implements Serializable {
private static final long serialVersionUID = -2L;
private String status;
private String message;
}
3、开始使用 MyBatis
项目依旧划分为三层,只不过数据持久层框架换成了 MyBatis。
<img src="https://gitee.com/ylooq/image-repository/raw/master/image2020/20200308195133.png" alt="项目结构" style="zoom:50%;" />
API 接口层:提供 RESTful API 接口,是系统对外的交互的接口。
接口服务层:应用的主要逻辑部分,不推荐在 API 接口层写应用逻辑。
数据持久层:编写相应的 MyBatis Mapper 接口,实现与 MySQL 数据库的交互。
本小节中,将实现如下几个 RESTful API 接口:
- /api/user/insert : 插入一条数据
- /api/user/select : 查询一条数据
- /api/user/update : 更新一条数据
- /api/user/delete : 删除一条数据
- /api/user/selectAll : 浏览所有数据
- /api/user/selectPage : 分页浏览
3.1、数据表结构和 Mapper 实体类
跟上一小节的表结构一样:
字段名 字段类型 备注
user_id
bigint
主键,自增
username
varchar(18)
用户名,非空唯一
nickname
varchar(36)
用户昵称,非空
user_age
tinyint
用户年龄
user_sex
varchar(2)
用户性别
SQL 语句
-- MySQLcreate table sys_user
(
user_id bigint auto_increment,
username varchar(18) not null,
nickname varchar(36) not null,
user_age tinyint null,
user_sex varchar(2) null,
constraint sys_user_pk
primary key (user_id)
);
Mapper 实体类:新建 package,名称为 entity 。在 entity下新建一个 SysUser 类:
@Datapublic class SysUser implements Serializable {
private static final long serialVersionUID = 4522943071576672084L;
private Long userId;
@NotEmpty(message = "用户名不能为空")
@Pattern(regexp = "^[a-zA-Z0-9]{3,16}$", message = "用户名需3到16位的英文,数字")
private String username;
@NotEmpty(message = "用户昵称不能为空")
private String nickname;
@Range(min=0, max=100,message = "年龄需要在 0 到 100 之间")
private Integer userAge;
private String userSex;
}
@NotEmpty(message = " "):不能为空,message 表示 null 或者字符长度为 0 时候的提示信息。
@Pattern:正则表达式,例如你可以用来验证用户名、密码是否符合规范。
@Range:指定最大值和最小值,例如指定分数最大是 100。
3.2、注解方式的 Mapper 接口
新建 mapper 的包,新建 SysUserMapper 接口。跟 JPA 不同是,你需要通过注解或者 mapper.xml 的方式自定义编写 SQL。OnXml 结尾的方法是使用 Mapper.xml 方式指定 SQL ,这在后面会提到,其和不带 OnXml 后缀的方法是等同的。
public interface SysUserMapper { /** 往 sys_user 插入一条记录
* @param sysUser 用户信息
*/
@Insert("Insert Into sys_user(username, nickname, user_age, user_sex) " +
"Values(#{username}, #{nickname}, #{userAge}, #{userSex})")
@Options(useGeneratedKeys = true, keyProperty = "userId")
void insert(SysUser sysUser);
void insertOnXml(SysUser sysUser);
/** 根据用户 ID 查询用户信息
* @param userId 用户 ID
* @return 用户信息
*/
@Select("Select user_id,username, nickname, user_age, user_sex From sys_user Where user_id=#{userId}")
@Results({
@Result(property = "userId", column = "user_id"),
@Result(property = "userAge", column = "user_age"),
@Result(property = "userSex", column = "user_sex")
})
SysUser selectByUserId(Long userId);
SysUser selectByUserIdOnXml(Long userId);
/** 根据用户名更新用户昵称、用户年龄、用户性别 信息
* @param sysUser 用户信息
*/
@Update("Update sys_user Set nickname=#{nickname}, user_age=#{userAge}, user_sex=#{userSex} Where username=#{username}")
void update(SysUser sysUser);
void updateOnXml(SysUser sysUser);
/** 根据用户 ID 删除用户信息
* @param userId 用户 ID
*/
@Delete("Delete From sys_user where user_id=#{userId}")
void delete(Long userId);
void deleteOnXml(Long userId);
/** 浏览所有用户信息
* @return 所有用户信息
*/
@Select("Select * From sys_user")
List<SysUser> selectAll();
List<SysUser> selectAllOnXml();
}
代码解析:
@Insert,@Select,@Update, @Delete 分别注解 SQL 的 insert,select,update,delete 语句。
MyBatis 的 SQL 传入参数通过 #{param} 的方式,param 的名字应当和你 Java 变量名一样。SQL 的其他部分和标准的 SQL 语句没有区别。
如果 Mapper 接口方法传入的是一个类,也无需手动使用 Getter 方法给 SQL 的传入参数赋值。MyBatis 会自动根据类中的成员变量名自动赋值。
@Results 如果没有开启 MyBatis 驼峰命名转换,或者某些字段不符合驼峰命名转换规则,如:数据库中字段名称为 user_sex,而 Java 类中的成员变量却是 sex,则需要通过此方式手动进行映射。
@Result(property = "userId", column = "user_id"),property 指定 Java 类的成员变量名,column 指定数据库的字段名。
3.3、接口服务层 Service
新增 service 的 Package 包,新增 SysUserService:
@Servicepublic class SysUserService {
@Resource
private SysUserMapper sysUserMapper;
/** 保存一条记录
* @param sysUser 用户信息
* @return 保存结果
*/
public MyResponse insert(SysUser sysUser) {
try {
sysUserMapper.insert(sysUser);
return new MyResponse("success", "新增成功");
} catch (Exception e) {
return new MyResponse("error", e.getMessage());
}
}
/** 根据用户 ID 查询一条记录
* @param userId 用户 ID
* @return 用户信息
*/
public SysUser select(Long userId) {
return sysUserMapper.selectByUserIdOnXml(userId);
}
/** 根据用户名更新用户年龄、性别、昵称信息
* @param sysUser 用户信息
* @return 结果
*/
public MyResponse update(SysUser sysUser) {
try {
sysUserMapper.update(sysUser);
return new MyResponse("success", "更新成功");
} catch (Exception e) {
return new MyResponse("error", e.getMessage());
}
}
/** 根据用户 ID 删除用户信息
* @param userId 用户 ID
* @return 操作结果
*/
public MyResponse delete(Long userId) {
try {
sysUserMapper.delete(userId);
return new MyResponse("success", "删除成功");
} catch (Exception e) {
return new MyResponse("error", e.getMessage());
}
}
/** 浏览所有用户信息
* @return 所有用户信息
*/
public List<SysUser> selectAll() {
return sysUserMapper.selectAll();
}
/** 分页浏览
* @return 一页的用户信息
*/
public PageInfo<SysUser> selectPage(int page,int size) {
// PageHelper 随后执行的查询会自动分页
PageHelper.startPage(page, size);
PageHelper.orderBy("user_id DESC");
return PageInfo.of(sysUserMapper.selectAllOnXml());
}
}
代码解析:
@Service:定义一个 Bean ,此注解的类会自动注册到 Spring 容器。
@Resource:相对于 @Autowired 注解,Bean 的自动装配。
PageHelper.startPage(page, size); 调用此方法后,随后的查询讲自动使用分页模式。
PageInfo.of(sysUserMapper.selectAllOnXml()); 讲查询返回某一页的信息使用 PageInfo 打包。
3.4、 API 接口层
新增 controller 的 Package 包,新增 SysUserController:
@RestController@RequestMapping(value = "/api/user")
public class SysUserController {
@Resource
private SysUserService sysUserService;
@PostMapping(value = "/insert")
public MyResponse insert(@RequestBody SysUser sysUser) {
return sysUserService.insert(sysUser);
}
@PostMapping(value = "select")
public SysUser select(@RequestBody Long userId) {
return sysUserService.select(userId);
}
@PostMapping(value = "/update")
public MyResponse update(@RequestBody SysUser sysUser) {
return sysUserService.update(sysUser);
}
@PostMapping(value = "delete")
public MyResponse delete(@RequestBody Long userId) {
return sysUserService.delete(userId);
}
@GetMapping("selectAll")
public List<SysUser> selectAll() {
return sysUserService.selectAll();
}
@GetMapping("selectPage")
public PageInfo<SysUser> selectPage(@RequestParam(defaultValue = "0") Integer page,
@RequestParam(defaultValue = "3") Integer size) {
return sysUserService.selectPage(page, size);
}
}
3.5、查看运行效果
运行项目,使用 Postman 访问 RESTful API 接口:
- /api/user/insert : 插入一条数据
- /api/user/select : 查询一条数据
- /api/user/update : 更新一条数据
- /api/user/delete : 删除一条数据
- /api/user/selectAll : 浏览所有数据
- /api/user/selectPage : 分页浏览
3.6、使用 Mapper.xml 方式自定义 SQL 语句
上文通过注解的方式指定 Mapper 接口的方式,而 Mapper.xml 自定义 SQL 的方式在 Spring Boot 中也做了极大的优化,你只需要在 resources/application.properties 指定 Mapper.xml 文件的位置,其他的配置将由 MyBatis 的启动器(Starter)自动完成。
在 3.2 小节 Mapper 接口,每个使用注解自定义 SQL 的方法下面,有个 OnXml 后缀的方法,如果在 SysUserService 直接调用此方法会报错。在使用这些方法前,我们还需要在 Mapper.xml 文件下自定义 SQL 字段:
新建 resources/mapper/SysUserMapper.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 类 -->
<mapper namespace="org.xian.boot.mapper.SysUserMapper">
<!-- 返回结果的字段名、字段类型,对应类的成员变量名 ,可以定义多个 id 值不同的 resultMap -->
<resultMap id="BaseResultMap" type="org.xian.boot.entity.SysUser">
<id column="user_id" jdbcType="BIGINT" property="userId"/>
<result column="username" jdbcType="VARCHAR" property="username"/>
<result column="nickname" jdbcType="VARCHAR" property="nickname"/>
<result column="user_age" jdbcType="TINYINT" property="userAge"/>
<result column="user_sex" jdbcType="VARCHAR" property="userSex"/>
</resultMap>
<!-- SQL 语句 的通用部分 -->
<sql id="SysColumn">
user_id, username, nickname, user_age, user_sex
</sql>
<!-- Id 为 Mapper 类的成员变量名 ,resultMap 指定返回接受类 -->
<select id="selectAllOnXml" resultMap="BaseResultMap">
select
<!-- 使用 Include 包含通用的 SQL 部分 -->
<include refid="SysColumn"/>
from sys_user
</select>
<select id="selectByUserIdOnXml" parameterType="java.lang.Long" resultMap="BaseResultMap">
select
<include refid="SysColumn"/>
from sys_user Where user_id=#{userId}
</select>
<!-- parameterType 指定传入参数类型 -->
<insert id="insertOnXml" parameterType="org.xian.boot.entity.SysUser">
Insert Into sys_user(username, nickname, user_age, user_sex)
Values (#{username}, #{nickname}, #{userAge}, #{userSex})
</insert>
<!-- 完整代码看源代码仓库 03-sql-mybatis 的 resources/mapper/SysUserMapper.xml -->
</mapper>
具体的代码解析已经在源代码中注释,跟注解方式类似,只不过把自定义 SQL 挪移到了 XML 文件中。更改 SysUserService,使其调用 OnXml 后缀 Mapper 接口方法。重新运行项目,访问对应的 API,看看结果和注解方式有什么不同。
后记
在 Spring Boot 中整合 MyBatis,其启动器 mybatis-spring-boot-starter 将许多配置自动完成,不管是使用注解方式还是 Mapper.xml 的方式,都变得非常简洁。
另外,MyBatis Generator 支持自动生成 Mapper 接口、Mapper.xml、实体类,感兴趣的读者可以自行搜索了解。
下一小节中,将实战 Spring Boot 2.X 整合 RocketMQ,对于 NoSQL 的部分将稍微延后。
参考和扩展阅读:
https://mybatis.org/mybatis-3/zh/index.html
https://github.com/mybatis/spring-boot-starter
以上是 SpringBoot2.X实战SQL数据库(MyBatis) 的全部内容, 来源链接: utcz.com/z/515359.html