MyBatis

  • MyBatis是一款优秀的持久层框架,用于简化JDBC开发

持久层

  • 负责将数据保存到数据库的那一层代码
  • JavaEE三层架构:表现层、业务层、持久层。

框架

  • 框架就是一个半成品,是一套可重用的、通用的、软件基础代码模型
  • 在框架的基础上构建软件是的软件编写更加高效、规范、通用、可扩展

MyBatis简化

  1. 硬编码
    1. 注册驱动
    2. 获取连接
  2. 操作繁琐
    1. 手动设置参数
    2. 手动封装结果集

image-20220310191000738

入门代码框架

项目结构

image-20220310202219905

主要文件代码

User.java

数据类,查出来的数据将以User的实例形式保存

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

import lombok.Data;

/**
* @author 11731
*/
@Data
public class User {
private Integer id;

private String username;

private String password;
}

MyBatisDemo.java

核心控制类

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
package com.littleblack;


import com.littleblack.demo.User;
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;
import java.util.List;

public class MyBatisDemo {
public static void main(String[] args) {
//1. 加载MyBatis的核心配置文件,获取SQLSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

//2. 获取SqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession();

//3. 执行SQL
List<User> users = sqlSession.selectList("test.selectAll");

System.out.println(users);
//4. 释放资源
sqlSession.close();
}
}

编写时如果出现红色报错,可使用Alt + Enter快捷键导入类

mybatis-config.xml

核心配置文件

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"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///db1?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="userMapper.xml"/>
</mappers>
</configuration>

主要修改driver、url、username、password

UserMapper.xml

映射类

用于配置具体的sql语句,命名方式一般为查询表名 + Mapper

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="test">
<select id="selectAll" resultType="com.littleblack.demo.User">
select * from db1.tb_user;
</select>
</mapper>

pom.xml

由于本人配置时出现了因为版本不合和缺少导入包等各种阿巴阿巴的问题,所以贴出maven配置,供大家参考

​ ——2022/3/10

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
<?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--demo</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.10</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.10</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>compile</scope>
</dependency>

</dependencies>
<properties>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
</properties>

</project>

Mapper代理开发

目的

  • 解决原生方式中的硬编码
  • 简化后期执行SQL
1
2
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAll();

不依赖字符串全面值,且可以借助IDE的自动补全功能

使用

  1. 定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下。(创建时可使用com/littleblack/demo来代替com/littleblack.demo)
  2. 设置SQL映射文件的namespace属性为Mapper接口全限定名。
  3. 在Mapper接口中定义方编码法,方法名就是SQL映射文件中SQL语句的id,并保持参数类型和返回值类型一致。
  4. 编码:
    1. 通过SqlSession的getMapper方法获取Mapper接口的代理对象。
    2. 调用对应方法完成sql的执行。

注:如果Mapper接口名称和SQL映射文件名称相同,并在同一目录下,则可以使用包扫描的方式简化SQL映射文件的加载

mybatis-config.xml文件中内容如下

1
2
3
4
5
6
7
<mappers>
<!-- 加载sql映射文件-->
<mapper resource="com/littleblack/mapper/UserMapper.xml"/>

<!-- Mapper代理方式 -->
<package name="com.littleblack.mapper"/>
</mappers>

两种方法等效,推荐使用第二种方法

源码展示

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
package com.littleblack;


import com.littleblack.demo.User;
import com.littleblack.mapper.UserMapper;
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 org.contract4j5.contract.Post;
import org.contract4j5.contract.Pre;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyBatisDemo2 {
public static void main(String[] args) throws IOException{
//1. 加载MyBatis的核心配置文件,获取SQLSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

//2. 获取SqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession();

//3. 执行SQL
//List<User> users = sqlSession.selectList("test.selectAll");
//3.1 获取UserMapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectAll();

System.out.println(users);
//4. 释放资源
sqlSession.close();
}
}

MyBatis核心配置文件

environments标签

1
<environments default="development">

配置数据库连接环境信息,其中可包含多个数据库,可以配置多个environment,通过default属性切换不同的environment

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"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///db1"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///student"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>

transactionManager

事物关系信息,暂时不用考虑

dataSource

数据库连接池,暂时不用考虑

typeAliases

别名

1
2
3
4
<configuration>
<typeAliases>
<package name="com.littleblack.demo"/>
</typeAliases>

在mapper中resultType中就可以直接使用类名称而不需要添加包名

1
2
3
4
5
<mapper namespace="com.littleblack.mapper.UserMapper">
<select id="selectAll" resultType="User">
select * from db1.tb_user;
</select>
</mapper>

同时,如果出现封装类中变量名称和数据库中属性名称不一样的情况会导致返回数据无法自动封装,这个时候也可以使用别名

1
2
3
<select id="selectAll" resultType="User">
select id, username as userName, password as passWord from db1.tb_user;
</select>

如果觉得每次都需要重新定义别名很麻烦,可使用sql片段

1
2
3
4
5
6
7
8
<sql id="brand_column">
id, username, password
</sql>
<select id="selectAll" resultType="User">
select
<include refid="brand_column"/>
from db1.tb_user;
</select>

但是这种方法不推荐,片段化的sql语句可能会引起IDE报错并且无法使用自动补全

撰写映射

推荐方法:撰写映射

  • 定义resultMap标签
  • 在select标签中,使用resultMap属性替换resultType属性
