SpringBoot整合MyBatis
1.MyBatis介绍
三层架构和SSM框架的关系
三层架构指的是持久层、业务层和Web层
SSM就是Spring Framework Spring MVC Mybatis
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接和Java POJO (Plain Old Java Objects,普通老式Java 对象)为数据库中的记录。
2.SpringBoot整合MyBatis快速体验
1.创建一个空的SpringBoot项目
2.添加使用MyBatis的相关依赖
可以在https://mvnrepository.com搜索相关驱动
2.1.添加MyBatis的MyBatis Spring Boot Starter坐标依赖
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
在pom.xml中:先在中定义版本号<mybatis.version>2.1.1</mybatis.version>,再在中粘贴上述代码,注意中版本改为${mybatis.version}。
2.2.添加mysql数据库驱动
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
2.3.添加数据库连接池:druid
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
3.添加相应的包
- dao 进行数据访问
- entity 创建实体类的,一个实体类就对应数据库中的一张表
- contorller 开发控制层
- service 开发业务层 通过service调用dao来实现对数据库的操作
3.1 在entity中创建一个实体类
注意:可能会报错Plugin ‘org.springframework.boot:spring-boot-maven-plugin:’ not found使得无法进行后续操作,可按照https://blog.csdn.net/weixin_46291251/article/details/125289383中的方法二解决。
首先连接数据库:这里选择oa数据库中的oa_emp数据表。
在entity包中新建一个User实体,并在其中添加如下代码:
这样,一个User实体便开发好了。
注意:
- 数据库中的数据类型对应Java数据类型见下表。
类型名称 | 显示长度 | 数据库类型 | Java类型 | 备注/描述 |
---|---|---|---|---|
varchar | L+N | varchar | java.lang.String | 长度根据填入的长度变化 |
CHAR | N | char | java.lang.String | 固定长度,未填满的会以空格补齐 |
TEXT | 65535 | varchar | java.lang.String | |
INTEGER | 4 | integer unsigned | java.lang.Long | |
DECIMAL | 11 | decimal | java.lang.BigDecimal | 适用于标记价格等,自定义小数位 |
DATE | 10 | date | java.sql.Date | |
TIME | 8 | time | java.sql.Time | 只包括时分秒 |
DATETIME | 19 | datetime | java.sql.Timestamp | 包括年月日时分秒 |
TIMESTAMP | 19 | timestamp | java.sql.Timestamp | 适合记录最后的编辑时间 |
YEAR | 4 | year | java.sql.Date | |
BLOB | L+N | blob | java.lang.byte[] | |
TINYINT | 3 | tinyint unsigned | java.lang.Interger | 适合标志位 |
SMALLINT | 5 | smallint unsigned | java.lang.Interger | |
MEDIUMINT | 8 | mediumint unsigned | java.lang.Interger | |
BIT | 1 | bit | java.lang.Boolean | |
BIGINT | 20 | bigint unsigned | java.math.BigInteger | |
FLOAT | 4+8 | float | java.lang.Float | +8是因为有小数位 |
DOUBLE | 22 | double | java.lang.Double | |
ID | 11 | pk(integer unsigned) | java.lang.Long | 主键,long大一点 |
-
这里可以使用Lombok插件简化了程序的编写:
Lombok能通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法。
将其添加至依赖:
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
程序中的几个@分别代表的意思如下:
- @Data注解在类上,会为类的所有属性自动生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
- @AllArgsConstructor注解, 对象的全参数构造
- @NoArgsConstructor注解,对象的无参构造
3.2 配置Spring的相关文件
修改application.properties配置如下:
注意:application.properties中的logging.level设置日志级别
- 日志级别 trace<debug<info<warn<error<fatal,默认级别为info,即默认打印info及其以上级别的日志
- logging.level设置日志级别,后面跟生效的区域,比如root表示整个项目,也可以设置为某个包下,也可以具体到某个类名(日志级别的值不区分大小写)
3.3 在dao包中定义UserDao接口,接口中主要用来定义实现增删改查操作
注意定义时选择Interface选项。(interface也即接口,接口程序)
在该接口文件中定义功能的接口:
UserQuery类中代码如下:
// 功能:根据用户名查询用户信息并分页展示
package com.example.studymybatis.dao.query;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserQuery {
private Integer pageNum = 1; //当前的页码
private Integer pageSize = 2; //每一页所显示的数量
private String name; //根据用户名查询
}
注意这里使用的两个注解:
- @Mapper注解是由Mybatis框架中定义的一个描述数据层接口的注解,即dao层开发。注解往往起到的都是一个描述性作用,用于告诉sprigng框架此接口的实现类由Mybatis负责创建,并将其实现类对象存储到spring容器中。这样就不用再写mapper映射xml文件,自动根据这个添加@Mapper注解的接口生成一个实现类。
- @Repository用在持久层的接口上,这个注解是将接口的一个实现类交给spring管理。
3.4 在mybatis文件夹下新建一个UserMapper.xml文件实现MySQL语句
具体内容如下:
<?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.studymybatis.dao.UserDao">
<select id="listUser" resultType="com.example.studymybatis.entity.User">
slect *
from oa.oa_emp
</select>
<select id="listUserByName" parameterType="com.example.studymybatis.entity.query.UserQuery" resultType="com.example.studymybatis.entity.User">
select *
from oa.oa_emp
<where>
<if test="name !=null and name !=' '">
and 'name' like concat('%',#{name},'%')
</if>
</where>
</select>
</mapper>
3.5 编辑service层
在service包中新建一个UserService接口,其内容与UserDao接口中的内容一致。
package com.example.studymybatis.service;
import com.example.studymybatis.entity.User;
import com.example.studymybatis.entity.query.UserQuery;
import com.github.pagehelper.PageInfo;
import java.util.List;
public interface UserService {
//查询所有用户
public List<User> listUser();
//根据用户名来查询用户,并分页展示
public PageInfo<User> listUserByName(UserQuery userQuery);
}
注意:由于listUserByName函数是根据用户名查询结果分页展示信息,因此需要将List更改为PageHelper分页插件所提供的PageInfo函数
PageHelper分页插件的引入可在pom.xml文件中引入以下依赖:
<!-- 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.4.6</version>
</dependency>
PageHelper分页插件具体的使用教程可见:https://zhuanlan.zhihu.com/p/344982068
再在service包中新建一个名为UserServiceImpl的UserService实现类,用于实现UserService接口。
各个步骤的详细说明如下:
-
implements是一个类实现一个接口用的关键字。实现一个接口,必须实现接口中的所有方法。
-
进行重写操作,其快捷键为Crtl+O。接口是抽象的,其中的方法不需要实现,但是对于调用这个接口的类都必须实现或者重写这个方法。
-
使用@Serive注解:@Service注解用于类上,标记当前类是一个service类,加上该注解会将当前类自动注入到spring容器中,不需要再在applicationContext.xml文件定义bean了。
-
注入UserDao层,这样就可以直接调用UserDao层里的方法:当我们在将一个类上标注@Service或者@Controller或@Component或@Repository注解之后,spring的组件扫描就会自动发现它,并且会将其初始化为spring应用上下文中的bean。 当需要使用这个bean的时候,例如加上@Autowired注解的时候,这个bean就会被创建。而且初始化是根据无参构造函数。
-
编写listUser()函数的返回值。
-
编写listUserByName(UserQuery userQuery)函数的返回值:注意这里首先需要使用PageHelper.startPage(userQuery.getPageNum(),userQuery.getPageSize())进行启动,其中的括号里为传入对象的所有参数。
3.6 编辑contorller开发控制层
在contorller包下新建一个UserContorller类,用于控制跳转。
这里有几个注意的点:
- 加@Controller注解。(几个加注解的地方都比较固定)
- 注入UserService层并添加注解
- 编写一个index()方法用于返回index.html,并添加@GetMapping注解用于设置地址。
3.7 index.html文件的编写
index.html文件用于定义程序运行后http://localhost/8080网页所展示的内容和形式,其应在resources/templates中创建,其中templates即专门用于存放html文件的文件夹。
注意:需提前在pom.xml文件中引入spring-boot-starter-thymeleaf依赖才能正常实现html文件页面的跳转。(Thymeleaf是一个模板引擎,主要用于编写动态页面。)
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>3.0.0</version>
</dependency>
index.html文件的结构如下:
此时直接运行会报错,报错界面如下:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field userDao in com.example.studymybatis.service.UserServiceImpl required a bean of type 'com.example.studymybatis.dao.UserDao' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.example.studymybatis.dao.UserDao' in your configuration.
Process finished with exit code 0
网上搜索该错误解决方案,发现该问题都是由于没有添加@Mapper注解引起的,但是我在UserDao接口已经添加过@Mapper注解了。最后发现是spring-boot-starter-parent版本太高(原来是3.0.0版本)的缘故,在pom.xml文件中将其版本改为2.4.3即可成功编译运行。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
网页界面:
此时网页比较单调,若要更改页面形式和内容,可继续在index.html文件进行编写。
更改ui样式
https://semantic-ui.com/introduction/build-tools.html
Semantic-UI是一个前端ui库
https://echarts.apache.org/zh/index.html
Apache ECharts是一个基于 JavaScript 的开源可视化图表库
这里使用Semantic-UI来尝试进行前端的UI简单开发。
首先选择一个基本表格,并复制其源代码粘贴至index.html文件的
中。<body>
<div class="ui container">
<table class="ui table" style="margin-top: 40px !important;"> # 把顶部间距调至40px
<thead>
<tr>
<th>Name</th>
<th>Registration Date</th>
<th>E-mail address</th>
<th>Premium Plan</th>
</tr>
</thead>
<tbody>
<tr>
<td>John Lilki</td>
<td>September 14, 2013</td>
<td>jhlilk22@yahoo.com</td>
<td>No</td>
</tr>
<tr>
<td>Jamie Harington</td>
<td>January 11, 2014</td>
<td>jamieharingonton@yahoo.com</td>
<td>Yes</td>
</tr>
<tr>
<td>Jill Lewis</td>
<td>May 11, 2014</td>
<td>jilsewris22@yahoo.com</td>
<td>Yes</td>
</tr>
</tbody>
</table>
</div>
</body>
程序运行结果如下,可以发现还不是很美观,可以继续引入样式和JavaScript文件。
引入样式和JavaScript文件
将上面的两行代码分别粘贴至index.html文件中,同时引入jquery cdn(见https://www.bootcdn.cn/jquery/)如下所示:
<head>
<meta charset="UTF-8">
<title>首页</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.5.0/dist/semantic.min.css">
</head>
<body>
<div class="ui container">
...
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script>
<script src="https://cdn.jsdelivr.net/npm/semantic-ui@2.5.0/dist/semantic.min.js"></script>
</body>
运行程序,此时已经出现了一个较为美观的表格。
最后,将表格样式调整至我们需要的形式。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui@2.5.0/dist/semantic.min.css">
</head>
<body>
<div class="ui container">
<table class="ui table" style="margin-top: 40px !important;">
<thead>
<tr>
<th>id</th>
<th>姓名</th>
<th>性别</th>
<th>工资</th>
<th>入职日期</th>
<th>部门id</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>John Lilki</td>
<td>September 14, 2013</td>
<td>jhlilk22@yahoo.com</td>
<td>No</td>
<td>No</td>
<td>No</td>
<td>
<a href=" " class="ui button mini pink">编辑</a>
<a href=" " class="ui button mini teal">删除</a>
</td>
</tr>
</tbody>
</table>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script>
<script src="https://cdn.jsdelivr.net/npm/semantic-ui@2.5.0/dist/semantic.min.js"></script>
</body>
</html>
4.给前端页面传递数据
4.1 分页显示数据库中数据
在UserContorller类的index函数中传入Model接口
@GetMapping("/")
public String index(Model model, UserQuery userQuery){
PageInfo<User> userPageInfo = userService.listUserByName(userQuery);
model.addAttribute("page",userPageInfo);
return "index";
}
Model(org.springframework.ui.Model)是一个接口,包含addAttribute方法,其实现类是ExtendedModelMap。ExtendedModelMap继承了ModelMap类,ModelMap类实现了Map接口。Model通过以下方法向页面传递参数:
Model.addAttribute(String attributeName,Object attributeValue);
之后,在index.html文件中引入thymeleaf命名空间以使用thymeleaf模板:
<html lang="en" xmlns:th="http://www.thymeleaf.org">
再更改数据的获取代码:
<tbody>
<tr th:each="oa_emp : ${page.list}">
<td th:text="${oa_emp.id}">John Lilki</td>
<td th:text="${oa_emp.name}">September 14, 2013</td>
<td th:text="${oa_emp.gender}">jhlilk22@yahoo.com</td>
<td th:text="${oa_emp.salary}">No</td>
<td th:text="${oa_emp.hire_date}">No</td>
<td th:text="${oa_emp.dept_id}">No</td>
<td>
<a href=" " class="ui button mini pink">编辑</a>
<a href=" " class="ui button mini teal">删除</a>
</td>
</tr>
</tbody>
由于是分页显示数据,因此再加上页码栏用于实现页码跳转:
<div class="ui attached segment" >
<table class="m-mobile-wide" width="425px">
<tbody>
<tr align="center">
<td>
<a th:href="@{/(pageNum=${page.pageNum}-1)}" class="ui button basic mini" th:unless="${page.isFirstPage}">上一页</a>
</td>
<td>
第
<h8 th:text="${page.pageNum}">2</h8>
页/共
<h8 th:text="${page.pages}">4</h8>
页
共
<h8 th:text="${page.total}">29</h8>
条
</td>
<td>
<form name="pageForm" th:action="@{/}" method="get">
<div class="ui mini input ">
<input type="text" class="m-bg" name="pageNum" placeholder="页码" style="width: 50px!important; height: 27.76px!important;" required>
<button type="submit" style="font-size: 11px!important;width: 30px!important; height: 0px!important; border: none;margin: 5px;padding: 0;" class="button mini">
跳转
</button>
</div>
</form>
</td>
<td> </td>
<td style="float: right">
<a th:href="@{/(pageNum=${page.pageNum}+1)}" class="ui button basic mini " style="float: right;" th:unless="${page.isLastPage}">下一页</a>
</td>
</tr>
</tbody>
</table>
</div>
运行程序,结果如下:
4.2 根据用户名搜索数据库数据
在UserContorller类中新建一个listUserByName方法:
@PostMapping("/")
public String listUserByName(Model model,UserQuery userQuery){
PageInfo<User> userPageInfo = userService.listUserByName(userQuery);
model.addAttribute("page",userPageInfo);
return "index";
}
这里@PostMapping注解是用于处理post请求。
在index.html文件中增加一个form表单用于显示搜索框和获取UserQuery中所定义的name参数值。
<div>
<form th:action="@{/}" method="post">
<input type="text" name="name" placeholder="输入用户名进行查找">
<input type="submit" value="搜索">
</form>
</div>
运行程序,结果如下:
4.3 新增用户实现
先在index页面中添加一个“新增”按钮,再在UserDao接口中定义一个新增用户功能的持久化方法。
…
在UserServiceImpl的UserService实现类中添加接口实现方法
…
有人看到话后面再写。
4.4 删除用户实现
在UserDao接口中定义一个删除用户功能的持久化方法。
//根据用户id删除用户
public int deleteUserById(Integer id); //传入的参数是被删除的id,故是int类型
再在UserMapper.xml文件中编写删除用户的SQL语句。
<delete id="deleteUserById" parameterType="int">
delete from oa.oa_emp where id=#{id}
</delete>
在UserService接口文件中增加删除方法,并在名为UserServiceImpl的UserService实现类中Crtl+O进行实现。
//根据用户id删除用户
public boolean deleteUserById(Integer id); //希望得到的结果是是否被删除,故是boolean类型
@Override
public boolean deleteUserById(Integer id) {
int i = userDao.deleteUserById(id);
if (i > 0){
return true; //删除成功
}else {
return false;
}
}
在UserContorller文件下添加关于用户删除的代码:
@GetMapping("/delete/{id}") //网页路径
public String delete(@PathVariable("id") Integer id, RedirectAttributes attributes){
boolean b = userService.deleteUserById(id);
if (b){
attributes.addFlashAttribute("message","删除用户成功");
return "redirect:/"; //重定向到/页面
}else {
attributes.addFlashAttribute("message","删除用户失败");
return "redirect:/"; //重定向到/页面
}
}
再在首页文件中找到删除按钮对应的代码。修改为如下代码:
<a th:href="@{/delete/{id}(id=${oa_emp.id})}" class="ui button mini teal">删除</a>
运行程序,可以删除,但是并没有提示"删除用户成功"这个消息界面出现,此时应在首页文件中继续添加
<div class="ui success message" th:unless="${#strings.isEmpty(message)}">
<i class="close icon"></i>
<div class="header">提示:</div>
<p th:text="${message}"></p>
</div>
<script>
$(".message .close").on('click',function () {
$(this).closest(".message")
.transition("fade");
})
</script>
这样,再执行程序,并点击删除按钮即可以完成从数据库中删除数据的功能,且在页面下方会出现消息提示框。
4.5 编辑用户实现
首先在templates文件夹下新建一个用于编辑和新增用户的editUser.html文件并修改
<div class="ui container">
<form class="ui form" action="" method="post" style="margin-top: 80px !important;" th:object="${user}">
<div class="field">
<label>用户名</label>
<input type="text" name="name" placeholder="请输入用户名" required th:value="*{name}">
</div>
<div class="field">
<label>性别</label>
<input type="text" name="gender" placeholder="请输入性别" required th:value="*{gender}">
</div>
<div class="field">
<label>工资</label>
<input type="text" name="salary" placeholder="请输入工资" th:value="*{salary}">
</div>
<div class="field">
<label>入职日期</label>
<input type="text" name="hire_date" placeholder="请输入入职日期" th:value="*{hire_date}">
</div>
<div class="field">
<label>部门id</label>
<input type="text" name="dept_id" placeholder="请输入部门id" th:value="*{dept_id}">
</div>
<button class="ui button" type="submit">提交</button>
</form>
</div>
在UserDao接口文件中定义一个根据用户id查询用户的方法,并在UserMapper.xml文件中编写删除用户的SQL语句。
//根据用户id查询用户
public User queryUserById(Integer id);
<select id="queryUserById" parameterType="int" resultType="com.example.studymybatis.entity.User">
select * from oa.oa_emp where id=#{id}
</select>
在UserService接口文件中增加根据用户id查询用户的方法,并在名为UserServiceImpl的UserService实现类中Crtl+O进行实现。
//根据用户id查询用户
public User queryUserById(Integer id);
@Override
public User queryUserById(Integer id) {
return userDao.queryUserById(id);
}
然后在UserContorller文件下添加关于根据用户id查询用户的代码:
@GetMapping("/edit/{id}")
public String toEdit(@PathVariable Integer id, Model model){
model.addAttribute("user",userService.queryUserById(id));
return "editUser";
}
再在首页文件中找到编辑按钮对应的代码。修改为如下代码:
<a th:href="@{/edit/{id}(id=${oa_emp.id})}" class="ui button mini pink">编辑</a>
这样,即可通过点击编辑按钮进入修改用户界面,且显示了原本的值:
但此时修改后点击提交会报错,这是因为提交按钮还没有添加处理事件,且还没有写编辑过程的代码。
在UserDao接口文件中定义一个修改用户的方法,并在UserMapper.xml文件中编写更新用户的SQL语句。
//修改用户
public int updateUser(User user);
<update id="updateUser" parameterType="com.example.studymybatis.entity.User">
update oa.oa_emp
set name = #{name}, gender=#{gender},salary=#{salary},hire_date=#{hire_date},dept_id=#{dept_id}
where id=#{id};
</update>
在UserService接口文件中增加编辑用户的方法,并在名为UserServiceImpl的UserService实现类中Crtl+O进行实现。
//修改用户
public boolean updateUser(User user);
@Override
public boolean updateUser(User user) {
int i = userDao.updateUser(user);
if (i > 0){
return true;
}else {
return false;
}
}
在UserContorller文件下添加关于用户更新的代码:
@PostMapping("/edit")
public String edit(User user,RedirectAttributes attributes){
boolean b = userService.updateUser(user);
if (b){
attributes.addFlashAttribute("message","更新用户成功");
return "redirect:/"; //重定向到/页面
}else {
attributes.addFlashAttribute("message","更新用户失败");
return "redirect:/"; //重定向到/页面
}
}
再在editUser.html文件中添加如下代码:
<form class="ui form" th:action="@{/edit}" method="post" style="margin-top: 80px !important;" th:object="${user}">
<input type="hidden" th:value="*{id}" name="id">
运行程序,此时就可以修改用户信息了。
常用注解
@Mapper
// 使用这个注解表示这是一个mybatis的mapper类
@Component
// 泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注
@Repository
// 用于dao层 用于标注数据访问组件,即dao层
@Servico
// 用于标注业务层组件
@Controller
// 用于标注控制层组件(如struts中的action)
// 以上四个注解的作用都是将相应的类注入到spring容器中
@GetMapping
//处理get请求
@PostMapping
//处理post请求
@PutMapping
//处理put请求
@DeleteMapping
//处理delete请求
@PostMapping(value = “/user/login”)
//等价于
@RequestMapping(value = “/user/login”,method = RequestMethod.POST)