March 2, 2023

MyBatis

前言

终于开始学Mybatis了,其实是javaweb跳的很快,因为air师傅和其他师傅都说过jsp老化了,只需要学习一些原理即可,没必要深究,所以就直接来这了

什么是Mybatis

如果获取mybatis

一句话简单来说,Mybatis就是持久层的框架,也就是和数据库相关的框架

第一个Mybatis程序

环境搭建->导入Mybatis->编写代码->测试

环境搭建

由于和数据库有关,我们就要搭建一个MYSQL数据库
先创建一个数据库:
image.png
新建一个普通的maven项目:

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"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--父工程-->
<groupId>org.example</groupId>
<artifactId>Mybatis</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!--导入依赖-->
<dependencies>
<!--mysql驱动-->
<!--mybatis-->
<!--JUNIT-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
</dependency>
</dependencies>
</project>

记得把src文件夹删了,作为父工程

创建一个模块(maven项目)

image.png
这样每次子项目就不需要导包,方便很多
配置mybatis的核心配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"></property>
<property name="username" value="root"></property>
<property name="password" value="jiayou357753"></property>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/kino/dao/UserDao.xml"/>
</mappers>
</configuration>

其中<environment id="development">表示环境的ID,可以有多个测试环境,然后上面一个标签有 <environments default="development">就是默认的测试环境

<property name="url" value="${jdbc.url}"></property>
是我们jdbc的url:jdbc:mysql://localhost:3306
image.png

1
2
3
4
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"></property>
<property name="username" value="root"></property>
<property name="password" value="jiayou357753"></property>

URL一栏多了一些参数,分别是安全连接和编码

从 XML 中构建 SqlSessionFactory

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。 但也可以使用任意的输入流(InputStream)实例,比如用文件路径字符串或 file:// URL 构造的输入流。MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,使得从类路径或其它位置加载资源文件更加容易。

1
2
3
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

从 SqlSessionFactory 中获取 SqlSession

既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。例如:

1
2
3
try (SqlSession session = sqlSessionFactory.openSession()) {
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}

诚然,这种方式能够正常工作,对使用旧版本 MyBatis 的用户来说也比较熟悉。但现在有了一种更简洁的方式——使用和指定语句的参数和返回值相匹配的接口(比如 BlogMapper.class),现在你的代码不仅更清晰,更加类型安全,还不用担心可能出错的字符串字面值以及强制类型转换。
例如:

1
2
3
4
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
}

编写一个工具类

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
package com.Boogipop.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.IOException;
import java.io.InputStream;

