Mybatis

介绍

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

下面带你动手用mybatis 注解方式写一个简单的增删改查

注解开发案例

mybatis注解开发适用于一些简单的系统,数据库系统不复杂的系统使用注解开发效率会高很多,但对于一些复杂的数据库系统或者一些复杂的要求,这时候使用注解开发可能就没有办法完成了。

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
E:.
├─.idea
├─artifacts
├─.mvn
└─wrapper
└─src
├─main
├─java
└─com
├─Entity
├─Mapper
├─Service
└─utils
├─resources
└─webapp
└─WEB-INF
└─test
├─java
└─com
└─Service
└─resources

结构分析:

  • Entity:数据库对应的实体类
  • Mapper:注解实现映射器
  • Service:获取Mapper实现数据处理
  • utils:工具包

添加mybatis依赖

在pom.xml引入以下依赖,我这里使用mybatis 3.5.3版本,你也可以使用其他版本,数据库我使用的5.1.49版本。

引入内容后,加载依赖。右键—>maven—>重新加载项目

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>

添加mybatis配置文件

在resources下有两个文件,mybatis-config.xml是配置mybatis一些相关配置,db.properties是配置连接数据库的。

添加一个名为mybatis-config的xml格式文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"/>
<!-- 配置 mybatis 输出日志,可以打印 sql-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>


<!-- 配置环境 -->
<environments default="mysql">
<!-- 配置 mysql 的环境-->
<environment id="mysql">
<!-- 事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源 POOLED:表示使用连接池 -->
<dataSource type="POOLED">
<!-- 配置连接数据库的信息 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<!-- 改为自己的数据库账号密码-->
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>

<!-- 指定映射配置文件的位置-->
<mappers>
<!-- 该路径是Mapper 包 的路径,根据自己包路径修改-->
<package name="com.mapper"/>
</mappers>
</configuration>

新建一个资源文件db.properties,内容如下:

1
2
3
4
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/student?useUnicode=true&characterEncoding=utf-8
username=root
password=root

根据你自己的情况修改,数据库一般默认是3306端口,如果你可以修改过,那只需要修改username和password就好。

  • 改为自己的数据库账号密码

导入数据库

导入数据库是这样的,如图:

personal @student (DB) - 表20221114-174

新建实体类

在com包下新建一个名为entity包,在entity包下新建一个类名为Personal的实体类。

每一个属性都有对应数据库的字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.Entity;

public class Personal {
Integer id;
String name;
String height;
Integer age;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getHeight() {
return height;
}

public void setHeight(String height) {
this.height = height;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}
}

新建一个utils包

在com包下新建一个utils包,在utils包下新建一个名为SqlSessionFactoryUtils的类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class SqlSessionFactoryUtils {
private static SqlSessionFactory sqlSessionFactory;

static {
//静态代码块会随着类的加载而自动执行,且只执行一次

try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}

public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
}

编写接口

在com包下新建一个mapper包,在mapper包下新建一个PersonalMapper接口,我们需要写SQL语句时,在这接口写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.Mapper;

import com.Entity.Personal;
import com.Service.PersonalService;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface PersonalMapper {

/**
* 查询所有个人信息
* @return
* tips:因为是查询所有人的信息,会存在多个列表信息,所以应该使用列表来存储信息
*/
@Select("select * from personal")
List<Personal> findAll();

/**
* 增加个人信息
* @param personal
* @return 返回更新条数
*/
@Insert("insert into personal (id,name,height,age) value(#{id},#{name},#{height},#{age})")
int addPersonal(Personal personal);

/**
* 根据ID删除个人信息
* @param id
* @return 返回更新条数
*/
@Delete("delete from personal where id =#{id}")
int delPersonalById(@Param("id") Integer id);

/**
* 根据ID更新姓名
* @param id
* @param name
* @return
*/
@Update("update personal set name = #{name} where id = #{id}")
int updatePersonalById(@Param("name") String name, @Param("id") Integer id);

}

数据库操作实例

SQL语句写好了,就可以开始写实例了,对数据库进行增删改查了,我们将这部分的代码放在com包下的service包中。

在com下新建一个service包,并在service包新建一个PersonalService类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.service;

import com.entity.Personal;
import com.mapper.PersonalMapper;
import com.utils.SqlSessionFactoryUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import java.util.List;