1
2
3
4
5
6
7
8
<resultMap id="UserMapper" type="User">
<result column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
</resultMap>
<select id="selectAll" resultMap="UserMapper">
select * from db1.tb_user;
</select>

映射中可以只写数据库属性名称和映射类名称不一样的,一样的可以不用写

1
2
<id column="id" property="id"/>
<result column="username" property="username"/>
  • id用于完成主键字段的映射
  • result用于完成其他字段的映射

最好还是直接映射类中的变量和数据库中的一一对应

参数占位符

  • #{}:执行SQL时,会将#{}占位符替换为?,将来自动设置参数值

  • ${}:通过SQL语句拼接的方式执行,存在SQL注入的风险

  • 参数传递可以直接使用#{}

  • 但是如果需要对表名或者列名动态设置,则只能使用${}进行sql拼接

parameterType

  • 用于设置参数数据类型,改参数可以省略

SQL语句中特殊字符处理

转义字符

转义表示 符号 符号含义
& amp; 或 & &
& lt; 或 < < 小于号
& gt; 或 > > 大于号
& quot; 双引号
& nbsp; 空格
& copy; © 版权符
& reg; ® 注册符

代码区

1
2
3
<![CDATA][
编写字符
]]>

查询

传递参数的方式:

1
2
3
void insert(@Param("id")int id, @Param("username")String username, @Param("password")String password);
void insert(User user);
void insert(Map map);
1
2
3
<insert id="insert">
insert db1.tb_user values (#{id}, #{username}, #{password});
</insert>
  • 如果使用参数传递,可以使用@Param标注参数对应的位置
  • 如果使用类传递,则需要保证映射类的变量名称和xml中的变量名称一致
  • 如果使用map传递,则需要保证map中key的名称和xml中的变量名称一致

多条件动态查询

1
User selectByCondition(User user);

传递参数方式同上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<select id="selectByCondition" resultMap="UserMapper">
select * from tb_user
<where>
<if test="id != null">
id = #{id}
</if>
<if test="username != null and username != ''">
and username like #{username}
</if>
<if test="password != null and password != ''">
and password like #{password}
</if>
</where>
</select>
  • 使用if标签判断哪些参数需要使用,哪些不需要
    • 但是如果第一个参数不需要的话,sql语句将会变成”select * from tb_user where and username like #{username}”,这会造成语法检查无法通过,所以使用一个where标签,会自动判断是否加上and

单条件动态查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<select id="selectByCondition" resultMap="UserMapper">
select * from tb_user
<where>
<choose>
<when test="id != null">
id = #{id}
</when>
<when test="username != null and username != ''">
and username like #{username}
</when>
<when test="password != null and password != ''">
and password like #{password}
</when>
<otherwise/>
</choose>
</where>
</select>

choose类似Switch

when类似case(带break,只能执行一次)

otherwise类似default

添加

1
2
3
void insert(User user);
void insert(@Param("id")int id, @Param("username")String username, @Param("password")String password);
void insert(Map map);
1
2
3
<insert id="insert">
insert db1.tb_user values (#{id}, #{username}, #{password});
</insert>

如何在添加完后获取添加行主键的值呢?

1
2
3
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert db1.tb_user values (#{id}, #{username}, #{password});
</insert>

使用useGenerateedKeys和keyProperty标签映射至数据库内设置好的自动增加的主键列中,执行完后该对象或者该变量的值会被自动写入,只需要再次读取就可以获取到添加行主键的值。

修改

1
2
3
4
void update(@Param("id")int id, @Param("password")String password);
// 将变量和占位符一一对应
void update(User user);
void update(Map map);
1
2
3
<update id="update">
update db1.tb_user set password = #{password} where id = #{id};
</update>

删除

单个删除

1
void deleteByID(int id);
1
2
3
<delete id="deleteByID">
delete from tb_user where id = #{id};
</delete>

批量删除

1
2
3
4
5
6
@Test
public void testDeleteByIDs() throws IOException {
int[] id = {1,2,3};
userMapper.deleteByID(1);
System.out.println(userMapper.selectAll());
}
1
2
3
4
5
6
7
8
<delete id="deleteByIds" >
delete from tb_user where id
in (
<foreach collection="ids" item="id" separator=",">
#{id};
</foreach>
);
</delete>
  • 使用foreach标签
    • collection表示列表名称
    • item表示列表内对应赋值到的变量
    • separator表示用来拼接的分隔符(思路还是拼成sql语句,中间没有逗号会报错)

MyBatis参数封装

  • POJO类型:POJO是Plain OrdinaryJava Object的缩写,但是它通指没有使用Entity Beans的普通java对象,可以把POJO作为支持业务逻辑的协助类。

    • 直接使用,属性名和占位符名称一致
  • Map集合:

    • 直接使用,键名和参数占位符名称一致
  • Collection:

    • 封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
    1
    2
    map.put("arg0", collection集合);
    map.put("collection", collection集合);
  • List:

    • 封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
    1
    2
    3
    map.put("arg0", list集合);
    map.put("collection", list集合);
    map.put("list", list集合);
  • Array:

    • 封装为Map集合,可以使用@Param注解,替换Map集合中默认的arg键名
    1
    2
    map.put("arg0", 数组);
    map.put("array", 数组);
  • 其他类型:

    • 直接使用