用 MyBatis 实现数据的 CRUD
本篇博客将通过 MyBatis 来实现常用的数据增加、删除、修改、查询和分页功能。
1.创建项目 & 引入依赖
创建一个 Spring Boot 项目,并引入 MyBatis 和 MySQL 依赖。
<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.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</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>
</dependency>
</dependencies>
2.实现数据表的自动初始化
在项目的 resources
目录下新建 db
目录,并添加 schema.sql
文件,然后在此文件中写入创建 user
表的 SQL 语句,以便进行初始化数据表。具体代码如下:
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
在 application.properties
配置文件中配置数据库连接,并加上数据表初始化的配置。具体代码如下:
spring.datasource.initialize=true
spring.datasource.initialization-mode=always
spring.datasource.schema=classpath:db/schema.sql
完整的 application.properties
文件如下:
spring.datasource.url=jdbc:mysql://127.0.0.1/book?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=true
spring.datasource.username=xxxx
spring.datasource.password=xxxxxx
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
spring.datasource.initialization-mode=always
spring.datasource.schema=classpath:db/schema.sql
spring.thymeleaf.cache=false
server.port=8080
这样,Spring Boot 在启动时就会自动创建 user
表。
3.实现实体对象建模
用 MyBatis 来创建实体,见以下代码:
package com.example.demo.entity;
import lombok.Data;
@Data
public class User {
private int id;
private String name;
private int age;
}
从上述代码可以看出,用 MyBatis 创建实体是不需要添加注解 @Entity
的,因为 @Entity
属于 JPA 的专属注解。
4.实现实体和数据表的映射关系
实现实体和数据表的映射关系可以在 Mapper 类上添加注解 @Mapper
,见以下代码。建议以后直接在入口类加 @MapperScan("com.example.demo.mapper")
,如果对每个 Mapper 都加注解则很麻烦。
package com.example.demo.mapper;
import com.example.demo.entity.User;
import com.github.pagehelper.Page;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User queryById(@Param("id") int id);
@Select("SELECT * FROM user")
List<User> queryAll();
@Insert({"INSERT INTO user(name,age) VALUES(#{name},#{age})"})
int add(User user);
@Delete("DELETE FROM user WHERE id = #{id}")
int delById(int id);
@Update("UPDATE user SET name=#{name},age=#{age} WHERE id = #{id}")
int updateById(User user);
@Select("SELECT * FROM user")
Page<User> getUserList();
}
5.实现增加、删除、修改和查询功能
创建控制器实现操作数据的 API,见以下代码:
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
UserMapper userMapper;
@RequestMapping("/querybyid")
User queryById(int id) {
return userMapper.queryById(id);
}
@RequestMapping("/")
List<User> queryAll() {
return userMapper.queryAll();
}
@RequestMapping("/add")
String add(User user) {
return userMapper.add(user) == 1 ? "success" : "failed";
}
@RequestMapping("/updatebyid")
String updateById(User user) {
return userMapper.updateById(user) == 1 ? "success" : "failed";
}
@RequestMapping("/delbyid")
String delById(int id) {
return userMapper.delById(id) == 1 ? "success" : "failed";
}
}
- 启动项目,访问
http://localhost:8080/user/add?name=pp&age=20
,会自动添加一个name=pp
、age=20
的数据。
- 访问
http://localhost:8080/user/updatebyid?name=pipi&age=26&id=1
,会实现对id=1
的数据的更新,更新为name=pipi
、age=26
。
- 访问
http://localhost:8080/user/querybyid?id=1
,可以查找到id=1
的数据,此时的数据是name=pipi
、age=26
。
- 访问
http://localhost:8080/user/
,可以查询出所有的数据。
- 访问
http://localhost:8080/user/delbyid?id=1
,可以删除id
为1
的数据。
6.配置分页功能
6.1 增加分页支持
分页功能可以通过 PageHelper
来实现。要使用 PageHelper
,则需要添加如下依赖,并增加 Thymeleaf
支持。
<!-- 增加对PageHelper的支持 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.6</version>
</dependency>
<!--增加thymeleaf支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
6.2 创建分页配置类
创建分页配置类来实现分页的配置,见以下代码:
package com.example.demo.config;
import com.github.pagehelper.PageHelper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
@Configuration
public class PageHelperConfig {
@Bean
public PageHelper pageHelper(){
PageHelper pageHelper = new PageHelper();
Properties p = new Properties();
p.setProperty("offsetAsPageNum", "true");
p.setProperty("rowBoundsWithCount", "true");
p.setProperty("reasonable", "true");
pageHelper.setProperties(p);
return pageHelper;
}
}
代码解释如下。
@Configuration
:表示 PageHelperConfig 这个类是用来做配置的。@Bean
:表示启动 PageHelper 拦截器。offsetAsPageNum
:设置为true
时,会将 RowBounds 第一个参数offset
当成pageNum
页码使用。rowBoundsWithCount
:设置为true
时,使用 RowBounds 分页会进行count
查询。reasonable
:启用合理化时,如果pageNum<1
会查询第一页,如果pageNum>pages
会查询最后一页。
7.实现分页控制器
创建分页列表控制器,用以显示分页页面,见以下代码:
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@Controller
public class UserListController {
@Autowired
UserMapper userMapper;
@RequestMapping("/listall")
public String listCategory(Model m, @RequestParam(value="start", defaultValue="0")int start, @RequestParam(value="size", defaultValue="5") int size) throws Exception {
PageHelper.startPage(start,size,"id desc");
List<User> cs = userMapper.queryAll();
PageInfo<User> page = new PageInfo<>(cs);
m.addAttribute("page", page);
return "list";
}
}
start
:在参数里接收当前是第几页。默认值是0
。size
:每页显示多少条数据。默认值是5
。PageHelper.startPage(start,size,"id desc")
: 根据start
、size
进行分页,并且设置id
倒排序。List<User>
:返回当前分页的集合。PageInfo<User>
:根据返回的集合创建 Pagelnfo 对象。model.addAttribute("page", page)
:把page
(PageInfo
对象)传递给视图,以供后续显示。
8.创建分页视图
接下来,创建用于视图显示的 list.html
,其路径为 resources/template/list.html
。
在视图中,通过 page.pageNum
获取当前页码,通过 page.pages
获取总页码数,见以下代码:
<div class="with:80%">
<div th:each="u : ${page.list}">
<span scope="row" th:text="${u.id}">id</span>
<span th:text="${u.name}">name</span>
</div>
</div>
<div>
<a th:href="@{listall?start=1}">[首页]</a>
<a th:href="@{/listall(start=${page.pageNum-1})}">[上页]</a>
<a th:href="@{/listall(start=${page.pageNum+1})}">[下页]</a>
<a th:href="@{/listall(start=${page.pages})}">[末页]</a>
<div>当前页/总页数:<a th:text="${page.pageNum}" th:href="@{/listall(start=${page.pageNum})}"></a>
/<a th:text="${page.pages}" th:href="@{/listall(start=${page.pages})}"></a></div>
</div>
启动项目,多次访问 http://localhost:8080/user/add?name=pp&age=26
增加数据,然后访问 http://localhost:8080/listall
可以查看到分页列表。
但是,上述代码有一个缺陷:显示分页处无论数据多少都会显示“上页、下页”。所以,需要通过以下代码加入判断,如果没有上页或下页则不显示。
<a th:if="${not page.IsFirstPage}" th:href="@{/listall(start=${page.pageNum-1})}">[上页]</a>
<a th:if="${not page.IsLastPage}" th:href="@{/listall(start=${page.pageNum+1})}">[下页]</a>
上述代码的作用是:如果是第一页,则不显示“上页”;如果是最后一页,则不显示“下页”。
还有一种更简单的方法:在 Mapper 中直接返回 page 对象,见以下代码:
@Select("SELECT * FROM user")
Page<User> getUserList();
然后在控制器中这样使用:
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserListControllerB {
@Autowired
UserMapper userMapper;
// http://localhost:8080/listall2?pageNum=1&pageSize=2
@RequestMapping("/listall2")
// 如果方法的参数不指定默认值,且请求地址也没有指定参数值,则项目运行时会报错。
public Page<User> getUserList(@RequestParam(value="pageNum",defaultValue="0")int pageNum, @RequestParam(value = "pageSize", defaultValue = "5") int pageSize)
//public Page<User> getUserList(Integer pageNum, Integer pageSize)
{
PageHelper.startPage(pageNum, pageSize);
Page<User> userList= userMapper.getUserList();
return userList;
}
}
代码解释如下。
pageNum
:页码。pageSize
:每页显示多少记录。