public class PersonalService {
SqlSessionFactory factory = SqlSessionFactoryUtils.getSqlSessionFactory();

/**
* 查询全部个人信息
* @return
*/
public List<Personal> selectAll() {
SqlSession sqlSession = factory.openSession();
PersonalMapper mapper = sqlSession.getMapper(PersonalMapper.class);
List<Personal> personals = mapper.findAll();
sqlSession.close();
return personals;
}

/**
* 增加个人信息
* @param personal
* @return
*/
public int addPersonal(Personal personal){
SqlSession sqlSession = factory.openSession();
PersonalMapper mapper = sqlSession.getMapper(PersonalMapper.class);
//rows 受影响行数
int rows = mapper.addPersonal(personal);
sqlSession.commit();
return rows;
}

public int delPersonal(Integer id){
SqlSession sqlSession = factory.openSession();
PersonalMapper mapper = sqlSession.getMapper(PersonalMapper.class);
//rows 受影响行数
int rows = mapper.delPersonalById(id);
sqlSession.commit();
return rows;
}

public int updatePersonal(Integer id , String name){
SqlSession sqlSession = factory.openSession();
PersonalMapper mapper = sqlSession.getMapper(PersonalMapper.class);
//rows 受影响行数
int rows = mapper.updatePersonalById(name,id);
sqlSession.commit();
return rows;
}

}

测试实例

我们使用的是Junit5测试,在PersonalService类中,右键--->生成--->测试--->选择全部成员--->确定

生成测试后,在src/test/java/com.service/下可以看到这一个类PersonalServiceTest

复制以下已经写好的测试内容到你的类PersonalServiceTest中。

不会使用测试?

在测试类中,有很多个方法,方法名左边有个绿色的三角形,点击三角形就是开始测试,测试是否正常,看左下方的终端是绿色还是红色,绿色代表测试通过,红色代表测试不通过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.service;

import com.entity.Personal;
import org.junit.jupiter.api.Test;

import java.util.List;

class PersonalServiceTest {
PersonalService personalService = new PersonalService();
@Test
void selectAll() {
List<Personal> personals= personalService.selectAll();
System.out.println("-----------------");
for (Personal personal : personals) {
System.out.println("个人信息:"+personal.getName());
}
System.out.println("-----------------");
}

@Test
void addPersonal() {
Personal personal = new Personal();
personal.setId(7);
personal.setAge(19);
personal.setHeight("178");
personal.setName("小码博客");
int rows = personalService.addPersonal(personal);
System.out.println("受影响行数为:"+rows);
}

@Test
void delPersonal() {
int rows = personalService.delPersonal(5);
System.out.println("受影响行数为:"+rows);
}

@Test
void updatePersonal() {
int rows = personalService.updatePersonal(2,"blog.hikki.site");
System.out.println("受影响行数为:"+rows);
}
}

测试结果如下说明项目没有问题了,可以正常使用。

![01-测试-mybatis – PersonalSe20221126-421](D:/FileSave/图片素材/博客/计算机科学/编程语言/java/mybatis/01-测试-mybatis – PersonalSe20221126-421.png)

XMl映射器开发案例

这次使用XML映射器开发,不使用注解了。

MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。

项目使用方法

项目结构分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
├─java
└─com
├─entity # 用户数据库数据映射的实体类
Article.java
Tag.java
User.java

├─mapper # Dao层复制数据库的增删改查操作
ArticleDao.java
ArticleMapper.xml
UserDao.java
UserMapper.xml

└─utils # 工具类
MybatisUtils.java # mybatis的工具类
Othor.java

├─resources # 配置文件
db.properties
log4j.properties
mybatis-config.xml

└─webapp # 与本项目无关
index.jsp

└─WEB-INF
web.xml

连接数据库

将项目根目录discuss.sql文件导入你的数据库

默认数据库连接用户:root

默认数据库连接密码:root

根据你自己的情况修改用户名和密码

加载依赖

打开根目录的pom.xml文件,右键—>Maven—>重新加载项目

Junit测试项目

打开根目录下的src/test/java/com/mapper/ArticleDaoTest类