public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory; //提升作用域
static {
try {
//获取sqlsessionFactory对象 这一步是必须的
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}

}
//既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
public static SqlSession getSqlsession(){
return sqlSessionFactory.openSession();
//sqlsession包含了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
package com.Boogipop.pojo;

public class User {
private int id;
private String name;
private String pwd;

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

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

public void setPwd(String pwd) {
this.pwd = pwd;
}

public int getId() {
return id;
}

public String getName() {
return name;
}

public String getPwd() {
return pwd;
}

public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
}

Dao类(操作实体类的接口)

1
2
3
4
5
6
7
8
9
10
package com.Boogipop.dao;

import com.Boogipop.pojo.User;

import java.util.List;

public interface UserDao {
List<User> getuserlist();
}

接口实现类

这里就省去了JDBC对结果集的处理的繁琐过程了,体现Mybatis的优势
由原来的implement改为了一个mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.Boogipop.dao;

import com.Boogipop.pojo.User;

import java.util.List;

public class UserDaoImp implements UserDao{

@Override
public List<User> getuserlist() {
String sql=""
}
}

上面是原来的,下面是mybatis的处理方法

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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/>
<!--使我们的Dao和xml发生关系-->
<mapper namespace="com.Boogipop.dao.UserDao">
<!--在下面写sql语句-->
<select id="getuserlist" resultType="com.Boogipop.pojo.User">
-- resulttype是返回结果类型
select * from User
</select>
</mapper>

napespace就是Dao接口,resultType就是定义的要查询的sql对象类
这边select的id切记和之前的dao(mapper)接口的方法对应!

写一个测试类

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.Boogipop.dao;

import com.Boogipop.pojo.User;
import com.Boogipop.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserDaotest {
@Test
public void test(){
//获取工具类,sqlsession
SqlSession sqlsession = MybatisUtil.getSqlsession();
//执行sql
UserDao mapper = sqlsession.getMapper(UserDao.class);//获取mapper
List<User> getuserlist = mapper.getuserlist();
for (User user : getuserlist) {
System.out.println(user);
}
//关闭sqlsession
sqlsession.close();
}
}

运行过后会报错:
image.png
因为在刚开始创建mybatis-config.xml中没注册mapper:
image.png
之后又会报错初始化异常:
image.png
这是当时在Maven里讲的,由于maven约定大于配置,所以会产生配置文件无法生效的问题,解决方案就是在pom.xml(主工程和子工程)里添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>

错误总结(续上面)

然后还会有几个几把错误,希望不要再犯,首先一定要检查你的mybatis-config.xml是否配置正确,其次就是上面说的maven约定大于配置的问题(还有其他解决方法,例如直接把xml配置文件放到resource文件夹,因为resource文件夹是默认配置目录)
最后是弃用方法要省略
<property name="driver" value="com.mysql.cj.jdbc.Driver"></property>
这是mybatis-config.xml配置文件中的一句话,之前是没有cj的,最后也是可以如愿得到运行结果(呼!)
image.png
给折磨坏了

测试类里执行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
package com.kino.dao;

import com.kino.pojo.User;
import com.kino.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserDaotest {
@Test
public void test(){
//获取工具类,sqlsession
SqlSession sqlsession = MybatisUtil.getSqlsession();
//执行sql
UserDao mapper = sqlsession.getMapper(UserDao.class);//获取mapper
List<User> list = sqlsession.selectList("com.kino.dao.UserDao.getuserlist");
for (User user : list) {
System.out.println(user);
}
//关闭sqlsession
sqlsession.close();
}
}

他奶奶的第一集一个小时,我真的是日了狗了,之后再消化一下

增删改查实现(CURD)

namespace(命名空间)

namespace中的包名要和接口的包名一致
image.png
上面我用的是UserDao,但这次我改了个名字UserMapper:
image.png

select

查询语句嘛,我们在写UserMapper.xml的时候有个select标签,里面有几个属性

parameterType怎么用,我们写一个根据id返回结果的小Demo来学习

1
User getuserbyid(int id);

在这一步定义了一个接口有参方法,里面传入的形参就是我们的id

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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.kino.dao.UserMapper">
<select id="getuserlist" resultType="com.kino.pojo.User">
select * from user
</select>
<!--补充一个getuserbyid的select标签-->
<select id="getuserbyid" resultType="com.kino.pojo.User" parameterType="int">
select * from user where id=#{id}
</select>
</mapper>

Mapper.xml也添加相应的select标签和语句,这里的select标签的parameterType就是int,然后#{id}就表示我们自定义的id,这是一个语法,用#{}去包裹我们需要传入的参数,这里的#{id}看起来和上面的形参id名称一样,实际上可以不一样,你可以#{id2}、#{number}等等,他也算一个形参
最后再写一个测试方法:

1
2
3
4
5
6
7
8
9
@Test
public void getuserbyid(){
SqlSession sqlSession=MybatisUtil.getSqlsession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getuserbyid(1);
System.out.println(user);

sqlSession.close();
}

image.png

insert

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.kino.dao;

import com.kino.pojo.User;

import java.util.List;

public interface UserMapper {
List<User> getuserlist();
//根据ID返回数据
User getuserbyid(int id);
//添加用户
Boolean adduser(User user);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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.kino.dao.UserMapper">
<select id="getuserlist" resultType="com.kino.pojo.User">
select * from user
</select>
<!--补充一个getuserbyid的select标签-->
<select id="getuserbyid" resultType="com.kino.pojo.User" parameterType="int">
select * from user where id=#{id}
</select>
<insert id="adduser" parameterType="com.kino.pojo.User">
insert into mybatis.user(id,name,pwd) values(#{id},#{name},#{pwd});
</insert>
</mapper>

语法后的分号可有可不有

1
2
3
4
5
6
7
8
9
10
public void adduser(){
SqlSession sqlSession=MybatisUtil.getSqlsession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Boolean adduser = mapper.adduser(new User(3, "Boogipop2", "2662501"));
if (adduser){
System.out.println("添加成功");
}
sqlSession.commit(); //提交事务,是必须的,否则不会添加
sqlSession.close();
}

image.png
添加成功了

delete

和insert也是一个思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.kino.dao;

import com.kino.pojo.User;

import java.util.List;

public interface UserMapper {
List<User> getuserlist();
//根据ID返回数据
User getuserbyid(int id);
//添加用户
Boolean adduser(User user);
//删除用户
Boolean deluser(int id);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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.kino.dao.UserMapper">
<select id="getuserlist" resultType="com.kino.pojo.User">
select * from user
</select>
<!--补充一个getuserbyid的select标签-->
<select id="getuserbyid" resultType="com.kino.pojo.User" parameterType="int">
select * from user where id=#{id}
</select>
<insert id="adduser" parameterType="com.kino.pojo.User">
insert into mybatis.user(id,name,pwd) values(#{id},#{name},#{pwd});
</insert>
<delete id="deluser" parameterType="int">
delete from user where id=#{id}
</delete>
</mapper>

1
2
3
4
5
6
7
8
9
10
11
@Test
public void deluser(){
SqlSession sqlSession=MybatisUtil.getSqlsession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Boolean deluser=mapper.deluser(3);
if(deluser){
System.out.println("删除成功");
}
sqlSession.commit();//提交事务
sqlSession.close();
}

image.png,最后也是删除成功了

update

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.kino.dao;

import com.kino.pojo.User;

import java.util.List;

public interface UserMapper {
List<User> getuserlist();
//根据ID返回数据
User getuserbyid(int id);
//添加用户
Boolean adduser(User user);
//删除用户
Boolean deluser(int id);
//更新用户
Boolean cguser(User user);
}

1
2
3
<update id="cguser" parameterType="com.kino.pojo.User">
update user set name=#{name},pwd=#{pwd} where id=#{id}
</update>

这里#{}中的内容一定要和类中定义的名字一样

1
2
3
4
5
6
7
8
9
10
11
@Test
public void cguser(){
SqlSession sqlSession=MybatisUtil.getSqlsession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Boolean cguser=mapper.cguser(new User(1,"Kino2!","asdasdasd"));
if(cguser){
System.out.println("更新成功");
}
sqlSession.commit();//提交事务
sqlSession.close();
}

image.png
也是修改成功

错误排查

一句话说就是自己排查错误

Map和模糊查询

细心一点的人就发现了,假如每次参数类型都是User,咱们就不好去指定需要插入的数据或者是删除的数据。Map集合就可以很好解决这一点问题
接下来就写个小demo,用Map来添加数据

1
Boolean mapadduser(Map<String,Object> map);
1
2
3
<insert id="mapadduser" parameterType="Map">
insert into user(id,name) values(#{id},#{name})
</insert>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void mapadduser(){
SqlSession sqlSession=MybatisUtil.getSqlsession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> map=new HashMap<>();
map.put("id","3");
map.put("name","asdas5da65s");
Boolean mapadduser = mapper.mapadduser(map);
if(mapadduser){
System.out.println("添加成功");
}
sqlSession.commit();
sqlSession.close();

}

image.png
添加成功了,这边Map的键要和我们的字段名称一一对应,否则会出现为null的情况

接下来就是如何使用mybatis进行模糊查询,也就是like语句的使用,并且不留SQL注入隐患

1
User getuserbylike(Map<String,Object> map);
1
2
3
<select id="getuserbylike" parameterType="Map" resultType="com.kino.pojo.User">
select * from user where name like concat("%",#{name},"%")
</select>
1
2
3
4
5
6
7
8
9
10
@Test
public void selectbylike(){
SqlSession sqlSession=MybatisUtil.getSqlsession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String,Object> hashMap=new HashMap<>();
hashMap.put("name","B");
User getuserbylike = mapper.getuserbylike(hashMap);
System.out.println(getuserbylike);
sqlSession.close();
}
![image.png](https://cdn.nlark.com/yuque/0/2023/png/32634994/1672928237047-9dd461d5-67b8-417c-88db-fdcb1bfe39e1.png#averageHue=%238a7861&clientId=uf2c195bd-4fd7-4&from=paste&height=101&id=uf3a90cc7&name=image.png&originHeight=126&originWidth=565&originalType=binary&ratio=1&rotation=0&showTitle=false&size=13708&status=done&style=none&taskId=u3b110d33-fff4-4127-9813-a409c5f5a45&title=&width=452)

注意XML中我用了concat,这样可以从一定程度避免sql注入

配置优化——属性

image.png
mybatis核心配置文件如上,我们environment和mappers我们都用过了,这里介绍一下属性properties
首先先回顾一下environments:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"></property>
<property name="username" value="root"></property>
<property name="password" value="jiayou357753"></property>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"></property>
<property name="username" value="root"></property>
<property name="password" value="jiayou357753"></property>
</dataSource>
</environment>
</environments>

上面部分核心配置文件中,有2个environment,我们假如想要切换,只需修改default标签为对应的id即可

在mybatis中自带3种内置数据源类型JNDI,UNPOOLED,POOLED,POOL也就是池的意思,代表能否复用的意思(用完了换个进程继续用),UNPOOLED也就是没有池的概念,因此性能要求不高
mybatis可以配置成适应多种环境,但是SqlsessionFactory只能用一种
mybatis的默认事务处理器是JDBC,连接池是POOLED

properties属性

可以用来引入.properties配置文件,也可以在标签中自己添加属性和数值

1
2
3
4
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&seUnicode=true&characterEncoding=UTF-8
username=root
password=jiayou357753
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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">
<property name="useless" value="useless"/>
</properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/kino/dao/UserMapper.xml"/>
</mappers>
</configuration>

${}:和#{}一样也是一种占位符

别名配置优化

方案一

在mybatis-config.xml有个tag叫做<typeAliases>,它用来对我们的类设置别名,比如我们再Usermapper.xml中,设置resultType每次都是com.kino.pojo.User
每次都要写这么长就很繁琐,因此可以设置别名:

1
2
3
<typeAliases>
<typeAlias type="com.kino.pojo.User" alias="User"/>
</typeAliases>

mybatis核心配置文件内,这些标签有顺序的,这个需要写在properties标签下,这里需要注意
image.png
标签顺序如上
配置过后就可以把UserMapper.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
<?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.kino.dao.UserMapper">
<select id="getuserlist" resultType="com.kino.pojo.User">
select * from user
</select>
<!--补充一个getuserbyid的select标签-->
<select id="getuserbyid" resultType="User" parameterType="int">
select * from user where id=#{id}
</select>
<insert id="adduser" parameterType="User">
insert into mybatis.user(id,name,pwd) values(#{id},#{name},#{pwd});
</insert>
<delete id="deluser" parameterType="int">
delete from user where id=#{id}
</delete>
<update id="cguser" parameterType="User">
update user set name=#{name},pwd=#{pwd} where id=#{id}
</update>
<insert id="mapadduser" parameterType="Map">
insert into user(id,name) values(#{id},#{name})
</insert>
<select id="getuserbylike" parameterType="Map" resultType="User">
select * from user where name like concat("%",#{name},"%")
</select>
</mapper>

运行test类看看结果是否正常:
image.png
结果正常说明配置生效

方案二

还有一种写法

1
2
3
<typeAliases>
<package name="com.kino.pojo"/>
</typeAliases>

这样的写法默认会讲com.kino.pojo下的类别名设置为类名的小写形式,如User的别名就是user
当然我们可以通过注解自定义别名:
image.png
测试一下是否正常运行
image.png
运行正常

这两种的适用的场合也就是你需要别名的类是否很多,如果很多的话就用方案二的package,很少就用方案一吧,我个人是倾向于方案二的

映射器配置

映射器已经见得多了,就是mybatis核心配置文件中注册我们mapper的标签
它有如下三种写法:

1
2
3
4
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="com/kino/dao/UserMapper.xml"/>
</mappers>
1
2
3
4
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="com.kino.dao.UserMapper"/>
</mappers>
1
2
3
4
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers>
1
2
3
4
<!-- 将包内的映射器接口全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>

以上四种用法结合实际情况去使用

作用域(Scope)和生命周期

SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。(就如我们这次项目使用的就是单例模式)

SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:

1
2
3
try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
}

可以将SqlSessionFactory理解为一个线程池,然后SqlSession用来连接线程池,因此当SqlSession结束了事务后,要关闭SqlSession避免占用

ResultMap结果集映射

我们如何利用ResultMap结果映射集去解决pojo中类属性名称和数据库中字段名称不一致的问题呢,针对这个问题其实有两种解法,分别从数据库和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
46
47
48
package com.kino.pojo;

import org.apache.ibatis.type.Alias;

@Alias("User")
public class User {
private int id;
private String name;
private String password;

@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}

public void setId(int id) {

this.id = id;
}

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

public void setPassword(String password) {
this.password = password;
}

public int getId() {
return id;
}

public String getName() {
return name;
}

public String getPassword() {
return password;
}

public User(){
}
}

注意这里我用了无参构造方法啊,这里的坑后面会讲
image.png
image.png
这样查出来的password为空

方案1——使用as别名

select * from user改为select id,name,pwd as password from user即可
image.png

方案2(推荐)——使用ResultMap映射

image.png
这是官方文档的用法,但是看官方文档总是会有种不说人话的感觉
我们将我们的UserMapper文件改为:

1
2
3
4
5
6
7
<resultMap id="Anything" type="User">
<result property="password" column="pwd"/>
</resultMap>
<!--补充一个getuserbyid的select标签-->
<select id="getuserbyid" resultMap="Anything" parameterType="int">
select * from user where id=#{id}
</select>

将结果设为resultMap,名称自定义,但是要与上面的resultMap标签的id属性对应,这就是设置一个映射关系,同样的结果也能显示出来
image.png

填坑

回来填坑,为什么要用无参的构造方法呢,因为经过我的测试,假如你的构造器是有参构造器,不管你字段名和属性对不对应结果都会返回结果
这边猜测是mybatis处理结果时,会优先使用有参构造函数去返回结果的User类,这就是一个自动对应
假如用无参构造方法的话,就只能更改属性了,因此就没有对应关系
纯属个人猜测

日志工厂

在mybatis中由于简化jdbc事务,因此不能直接使用out去输出运行的Sql语句,由此日志工厂诞生了:
image.png
mybatis中默认不开启日志事务,因此需要手动添加配置

默认日志

1
2
3
4
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 系统默认控制台日志输出 -->
</settings>

配置好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
38
39
40
41
42
43
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
Class not found: org.jboss.vfs.VFS
JBoss 6 VFS API is not available in this environment.
Class not found: org.jboss.vfs.VirtualFile
VFS implementation org.apache.ibatis.io.JBoss6VFS is not valid in this environment.
Using VFS adapter org.apache.ibatis.io.DefaultVFS
Find JAR URL: file:/E:/CTF%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0/Mybatis/Mybatis/mabtis-01/target/classes/com/kino/pojo
Not a JAR: file:/E:/CTF%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0/Mybatis/Mybatis/mabtis-01/target/classes/com/kino/pojo
Reader entry: User.class
Listing file:/E:/CTF%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0/Mybatis/Mybatis/mabtis-01/target/classes/com/kino/pojo
Find JAR URL: file:/E:/CTF%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0/Mybatis/Mybatis/mabtis-01/target/classes/com/kino/pojo/User.class
Not a JAR: file:/E:/CTF%e5%ad%a6%e4%b9%a0%e7%ac%94%e8%ae%b0/Mybatis/Mybatis/mabtis-01/target/classes/com/kino/pojo/User.class
Reader entry: ���� 4 @ -
Checking to see if class com.kino.pojo.User matches criteria [is assignable to Object]
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 1380806038.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@524d6d96]
==> Preparing: select * from user
==> Parameters:
<== Columns: id, name, pwd
<== Row: 1, Kino!, asdasdasd
<== Row: 2, boogipop, admin
<== Row: 3, Boogipop2, 2662501
<== Row: 4, Boogipop4, 2662501
<== Row: 4, Boogipop4, 2662501
<== Row: 4, Boogipop4, 2662501
<== Total: 6
User{id=1, name='Kino!', pwd='asdasdasd'}
User{id=2, name='boogipop', pwd='admin'}
User{id=3, name='Boogipop2', pwd='2662501'}
User{id=4, name='Boogipop4', pwd='2662501'}
User{id=4, name='Boogipop4', pwd='2662501'}
User{id=4, name='Boogipop4', pwd='2662501'}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@524d6d96]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@524d6d96]
Returned connection 1380806038 to pool.

Process finished with exit code 0

我们的SQL执行语句已经在上面给显示出来了

LOG4J2

https://www.jianshu.com/p/c6c543e4975e
配置文件之类的参考如上:
LOG4J漏洞百出,现在用的都是2
先安装Maven依赖:

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>

之后再mybatis核心配置文件中的settings设置一下logImpl的种类为LOG4J2:

1
2
3
<settings>
<setting name="logImpl" value="LOG4J2"/>
</settings>

之后只需要设置一下log4j2的xml配置文件即可:
image.png

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
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="TRACE">
<!-- 配置日志信息输出目的地 -->
<Appenders>
<!-- 输出到控制台 -->
<Console name="Console" target="SYSTEM_OUT">
<!--配置日志信息的格式 -->
<PatternLayout
pattern="%d{HH:mm:ss} [%t] %-5level %logger{36} - %msg%n" />
"/>
</Console>

<!-- 输出到文件,其中有一个append属性,默认为true,即不清空该文件原来的信息,采用添加的方式,若设为false,则会先清空原来的信息,再添加 -->
<File name="MyFile" fileName="./logs/info.log" append="true">
<PatternLayout>
<!--配置日志信息的格式 -->
<pattern>%d{HH:mm:ss} [%t] %-5level %logger{36} - %msg%n</pattern>
</PatternLayout>
</File>

</Appenders>
<!-- 定义logger,只有定义了logger并引入了appender,appender才会有效 -->
<Loggers>
<!-- 将业务dao接口所在的包填写进去,并用在控制台和文件中输出 -->
<logger name="com.kino.dao.UserMapper" level="TRACE"
additivity="false">
<AppenderRef ref="Console" />
<AppenderRef ref="MyFile" />
</logger>

<Root level="info">
<AppenderRef ref="Console" />
<AppenderRef ref="MyFile" />
</Root>
</Loggers>
</Configuration>

image.png
这样日志文件就会在控制台和文件中输出

然后同时我们也可以在我们的测试类中使用LOG4J2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static Logger logger=LogManager.getLogger();
@Test
public void test(){
//获取工具类,sqlsession
SqlSession sqlsession = MybatisUtil.getSqlsession();
//执行sql
UserMapper mapper = sqlsession.getMapper(UserMapper.class);//获取mapper
logger.info("=====info信息开始=====");
List<User> getuserlist = mapper.getuserlist();
for (User user : getuserlist) {
System.out.println(user);
}
sqlsession.close();
}

image.png
image.png
用法大概就这样拉QWQ

Limit实现分页

使用limit

就普通SQL的limit语法

使用RowBounds

image.png
大概就如上的用法,通过代码层面来实现分页,其实没啥东西

使用注解进行开发

上述我们进行的操作都是通过接口和xml文件进行的,现在来说一下如何直接利用注解来执行SQL语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.boogipop.dao;

import com.boogipop.pojo.User;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface UserMapper {
@Select("select * from user")
List<User> getuserlist();
//根据ID返回数据
}

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.boogipop.dao;

import com.boogipop.pojo.User;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface UserMapper {
@Select("select * from user")
List<User> getuserlist();
//根据ID返回数据
}

1
2
3
<mappers>
<mapper class="com.boogipop.dao.UserMapper"/>
</mappers>

核心配置文件中关于mappers的映射就要改为class了
image.png
结果是能正常输出,但是password为null,这是因为字段和属性名称不对应,所以假如用注解去执行SQL的时候就切记一定要对应起来,否则就null了
image.png
下个断点调试其实可以发现mapper是后续是通过反射获取的

增删改查

我们同样也可以通过注解来实现增删改查

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

import com.boogipop.pojo.User;
import org.apache.ibatis.annotations.*;

import java.util.HashSet;
import java.util.List;

public interface UserMapper {
@Select("select * from user")
List<User> getuserlist();
@Insert("insert into user(id,name,pwd) values(#{id},#{name},#{password})")
Boolean adduser(User user);

@Update("update user set name=#{name},pwd=#{password} where id=#{id}")
Boolean updateuser(User user);
@Delete("delete from user where id=#{id}")
//此处注解中的id和param中的名字对应
Boolean deleteuser(@Param("id") int id);

@Select("select * from user where name=#{name}")
HashSet<User> getuserbyname(@Param("name") String name);
}

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
60
61
62
63
64
65
66
67
68
package com.boogipop.dao;

import com.boogipop.pojo.User;
import com.boogipop.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.HashSet;
import java.util.List;

public class UserDaotest {
@Test
public void select(){
//获取工具类,sqlsession
SqlSession sqlsession = MybatisUtil.getSqlsession();
//执行sql
UserMapper mapper = sqlsession.getMapper(UserMapper.class);//获取mapper
List<User> getuserlist = mapper.getuserlist();
for (User user : getuserlist) {
System.out.println(user);
}
sqlsession.close();
}
@Test
public void adduser(){
SqlSession sqlsession = MybatisUtil.getSqlsession();
//执行sql
UserMapper mapper = sqlsession.getMapper(UserMapper.class);//获取mapper
User user=new User(5,"ass","123");
Boolean adduser = mapper.adduser(user);
if (adduser){
System.out.println("Add successfully!");
}
sqlsession.close();
}
@Test
public void updateuse(){
SqlSession sqlSession=MybatisUtil.getSqlsession();
UserMapper mapper=sqlSession.getMapper(UserMapper.class);
User user=new User(3,"Jack","sada123sdd2!");
Boolean updateuser = mapper.updateuser(user);
if(updateuser){
System.out.println("update successfully");
}
sqlSession.close();
}
@Test
public void deleteuser(){
SqlSession sqlSession=MybatisUtil.getSqlsession();
UserMapper mapper=sqlSession.getMapper(UserMapper.class);
Boolean deleteuser = mapper.deleteuser(4);
if(deleteuser){
System.out.println("delete successfully");
}
sqlSession.close();
}
@Test
public void getbyid(){
SqlSession sqlSession=MybatisUtil.getSqlsession();
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
HashSet<User> boogipop = userMapper.getuserbyname("boogipop");
for (User user : boogipop) {
System.out.println(user);
}
sqlSession.close();
}
}

当参数为基本类型时需要用param去强调,是引用类型的时候就免了,另外注意param参数对应的问题
然后这里大家可能发现我没提交事务,因为我改了一下工具类:
image.png
我传了一个ture进去就代表会自动提交,默认为false

mybatis执行流程

这个我们已经有了一个总体的认知,mybatis其实就是对底层jdbc的一个包装
不细讲
SqlSessionFactoryBuilder->inputStream->得到xml配置->sqlSessionFactory->transaction事务管理->excutor执行器->得到Sqlsession
image.png

Lombok简化

啥是lombok呢,我们创建一个类可以用alt+insert去添加一些常用方法
而lombok直接就更加简化了这些步骤,首先在settings里的plugins中安装lombok插件,然后再导入maven依赖:
image.png

1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
</dependencies>

image.png

image.png

包含所有参数的构造方法

无参构造方法

这些注解会自动帮你添加这些方法,就挺方便

复杂查询模式环境搭建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, 'Boogipop');

CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', 'Kino', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', 'Jack', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', 'Benk', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', 'Siri', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', 'Benk', '1');

首先先在本地SQL创建一下表,这里其实对KEY这个概念有点生疏,PRIMARY KEY表示主键,FOREIGN KEY表示外键,用来把本表字段和其他表字段做一个约束:
https://www.jianshu.com/p/010106e61cc6
在这里对student表创建了主键id,不准有重复的列、不能有NULL值,每个表只有一个主键
image.pngimage.png
这样表就构造好了,接下来要做的是在Mybatis中去处理这种关联关系,在这里一个老师对应多个学生
先新建一个mybatis子项目,然后把pojo和dao啥的翻一番:

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
<?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">
<property name="useless" value="useless"/>
</properties>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<package name="com.mybatis3.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="StudentMapper.xml"/>
</mappers>
</configuration>
1
2
3
4
5
6
7
8
9
<?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.mybatis3.dao.StudentMapper">
<select id="getstudentlist" resultType="Student">
select * from student
</select>
</mapper>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.mybatis3.pojo;

import lombok.Data;
import org.apache.ibatis.type.Alias;

@Data
@Alias("Student")
public class Student {
private int id;
private String name;
private int tid;
private Teacher teacher;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import com.mybatis3.dao.StudentMapper;
import com.mybatis3.pojo.Student;
import com.mybatis3.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class Excute {
@Test
public void getstudent(){
SqlSession sqlSession= MybatisUtil.getSqlsession();
StudentMapper mapper=sqlSession.getMapper(StudentMapper.class);
List<Student> getstudentlist = mapper.getstudentlist();
for (Student student : getstudentlist) {
System.out.println(student);
}
sqlSession.close();
}
}

1
2
3
4
5
6
7
8
9
10
package com.mybatis3.dao;

import com.mybatis3.pojo.Student;

import java.util.List;

public interface StudentMapper {
List<Student> getstudentlist();
}

这样就可以正常得到我们的测试结果:
image.png
因为返回值里没teacher所以为false
然后由于我们设置了主键,所以现在就有个非常有意思是的现象:
image.png
主键不能重复
而且我们添加数据的时候会自动帮我们加上id:
image.pngimage.png
挺方便的,所以在创建SQL的时候记得加个主键约束

多对一处理

现在我们要完成一个需求,搜索学生的时候,顺便把和tid绑定的teacher的信息一起输出出来。

查询进行嵌套

1
List<Student> getteacherlist();
1
2
3
4
5
6
<select id="selectall" resultMap="all">
select * from student
</select>
<resultMap id="all" type="Student">
<association property="teacher" column="tid" javaType="Teacher" select="getteacherlist"/>
</resultMap>
1
2
3
4
5
6
7
8
9
public void selectall(){
SqlSession sqlSession= MybatisUtil.getSqlsession();
StudentMapper mapper=sqlSession.getMapper(StudentMapper.class);
List<Student> selectall = mapper.selectall();
for (Student student : selectall) {
System.out.println(student);
}
sqlSession.close();
}

image.png
可以看到teacher也跟着被输出了,这里的意思其实是tid和getteacherlist接口绑定了

结果进行嵌套

1
2
3
4
5
6
7
8
9
10
11
<select id="selectall2" resultMap="all2">
select s.id sid,s.name sname,s.tid tid,t.name tname from student s join teacher t on s.tid=t.id
</select>
<resultMap id="all2" type="Student">
<result property="tid" column="tid"/>
<result property="name" column="sname"/>
<result column="sid" property="id"/>
<association property="teacher" javaType="Teacher">
<result column="tname" property="name"/>
</association>
</resultMap>
1
2
3
4
5
6
7
8
9
public void selectall2(){
SqlSession sqlSession= MybatisUtil.getSqlsession();
StudentMapper mapper=sqlSession.getMapper(StudentMapper.class);
List<Student> selectall = mapper.selectall2();
for (Student student : selectall) {
System.out.println(student);
}
sqlSession.close();
}

image.png
teacher中的id为0是因为我们没有选择t.id

一对多处理

昨天讲了多对一,今天将一对多,多对一使用了association标签,而一对多使用collenction标签,都是对应起来的
在实验之前先建一个新的mybatis项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.mybatis3.pojo;

import lombok.Data;
import org.apache.ibatis.type.Alias;

import java.util.List;

@Data
@Alias("Teacher")
public class Teacher {
private int id;
private String name;
List<Student> students;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.mybatis3.pojo;

import lombok.Data;
import org.apache.ibatis.type.Alias;

@Data
@Alias("Student")
public class Student {
private int id;
private String name;
private int tid;
}

这次两个pojo类反过来了,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
<?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.mybatis3.dao.StudentMapper">
<select id="getstudentlist" resultType="Student">
select * from student where tid=#{tid}
</select>
<insert id="add" parameterType="Student">
insert into student(id,name) values (#{id},#{name})
</insert>
<select id="getteacherlist" resultType="Teacher">
select * from teacher
</select>
<!--按照查询嵌套处理-->
<select id="selectall" resultMap="all">
select * from teacher
</select>
<resultMap id="all" type="Teacher">
<result property="id" column="id"/>
<collection property="students" ofType="Student" column="id" select="getstudentlist"></collection>
</resultMap>
<!--按照结果嵌套处理-->
<select id="selectall2" resultMap="all2">
select * from teacher where id=#{id}
</select>
<resultMap id="all2" type="Teacher">
<result column="id" property="id"/>
<result column="name" property="name"/>
<collection property="students" ofType="Student">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="tid" property="tid"/>
</collection>
</resultMap>
</mapper>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.mybatis3.dao;

import com.mybatis3.pojo.Student;
import com.mybatis3.pojo.Teacher;

import java.util.List;

public interface StudentMapper {
List<Student> getstudentlist(int tid);
Boolean add(Student student);
List<Teacher> getteacherlist();
List<Teacher> selectall();
List<Teacher> selectall2(int id);
}

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
import com.mybatis3.dao.StudentMapper;
import com.mybatis3.pojo.Student;
import com.mybatis3.pojo.Teacher;
import com.mybatis3.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class Excute {
@Test
public void getstudent(){
SqlSession sqlSession= MybatisUtil.getSqlsession();
StudentMapper mapper=sqlSession.getMapper(StudentMapper.class);
List<Student> getstudentlist = mapper.getstudentlist(1);
for (Student student : getstudentlist) {
System.out.println(student);
}
sqlSession.close();
}
@Test
public void getteacherlist(){
SqlSession sqlSession= MybatisUtil.getSqlsession();
StudentMapper mapper=sqlSession.getMapper(StudentMapper.class);
List<Teacher> getteacherlist = mapper.getteacherlist();
for (Teacher teacher : getteacherlist) {
System.out.println(teacher);
}
sqlSession.close();
}
@Test
public void selectall(){
SqlSession sqlSession= MybatisUtil.getSqlsession();
StudentMapper mapper=sqlSession.getMapper(StudentMapper.class);
List<Teacher> selectall = mapper.selectall();
for (Teacher student : selectall) {
System.out.println(student);
}
sqlSession.close();
}
@Test
public void selectall2(){
SqlSession sqlSession= MybatisUtil.getSqlsession();
StudentMapper mapper=sqlSession.getMapper(StudentMapper.class);
List<Teacher> selectall = mapper.selectall2(1);
for (Teacher student : selectall) {
System.out.println(student);
}
sqlSession.close();
}
}

这里要注意一个小坑,就是我们这样映射查询的时候pojo类中必须要有无参构造方法:
https://blog.csdn.net/weixin_50236329/article/details/112312423

动态SQL环境搭建

1
2
3
4
5
6
7
CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8

先把这个SQL搭建了,然后创建一个mybatis子项目搭建一个环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.mybatis5.pojo;

import lombok.Data;
import org.apache.ibatis.type.Alias;

import java.util.Date;

@Data
@Alias("Blog")
public class Blog {
private String id;
private String title;
private String author;
private Date create_time;
private int views;
}

1
2
3
4
5
6
7
8
package com.mybatis5.dao;

import com.mybatis5.pojo.Blog;

public interface BlogMapper {
Boolean AddBlog(Blog blog);
}

1
2
3
4
5
6
7
8
<?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.mybatis5.dao.BlogMapper">
<insert id="AddBlog" parameterType="Blog">
insert into blog(id,title,author,create_time,views) values (#{id},#{title},#{author},#{create_time},#{views})
</insert>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
package com.mybatis5.utils;

import java.util.UUID;

public class UuidUtils {
public static String getId() {
String uuid = UUID.randomUUID().toString().replace("-", "");
return uuid;
}
}

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
import com.mybatis5.dao.BlogMapper;
import com.mybatis5.pojo.Blog;
import com.mybatis5.utils.MybatisUtil;
import com.mybatis5.utils.UuidUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.Date;

public class Excute {
@Test
public void addblog(){
Blog blog=new Blog();
String uuid= UuidUtils.getId();
Date time=new Date();
blog.setId(uuid);
blog.setCreate_time(time);
blog.setAuthor("Nano");
blog.setTitle("不知道是什么");
blog.setViews(100);
SqlSession sqlSession= MybatisUtil.getSqlsession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Boolean aBoolean = mapper.AddBlog(blog);
if(aBoolean){
System.out.println("add successfully");
}
sqlSession.close();
}
}

这样先添加个几组数据:
image.png
在这我们多加了一个util,用于获取uuid的,在正规场合id不可能是1,2,3

动态SQL IF标签以及常用标签

https://mybatis.org/mybatis-3/zh/dynamic-sql.html
我觉得这部分跟着官方文档走就知道了

IF标签

这里的测试类就是上述构造出来的,这里就只放Xml文件中的语句了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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.mybatis5.dao.BlogMapper">
<insert id="AddBlog" parameterType="Blog">
insert into blog(id,title,author,create_time,views) values (#{id},#{title},#{author},#{create_time},#{views})
</insert>
<select id="selectall" resultType="Blog" parameterType="Map">
select * from blog where
<if test="author != null">
author=#{author}
</if>
<if test="title != null">
and title=#{title}
</if>
</select>
</mapper>

1
2
3
4
5
6
7
8
9
10
11
12
public void selectall(){
SqlSession sqlSession= MybatisUtil.getSqlsession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String,String> map=new HashMap<>();
map.put("title","不吉波普不笑");
map.put("author","Boogipop");
List<Blog> selectall = mapper.selectall(map);
for (Blog blog : selectall) {
System.out.println(blog);
}
sqlSession.close();
}

if如同字面意思假如author或者title形参不为空就拼接(这里的author和title是对应#{}的)
但是这样就有个问题,假如我author为空title不为空
sql就为select * from blog where and title=?,这里就要引入Where标签

WHERE标签

1
2
3
4
5
6
7
8
9
10
<select id="selectall" resultType="Blog" parameterType="Map">
select * from blog
<where><if test="author != null">
and author=#{author}
</if>
<if test="title != null">
and title=#{title}
</if>
</where>
</select>
1
2
3
4
5
6
7
8
9
10
11
12
13
    @Test
public void selectall(){
SqlSession sqlSession= MybatisUtil.getSqlsession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String,String> map=new HashMap<>();
// map.put("title","不吉波普不笑");
map.put("author","Nano");
List<Blog> selectall = mapper.selectall(map);
for (Blog blog : selectall) {
System.out.println(blog);
}
sqlSession.close();
}

image.png
使用where标签会自动为你剔除前置的第一个and,多了不行,后置了也不行

choose、when、otherwise

这三个就是if,else if,else,感觉没什么区别,也是以一个例子说明

1
2
3
4
5
6
7
8
9
10
11
12
13
<select id="chooseall" resultType="Blog" parameterType="Map">
select * from blog
<where>
<choose>
<when test="author !=null">
and author=#{author}
</when>
<when test="title !=null">
and title=#{title}
</when>
</choose>
</where>
</select>
1
2
3
4
5
6
7
8
9
10
11
12
13
  @Test
public void chooseall(){
SqlSession sqlSession= MybatisUtil.getSqlsession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String,String> map=new HashMap<>();
// map.put("title","不吉波普不笑");
// map.put("author","Nano");
List<Blog> selectall = mapper.chooseall(map);
for (Blog blog : selectall) {
System.out.println(blog);
}
sqlSession.close();
}

image.png
image.png
由于when都不满足就默认全选了

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void chooseall(){
SqlSession sqlSession= MybatisUtil.getSqlsession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String,String> map=new HashMap<>();
map.put("title","不吉波普不笑");
map.put("author","Nano");
List<Blog> selectall = mapper.chooseall(map);
for (Blog blog : selectall) {
System.out.println(blog);
}
sqlSession.close();
}

image.png
由于author标签在前,所以优先

trim、set

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

1
2
3
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>

然后再来看看set,set就是update的时候用的标签,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<update id="updateblog" parameterType="Map">
update blog
<set>
<if test="title!=null">
title=#{title},
</if>
<if test="author!=null">
author=#{author},
</if>
<if test="views!=null">
views=#{views}
</if>
<where>
<choose>
<when test="id!=null">
id=#{id}
</when>
<otherwise>
id="1"
</otherwise>
</choose>
</where>
</set>
</update>

究极无敌的一个update标签,哈哈哈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 @Test
public void updateblog(){
SqlSession sqlSession= MybatisUtil.getSqlsession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String,Object> map=new HashMap<>();
// map.put("title","不吉波普不笑");
map.put("author","Boogipop");
map.put("views",100000);
map.put("id","53bbe5be5a174532bcdb1f9951eb0664");
Boolean updateblog = mapper.updateblog(map);
if(updateblog){
System.out.println("update successfully");
}
sqlSession.close();
}

这个标签就指定的很智能了,如果不指定id就更改不到信息

include标签

动态SQL foreach标签

image.png
foreach的用法其实很简单,以一个例子来说,我们要取数据前三条
select * from user where id in(1,2,3)
在不用分页的情况下怎么做到呢?

1
List<Blog> foreachselect(Map map);
1
2
3
4
5
6
<select id="foreachselect" resultType="Blog" parameterType="Map">
select * from blog where author in
<foreach collection="strings" index="index" item="str" open="(" separator="," close=")">
#{str}
</foreach>
</select>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void foreach(){
SqlSession sqlSession= MybatisUtil.getSqlsession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String,Object> map=new HashMap<>();
List<String> strings = new ArrayList<>();
strings.add("Kino");
strings.add("Nano");
map.put("strings",strings);
List<Blog> foreachselect = mapper.foreachselect(map);
for (Blog blog : foreachselect) {
System.out.println(blog);
}
sqlSession.close();
}

就这么简单

缓存简介

也就是请求相同资源时假如不做缓存会让性能降低,缓存就是把结果储存,再请求的时候不需要反复执行SQL,只需要执行一次即可
一级缓存是默认开启的:
也就是在你一个sqlsession中,如果你反复执行一条同样的语句,语句的结果就会进入sqlsession的缓存,这样就只会执行一次sql语句,当sqlsession关闭时,缓存也没了

二级缓存

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
基本上就是这样。这个简单语句的效果如下:

提示 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。
这些属性可以通过 cache 元素的属性来修改。比如:

这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
可用的清除策略有:

默认的清除策略是 LRU。
flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
提示 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新

二级缓存是作用在UserMapper.xml上的,我们使用前需要开启一下settings中的缓存选项:
image.png
默认是开的,但是为了让别人知道开了再写一遍:
image.png
然后在usermapeer.xml中开启缓存即可

小结

完结啦!mybatis挺方便的,suki

About this Post

This post is written by Boogipop, licensed under CC BY-NC 4.0.

#开发