SpringBoot整合Mybatis超详细流程
文章目录
前言
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Ordinary Java Object,普通老式 Java 对象)为数据库中的记录。
因为 Mybatis的性能非常不错,SpringBoot 官方推荐使用 Mybatis 来连接数据库进行 CRUD 操作。
Mybatis的官方文档地址:https://mybatis.org/mybatis-3/zh/index.html
详细流程
0.引入Mybatis
建立项目后记得更改端口号为8081,我们之后的页面访问都会在8081端口。
我们使用 IDEA 建立一个 SpringBoot 项目,创建项目的流程可以回顾 SpringBoot入门
初始化组件部分选择 Web、JDBC API、MyBatis Framework、MySQL Driver
项目初始化完成之后,可以在 pom.xml
文件中看到如下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
1.创建数据
我们使用 Navicat 连接 MySql 数据库,在 Navicat 图形界面中创建一些简单的数据。
- 创建数据库,名字叫
zzz
命令行:
create database if not exists zzz default charset utf8 collate utf8_general_ci;
1
或者直接使用呢图形界面快速建立数据库
- 建立数据表
user
create table user(
id INT UNSIGNED auto_increment,
username varchar(20) not null,
password varchar(30) not null,
age INT not null,
primary key(id)
)charset=utf8;
- 利用图形界面插入几条数据,方便之后用来做简单的测试
2.创建程序目录
我们在 com.example 目录下新建四个目录,分别是 controller、dao、entity、service。
- controller层负责具体的业务模块流程的控制
- entity层用于存放我们的实体类,与数据库中的属性值基本保持一致,实现set和get的方法
- dao层主要是做数据持久层的工作,负责与数据库联络,封装了增删改查基本操作
- service层主要负责业务模块的逻辑应用设计,具体要调用到已定义的DAO层的接口
然后在 resource 目录下新建 mapper 目录。这个 mapper 目录是用来存放 SQL 语句的地方。
顺便提一下,我们知道的 MVC 框架,即 model-view-controller 三层架构。这里 model层=entity层,与数据库的数据表对应,view层和 controller层结合非常紧密,需要联合起来一起开发。可以简单理解为 view层是做前端界面的展示,controller层做业务模流程块的控制。
如果在网上看到有 mapper层这个概念,就记住,mapper层=dao层,就是对数据库进行数据持久化操作。
不管是什么框架,我们很多时候都会与数据库进行交互。如果遇到一个场景我们都要去写SQL语句,那么我们的代码就会很冗余。所以,我们就想到了把数据库封装一下,让我们的数据库的交道看起来像和一个对象打交道,这个对象通常就是DAO。当我们操作这个对象的时候,这个对象会自动产生SQL语句来和数据库进行交互,我们就只需要使用DAO就行了。
通常我们在DAO层里面写接口,里面有与数据打交道的方法。SQL语句通常写在mapper文件里面的。
Service层是建立在DAO层之上的,建立了DAO层后才可以建立Service层,而Service层又是在Controller层之下的,因而Service层应该既调用DAO层的接口,又要提供接口给Controller层的类来进行调用,它刚好处于一个中间层的位置。每个模型都有一个Service接口,每个接口分别封装各自的业务处理方法。
3.理解后台访问流程
用户从页面前端,也就是我们所说的 view 层进行查询访问,进入到 controller 层找到对应的接口,接 着 controller 进行对 service 层进行业务功能的调用,service 要进入 dao 层查询数据,dao 层调用 mapper.xml 文件生成 sql 语句到数据库中进行查询。
在数据库中查询到数据,dao 层拿到实体对象的数据,接着交付给 service 层,接着 service 进行业务 逻辑的处理,返回结果给 controller,controller 根据结果进行最后一步的处理,返回结果给前端页 面。
创建一个访问用户信息的流程为:entity->dao->mapper->service->controller,这里 mapper 指的是存放 SQL 语句的 xml 文件。
4.核心文件配置
这里我们可以使用 application.properties 文件,也可以使用 application.yml 文件来进行配置,当然推荐使用 application.yml 文件。
如果用 application.yml 在第一次启动项目的时候报错,项目右侧 maven -> lifecycle -> clean一下即可。
server:
port: 8081
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/zzz?useUnicode=true & characterEncoding=utf-8 &
useSSL=true & serverTimezone=Asia/Shanghai
username: root
password: 123456
mybatis:
mapper-locations: classpath:/mapper/*.xml
type-aliases-package: com.example.entity
这里注意 com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver。前者是 mysql-connector-java 5 中的,后者是 mysql-connector-java 6 中的(需要设置时区),相当于说一个是旧版,一个是新版。我们可以到 pom.xml 文件中查看 mysql 驱动版本,而现在基本都是 6 以上的新版本,所以直接使用后者即可。
特别强调一点,连接MySQL时使用的是spring.datasource.username,不要顺手打成spring.datasource.data-username,这个教训太深刻了。
参数解读:
- driver-class-name:mysql驱动
- url:mysql连接的url,默认是3306端口,zzz是数据库名,useSSL是使用安全套阶层连接进行数据传输(如果true出错可以选择false),serverTimezone设置时区,亚洲时区请设置上海或者香港,不要设置北京,因为系统里没有这个时区。
- username 是用户名,password 是密码
- mybatis.mapper-locations:用于将配置路径下的 * .xml 文件加载到 mybatis 中
# 方法一:只有一个路径
mybatis.mapper-locations= classpath:mapper/*.xml
# 方法二:有多个路径
mybatis.mapper-locations= classpath:mapper/.xml,classpath:mapper/user/.xml
12345
- type-aliases-package:指定POJO扫描包来让 mapper.xml 文件的 resultType 自动扫描到自定义POJO,这样就不用每次指定完全限定名
# mapper.xml文件中设置
# 完全限定名
<select id="getUsers" resultType="com.example.entity.User">
# 指定了POJO扫描包之后
<select id="getUsers" resultType="User">
5.编写entity
我们在编写entity时遵循POJO的思想。
这里需要提及“POJO最小侵入性编程”的概念,POJO(Plain Ordinary Java Object)意思是普通Java对象。类的成员是私有的,且有一系列的 setter and getter方法来提供访问。
POJO的内在含义是指那些没有从任何类继承、也没有实现任何接口,更没有被其它框架侵入的java对象。POJO的格式是用于数据的临时传递,它只能装载数据,作为数据存储的载体,而不具有业务逻辑处理的能力。
一般来讲,是 entity 中要取的数据应该和数据表相对应,但不一定要全部取出。
我们在刚才建立的 entity 目录中新建 User 类,定义属性 id、username、age。
public class User {
private int id;
private String username;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
按 alt+insert 键,生成 Getter and Setter 方法。
6.编写dao,注解@mapper和@Repository区别
springboot 集成 mybatis 开发有两种版本,注解版和配置文件版。
注解版不需要配置任何文件,拿来即用,主要依靠的是注解来生成 sql 语句。配置文件版与注解版相比,仅仅稍微复杂一点,两者的区别仅为mapper层处理的处理方式不一样。配置文件版多了一个xml文件,但是配置更加灵活,逻辑结构更加清晰,可读性更强。
注解版,用 @Mapper 注解标识,我们使用#{id}来标识参数。@Mapper 注解把 mapper 这个 DAO 交给 Spring 管理,不再写 mapper 映射文件
@Mapper
public interface UserDao {
@Select("select * from user where id=#{id}")
public User getUserById(int id);
@Delete("delete from user where id=#{id}")
public int deleteUserById(int id);
}
配置文件版,需要给编写的 dao 增加注解 @Repository。
@Repository注解修饰哪个类,则表明这个类具有对对象进行CRUD(增删改查)的功能。
而且@Repository是@Component注解的一个派生品,所以被@Repository注解的类可以自动的被@ComponentScan 通过路径扫描给找到。因此@Repository注解的类也能@Autowired实现自动装配。
通常将dao接口注入到service层的时候,需要写@Resource这个注解。加上@Repository,就是springboot生成一个bean,自动注入service的相关引用中。
我们在 dao 目录中新建 UserDao 接口,定义好方法。
/*
实现两个功能
1、根据用户id查询用户信息
2、查询同一年龄下的所有用户
返回一组数据我们用 List<E> 来存储,传递多个参数我们用 Map 来存储
例如 public List<User> selectById4(@Param("map") Map map);
*/
@Repository
public interface UserDao {
//不使用@Param
public User getUserById(Integer id);
//使用@Param
public User getUserById(@Param("id") int id);
public List<User> getUserByAge(@Param("age") int age);
}
我们推荐使用 @Param 进行传参,当映射器方法需要多个参数时,这个注解可以被用于:给映射器方法中的每个参数来取一个名字。这里有两点好处:
- 在 xml 文件中不需要再指定参数类型 parameterType
- 当传递对象时,使用 #{对象.属性} 可以更清晰地提示自己
如果不使用 @Param,多参数将会以它们的顺序位置和SQL语句中的表达式进行映射,这是默认的。
dao 层定义了接口,不需要写具体的实现类,我们只需要在 mapper 中将文件路径映射好就行了。DAO的实现原理:它是通过JDK动态代理方式实现的,我们在启动加载配置文件的时候,它会根据 mapper 的 xml文件去生成一个DAO的实现。
使用配置文件版时,我们还需要在主程序中通过使用@MapperScan可以指定要扫描的Mapper类的包的路径。
@MapperScan是要spring启动时,扫描到所有的Mapper文件,并生成代理类交给spring容器管理;
对着 dao 目录点击鼠标右键,选择 copy reference 即可复制包路径,粘贴进 MapperScan 中即可。
@MapperScan("com.example.dao")
@SpringBootApplication
public class SpringmybatisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringmybatisApplication.class, args);
}
}
7.编写Mapper
在 mapper 目录下新建 UserMapper.xml 文件。
mapper.xml 的文件头信息我们一般不会去记忆,都是直接从官网给出的教程中复制 xml 的头信息,网址是:https://mybatis.org/mybatis-3/zh/getting-started.html
我们需要更改命名空间 namespace 使之对应我们编写的 DAO,SQL语句都写在 mapper 标签里面。
<?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.dao.UserDao">
</mapper>
然后我们根据 UserDao 中要写的方法进行配置,为了方便展示,我们这里仅做查询,现在 UserDao 中有两个查询方法。
查询的话我们用 select 标签,id 指定为方法名,resultType指明返回类型,标签中间写 SQL 语句。因为我们已经配置了type-aliases-package,直接写 User 即可。
<select id="getUserById" resultType="User">
select * from `user` where id=#{id}
</select>
<select id="getUserById2" resultType="User">
select * from `user` where age=#{age}
</select>
我们在前面第四步核心文件配置的时候指定了 POJO 扫描包,所以这里 resultType 不需要完全限定名。
这里 user 表完全可以不写反引号,写反引号主要是为了方便我们区分,这是数据库的一个表。
编写 Mapper.xml 文件主要注意三点:
- namespace相对应
- id相对应
- resultType相对应
目前我们所讲的 resultType 返回单一类型的值,包括基础类型 String、Integer、Long,还有定义好的 Class 对象。
resultMap 则可以返回多个类型的值,适合多表连接查询。resultMap 的具体用法可以去官方文档中学习,这里我们不做过多的了解。
8.编写Service
我们需要在实现类中使用 @Service 注解,才能被 SpringBoot 扫描,在 Controller 中使用 @Autowired 注入
在 service 目录中新建 UserService 类
//这里只使用 @Service 也可以,括号里的内容不影响
@Service("UserService")
public class UserService {
}
使用 @Autowired 注解自动装配 UserDao。调用 dao 层接口设计逻辑应用
@Service("UserService")
public class UserService {
@Autowired
private UserDao userDao;
public User queryUser(int id){
return userDao.getUserById(id);
}
public List<User> queryUser2(int age){
return userDao.getUserByAge(age);
}
}
/*传递多个参数我们可以用 Map 来实现
public List<User> queryUser4(Integer age, Integer start, Integer move){
Map<String,Object> hashMap=new HashMap<>();
hashMap.put("age",age);
hashMap.put("start",start);
hashMap.put("move",move);
return userDao.selectById4(hashMap);
}
*/
9.编写Controller
在 controller 目录中新建 UserController 类
这里我们使用 Restful 风格,直接返回数据。
使用 @Autowired 注解自动装配 UserService。编写接口为 UserService 传输数据
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/getUser")
public User getUser(@RequestParam("id") int id){
return userService.queryUser(id);
}
@GetMapping("/getUser2")
public List<User> getUser2(@RequestParam("age") int age){
return userService.queryUser2(age);
}
}
10.运行项目
运行 SpringBoot 项目,我们在浏览器的地址栏中输入:
localhost:8081/getUser?id=1
localhost:8081/getUser2?age=20
12
即可看到返回了 id=1 和 age=20 的用户的 JSON 数据。因为这里是直接在地址栏输入参数和值,是采用 get 方式传输的,所以必须使用 GetMapping。
到这一步,整个的后台访问流程就全部打通了,大家仔细理解其中的逻辑,多多揣摩。
参考文章
spring boot框架mybatis.mapper-locations配置问题详解