点击类名左边的绿色三角符号开始测试该类,查看下方的终端,全是绿色,没有看见红色,则说明测试结果通过,该项目可以正常运行

配置文件

使用XML映射器开发也要设置配置文件

1
2
3
4
├─resources
db.properties
log4j.properties
mybatis-config.xml

数据库连接配置db.properties:

我这里使用mariadb数据库,mariadb数据库的用法和mysql几乎一样,如果你没有安装mariadb,可以试着去安装一下,或者你直接将数据库文件导入mysql也是一样的,但可能需要改动一下文件内容。

1
2
3
4
driver=org.mariadb.jdbc.Driver
url=jdbc:mariadb://localhost:3307/discuss?useUnicode=true&characterEncoding=utf-8
username=root
password=root

mybatis配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 引入数据库连接配置文件 -->
<properties resource="db.properties" />

<settings>
<!--开启驼峰密命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 打开延迟加载的开关 -->
<!-- lazyLoadingEnabled:设置懒加载,默认为false。如果为false:则所有相关联的都会被初始化加载。 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 将积极加载改为消息加载,即按需加载 -->
<!-- aggressiveLazyLoading:默认为true。当设置为true时,懒加载的对象可能被任何懒属性全部加载;否则,每个属性按需加载。 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

<!--使用扫描包的形式定义别名 -->
<typeAliases>
<typeAlias type="com.entity.Article" alias="Article"/>
<typeAlias type="com.entity.Tag" alias="Tag"/>
<typeAlias type="com.entity.User" alias="User"/>
</typeAliases>
<!--配置环境 ,默认的环境id为mysql -->
<environments default="development">
<!-- 配置id为mysql的数据库环境 -->
<environment id="development">
<!-- 使用JDBC的事务管理 -->
<transactionManager type="JDBC" />
<!--数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
<!--配置Mapper的位置 -->
<mappers>
<mapper resource="com/mapper/ArticleMapper.xml" />
<mapper resource="com/mapper/UserMapper.xml" />
</mappers>
</configuration>

如果你的数据库有下划线"_"的话,你需要开启驼峰命名转换成大写,否则无法存储如实体类,

开启驼峰命名

1
2
3
4
<settings>
<!--开启驼峰密命名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

新建实体类

数据库的每一张表一般都有一个实体类对应,根据数据库,我们新建三个实体类ArticleTagUser,并且在每一个实体类添加数据表的字段属性,设置该属性为私有,并且生成get/set方法和toString方法。

比如Tag实体类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.entity;

public class Tag {
private String id;
private String tag;
@Override
public String toString() {
return "Tag{" +
"id='" + id + '\'' +
", tag='" + tag + '\'' +
'}';
}
public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getTag() {
return tag;
}

public void setTag(String tag) {
this.tag = tag;
}
}

新建mybatis工具类

每一个mybatis的应用都是基于SqlSessionFactory 的实例为核心的,SqlSessionFactory 的实例可以通过SqlSessionFactoryBuilder获得,而SqlSessionFactoryBuilder则可以通过mybatis的配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。

使用mybatis文件构建SqlSessionFactory 的实例很简单,直接使用Resources.getResourceAsReader()即可,默认读取的路径是/src/main/resources/下的文件。

1
2
3
4
//使用MyBatis提供的Resources类加载mybatis的配置文件
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
//构建sqlSession的工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

完整的获取SqlSessionFactory 实例的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.Reader;
public class MybatisUtils {
public static SqlSessionFactory sqlSessionFactory = null;
// 初始化SqlSessionFactory对象
static{
try {
//使用MyBatis提供的Resources类加载mybatis的配置文件
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
//构建sqlSession的工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取SqlSession对象的静态方法
public static SqlSession getSession(){
return sqlSessionFactory.openSession();
}
}

新建Mapper

我们写的SQL语句存放在一个XML文件内,这个文件我们可以叫他为映射器。

XML的使用格式类似HTML,有单标签和双标签,比如我查询一个tb_article表的数据条数,使用<select></select>括住SQL语句。其中在每一个SQL语句都有一个id属性,这个属性就是为了从众多的SQL定位到某一条语句,resultType属性是表示结果返回类型。

1
2
3
4
<select id="getCount" resultType="int">
select count(*)
from tb_article
</select>

常见的属性

  • id:(必选)在命名空间中唯一的标识符,可以被用来引用这条语句。
  • parameterType:(可选)参数传入的类型
  • resultType:(可选)结果传出的参数类型,该属性和resultMap只能同时使用一个。
  • resultMap:(可选)对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性, resultType 和 resultMap 之间只能同时使用一个。

增删改查

我们一般在实际应用上不使用删除标签,在表中定义一个状态,更改状态来达到删除数据。

每一段SQL语句都有一个id属性,至于是否有parameterType属性,根据自己的需求来是否添加,如果你需要在SQL语句中传入某参数,那就添加啊parameterType,不需要就不添加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--    根据abbrlink查询文章,进行文章内容修改 -->
<select id="findArticleByAbbrlink" parameterType="String" resultType="Article">
select ta.id,ta.abbrlink,ta.title ,ta.context ,ta.create_time ,ta.wordcount ,ta.comment ,ta.number_reads ,tt.tag
from tb_article ta
inner join tb_tag tt on tt.id = ta.tag
where ta.abbrlink = #{abbrlink}
</select>

<!-- 添加文章 直接发布 -->
<insert id="addArticle" parameterType="Article">
insert into tb_article (abbrlink,title,tag,context,create_time,user,wordcount,state)
values (#{abbrlink},#{title},#{tag},#{context},#{create_time},"1",#{wordcount},#{state})
</insert>

<!-- 更新文章状态 -->
<update id="updateArticleState" parameterType="Article">
update tb_article set state =#{state} where abbrlink= #{abbrlink}
</update>

动态SQL语句

我们可以根据Article实体类下的abbrlink、title、context属性值是否为空来进行对应的模糊查询,下面的标签表示只从标签下执行一个条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="findArticleAsURLAsTitleAsContext" parameterType="Article" resultType="Article">
select * from tb_article ta
where 1=1 and
<choose>
<when test="abbrlink!=null and abbrlink!=''">
ta.abbrlink like concat('%',#{abbrlink},'%')
</when>
<when test="title!=null and title!=''">
ta.title like concat('%',#{title},'%')
</when>
<when test="context!=null and context!=''">
ta.context like concat('%',#{context},'%')
</when>
</choose>
</select>

我们还可以添加数组来遍历查询,比如,我们定义一个ArrayList,里面存放的是abbrlink的值,然后使用 <foreach>标签进行遍历查询,结果集使用Article接收

1
2
3
4
5
6
7
<!-- 使用动态SQL中<foreach>元素实现根据一组URL查询文章信息 -->
<select id="findArticleByAbbrlinks" parameterType="List" resultType="Article">
select * from tb_article where abbrlink in
<foreach item="abbrlink" index="index" collection="list" open="(" separator="," close=")">
#{abbrlink}
</foreach>
</select>

Mybatis-Plus

前言

Mybatis-Plus和Mybatis基本一样,开发步骤一样,下面我给出一个简单的案例来解释说明,不过,案例内容和上面不一样。

小技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 新建文章
* @param articleDTO 文章内容
*/
@Override
public void addArticle(ArticleDTO articleDTO) {
System.out.println("debug--addArticle--articleDTO:"+articleDTO);
//TODO:获取标签,判断是否存在,不存在则新添加标签
List<Integer> tagIdList = tagService.addTag(articleDTO.getTagList());

//TODO:获取分类,判断是否存在,不存在则新添加分类
Integer categoryId = categoryService.addCategory(articleDTO.getCategoryName());

//TODO:给文章设置默认值(如缩略图、置顶、删除、推荐、状态)
// 其中置顶、删除、推荐、状态在数据库均设有默认值,可不填写
// 该部分内容需要读取 t_site_config 数据库内容再设置,这里可以做一层缓存加快速度,此时先暂时做个虚拟数据
Article article = BeanCopyUtil.copyBean(articleDTO, Article.class);
article.setCategoryId(categoryId);
article.setArticleCover("https://lsky.hikki.site/thumbnails/9e54d99bfd6587c219627297e0d7749b.png");
// TODO:文章的和标签是一对多的关系,因此文章表不需要写入标签ID,使用关联表来写入记录
// 添加文章
articleMapper.insert(article);
System.out.println("debug--addArticle--article.getID:"+article.getId());
}