MyBatis 基础学习
MyBatis
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
Reference
- Media
- Document
- GitHub
1. 简介
1.1 什么是MyBatis
- Mybatis是一款优秀的持久层框架。
- 它支持定制化SQL、存储过程以及高级映射。
- ==Mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集==
- Mybatis可以使用简单的XML或注解来配置和映射原生类型、接口和Java的POJO(Plain Old Java Object,普通老式Java对象)为数据库中的记录。
- Mybatis本是Apache的一个开源项目Ibatis,2010年这个项目由Apache software foundation迁移到了Google Code,并改名为Mybatis。
- ==2013年11月迁移到Github==。
1.2 获取
maven 仓库
- ```xml
</dependency><groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.13</version>
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
- GitHub: [mybatis/mybatis-3](https://github.com/mybatis/mybatis-3)
## 1.3 持久化
- 数据持久化
- 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
- 内存:**断电即失**
- 数据库(Jdbc),io文件持久化
- 作用
- 节省内存
## 1.4 持久层
- 完成**持久化工作**的代码块
- 层界限十分明显
## 1.5 优点
- 将数据存入到数据库中
- 操作方便
- 传统的JDBC代码太复杂,简化–>框架–>自动化
- 使用的人多 ==(技术没有高低之分!)==
# 2. 第一个Mybatis程序
```mermaid
graph LR
搭建环境 --> 导入MyBatis --> 编写代码 --> 测试
- ```xml
参照官方文档进行配置:mybatis – MyBatis 3
2.1 搭建环境
新建maven项目
MySQL
``sql CREATE DATABASE
mybatis`;USE
mybatis
;CREATE TABLE
user
(`id` INT(20) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, `pwd` VARCHAR(30) DEFAULT NULL, PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO
user
(id
,name
,pwd
) VALUES
(1, ‘bayyy’, ‘123456’),
(2, ‘dnnner’, ‘123456’),
(3, ‘BAY’, ‘123465’)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- 导入maven依赖
- mysql - `mysql-connector-java`
- mybatis
- junit
- pom.xml
```xml
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
db.properties
1
2
3
4driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username=root
password=123456mybatis-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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<!--核心配置文件-->
<configuration>
<!-- 引入外部配置文件 -->
<properties resource="db.properties">
<!-- 也可以在内部增加配置, 若存在相同字段, 优先使用外部配置文件 -->
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
<!-- 设置 -->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 别名 -->
<typeAliases>
<!-- 给实体类起别名 -->
<typeAlias type="com.bayyy.pojo.User" alias="User"/>
<!-- 给包起别名: 扫描实体类的包, 默认别名就是类名首字母小写 -->
<package name="com.bayyy.pojo"/>
</typeAliases>
<!-- 环境配置(default-选择默认环境) -->
<environments default="development">
<environment id="development">
<!--事务管理器-->
<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>
<!-- 测试环境(可配置多个环境) -->
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED"></dataSource>
</environment>
</environments>
<!-- 指定映射文件 -->
<mappers>
<mapper resource="com/bayyy/dao/UserMapper.xml"/>
</mappers>
</configuration>IDUtils.java
1
2
3
4
5
6
7
8
9
10
11
12// 抑制警告
public class IDUtils {
public static String getId() {
return UUID.randomUUID().toString().replace("-", "");
}
public void test() {
System.out.println(IDUtils.getId());
}
}MyBatisUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// sqlSessionFactory --> sqlSession
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession(true);
}
}log4j.properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger = DEBUG,console ,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold = DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = [%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File = ./log/kuang.log
log4j.appender.file.MaxFileSize = 10mb
log4j.appender.file.Threshold = DEBUG
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = [%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
2.2 创建一个模块
2.2.1 编写mybatis的核心配置文件
1 |
|
2.2.2 编写mybatis工具类
1 | // sqlSessionFactory --> sqlSession |
2.3 编写代码
2.3.1 实体类
1 |
|
2.3.2 Dao接口
1 | public interface UserDao { |
2.3.3 Dao接口实现类
1)mybatis-xml配置(*)
UserMapper.xml
1 |
|
2)UserDaoImpl 实现
1 | public class UserDaoImpl implements UserDao{ |
2.4 测试
2.4.1 测试语句
- 测试在
test
对应文件下使用Junit
进行, 此处:
1 |
|
2.4.2 try-with
使用try-with-resources代替常规try-catch-finally
- 多条声明 ‘;’ 分割,且后声明的先释放
1 | // try-with-resources: try内部的资源会自动释放(且是先声明的资源先释放) |
2.4.3 易错点
1)配置文件
第一个易错点, MapperRegistry
, ==配置文件没有注册==:
org.apache.ibatis.binding.BindingException: Type interface com.bayyy.dao.UserDao is not known to the MapperRegistry.
==mybatis-config.xml 中配置mappers:==
-
1
2
3
4<mappers>
<!-- 使用 '/' 表示路径 -->
<mapper resource="com/bayyy/dao/UserMapper.xml"/>
</mappers>
2)导出资源
第二个错误点, ExceptionInInitializerError
, ==导出资源路径存在问题==:
问题原因:==放在java文件夹下的.xml/.properties不会自动在target目录中生成==
解决方法
在xml中使用
resources
加入这些文件```xml
<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> </resource> </resources>
</build>
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
- ![image-20230715215825830](https://s2.loli.net/2023/07/15/Xl9dvouy8qQ4DVU.png)
# 3. CRUD
==增删改需要提交错误==
## 3.1 namespace
- namespace中的包名要和 Mapper(Dao) 接口的包名一致
## 3.2 select
> 查询语句
![image-20230716131850778](https://s2.loli.net/2023/07/16/VNAJnZIHCmt3zbs.png)
- `id`: 对应 namespace 中的方法名
- `resultType`: sql 语句执行的返回值
- `parameterType`: 参数类型
### 3.2.1 编写接口
```java
/* UserMapper.java */
// 根据id查询用户
User getUserById(int id);
3.2.2 Mapper.xml配置
1 | <!-- UserMapper.xml --> |
3.2.3 测试
1 |
|
- 测试结果:
3.3 Insert
:key: 对象中的属性可以直接用 #{属性名} 取出
- Mapper.xml
1 | <!-- insert 插入语句 |
:warning: ==增删改==需要事务处理 ==SqlSession.commit();==
1
2
3
4
5
6
7
8
9
10
11
12
13 try (SqlSession sqlSession = MyBatisUtils.getSqlSession()) {
int add = sqlSession.getMapper(UserMapper.class).addUser(new User(4, "长江四号", "123456"));
User user = sqlSession.getMapper(UserMapper.class).getUserById(4);
System.out.println(user);
}
try (SqlSession sqlSession = MyBatisUtils.getSqlSession()) {
User user = sqlSession.getMapper(UserMapper.class).getUserById(4);
System.out.println(user);
}
>>>
User(id=4, name=长江四号, pwd=123456)
null
测试
- ```java
@Test
public void test3() {
}try (SqlSession sqlSession = MyBatisUtils.getSqlSession()) { int add = sqlSession.getMapper(UserMapper.class).addUser(new User(4, "长江四号", "123456")); if (add > 0) { User user = sqlSession.getMapper(UserMapper.class).getUserById(4); System.out.println(user); } else { System.out.println("插入失败"); } sqlSession.commit(); }
1
2
3
4
5
6
7
8
9
10
11
12
13
## 3.4 Update
- Mapper.xml
```xml
<!-- update 更新语句
// 修改一个用户
int updateUser(User user);
-->
<update id="updateUser" parameterType="com.bayyy.pojo.User">
update mybatis.user set name=#{name}, pwd=#{pwd} where id=#{id}
</update>
- ```java
测试
1 |
|
- 测试结果:
3.5 Delete
- Mapper.xml
1 | <!-- delete 删除语句 |
- 测试
1 |
|
- 测试结果:
3.6 万能Map
实体类或数据库表,字段或参数过多时,应当考虑用Map
1 | // Map 方式修改一个用户 |
1 | <!-- Map 方式传参 |
1 |
|
- 基本类型传参
parameterType="int"
可以直接取到 - 对象传参
parameterType='User'
直接取对象属性 - Map传参
parameterType='map'
直接取对应的 key 得到对应的 value
3.8 模糊查询
java代码中使用通配符
1
List<User> userList = mapper.getUserLike("%李%");
sql拼接中使用通配符
1
select * from mybatis.user where name like "%"#{value}"%"
4. 配置解析
- mybatis-config.xml
4.1 核心配置文件
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
configuration
配置顺序需要遵循 -The content of element type "configuration" must match "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".
示例:
```xml
<?xml version=”1.0” encoding=”UTF-8” ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 引入外部配置文件 --> <properties resource="db.properties"> <!-- 也可以在内部增加配置, 若存在相同字段, 优先使用外部配置文件 --> <property name="username" value="root"/> <property name="password" value="123456"/> </properties> <!-- 别名 --> <typeAliases> <!-- 给实体类起别名 --> <typeAlias type="com.bayyy.pojo.User" alias="User"/> <!-- 给包起别名: 扫描实体类的包, 默认别名就是类名首字母小写 --> <package name="com.bayyy.pojo"/> </typeAliases> <!-- 环境配置(default-选择默认环境) --> <environments default="development"> <environment id="development"> <!--事务管理器--> <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> <!-- 测试环境(可配置多个环境) --> <environment id="test"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"></dataSource> </environment> </environments> <!-- 指定映射文件 --> <mappers> <mapper resource="com/bayyy/dao/UserMapper.xml"/> </mappers>
</configuration>
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
## 4.2 环境配置(environments)
- MyBatis 可以配置成适应多种环境,**但每个 SqlSessionFactory 实例只能选择一种环境**
- DEFAULT
- 事务管理器 - JDBC
- 数据源 - POOLED
### 4.2.1 事务管理器(transactionManager)
- 在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
1. JDBC – 这个配置直接使用了 JDBC 的提交和回滚功能,它依赖从数据源获得的连接来管理事务作用域
2. MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)
> :pushpin: 如果使用 Spring + MyBatis,就没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
### 4.2.2 数据源(dataSource)
- 有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):
1. **UNPOOLED**– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择
2. ==**POOLED**==– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,**能使并发 Web 应用快速响应请求** (==使用后不会关闭==)
3. **JNDI** – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用
## 4.3 属性(properties)
### 4.3.1 描述
- 可以通过properties属性来实现引用配置文件
- 这些属性可以在外部进行配置,并可以进行**动态替换**。你既可以在典型的 **Java 属性文件**中配置这些属性,也可以在 **properties 元素**的子元素中设置。==db.properties==
### 4.3.2 过程
- db.properties
```properties
driver=com.mysql.cj.jdbc.Driver
url=jjdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username=root
password=123456
mybatis-config.xml : 使用
value="${...}"
获取
1 |
|
4.4 类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
1 | // mybatis-config.xml |
- 实体类起别名
<typeAlias type="com.bayyy.pojo.User" alias="User"/>
:User
可以用在任何使用com.bayyy.pojo.User
的地方
- 包起别名
- MyBatis 会在包名下面搜索需要的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如
com.bayyy.pojo.User
的别名为user
;若有注解,则别名为其注解值
- MyBatis 会在包名下面搜索需要的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如
1 | // UserMapper.xml |
4.5 设置
MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为
- 现介绍几个重要的设置
设置名 | 描述 | 有效值 | 默认值 | ||||||
---|---|---|---|---|---|---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true \ | false | true | |||||
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 |
true \ | false | false | |||||
useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 | true \ | false | False | |||||
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true \ | false | False | |||||
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J \ | LOG4J(3.5.9 起废弃) \ | LOG4J2 \ | JDK_LOGGING \ | COMMONS_LOGGING \ | STDOUT_LOGGING \ | NO_LOGGING | 未设置 |
4.6 其他配置
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- mybatis-generator-core
- mybatis-plus
- 通用mapper
4.7 映射器(mappers)
MapperRegistry:注册绑定我们的Mapper文件
方式一:使用相对于类路径的资源引用
1
2
3
4
5<mappers>
<mapper resource="com/bayyy/dao/UserMapper.xml"/>
<!-- 将此目录下的通配 -->
<mapper resource="com/bayyy/dao/*Mapper.xml"/>
</mappers>方式二:使用映射器接口实现类的完全限定类名
:warning: 接口和它的Mapper配置文件必须同名
:warning: 接口和它的Mapper配置文件必须在同一个包下!
1
2
3<mappers>
<mapper class="com.bayyy.dao.UserMapper"/>
</mappers>方式三:将包内的映射器接口全部注册为映射器
:warning: 接口和它的Mapper配置文件必须同名
:warning: 接口和它的Mapper配置文件必须在同一个包下!
1
2
3<mappers>
<package name="com.bayyy.dao"/>
</mappers>方式四:使用完全限定资源定位符(URL)
1
2
3
4
5<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
4.8 生命周期和作用域 Scope
不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题
1 | flowchart LR |
- SqlSessionFactoryBuilder
- 一旦创建了 SqlSessionFactory,就不再需要它了
- ==(局部变量)==
- SqlSessionFactory
- 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例 (数据库连接池)
- 因此 SqlSessionFactory 的最佳作用域是应用作用域
- ==单例模式或者静态单例模式==
- SqlSession
- 每个线程都应该有它自己的 SqlSession 实例 (连接请求)
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
- 用完后需要赶紧关闭,否则资源被占用!
5. 解决属性名和字段名不一致的问题
5.1 问题
运行结果:
解决方法:
- 起别名
select id,name,pwd as password from mybatis.user where id = #{id}
- resultMap
- 起别名
5.2 resultMap
1 | <resultMap id="userMap" type="User"> |
- resultMap 元素是 MyBatis 中最重要最强大的元素
- ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
- ResultMap 的优秀之处——你完全可以不用显式地配置它们。
6. 日志
6.1 日志工厂
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J \ | LOG4J(3.5.9 起废弃) \ | LOG4J2 \ | JDK_LOGGING \ | COMMONS_LOGGING \ | STDOUT_LOGGING \ | NO_LOGGING | 未设置 |
---|---|---|---|---|---|---|---|---|---|
- SLF4J
- LOG4J 【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING【掌握】
- NO_LOGGING
1 | <settings> |
- 测试结果:
6.2 Log4j
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
- 我们也可以控制每一条日志的输出格式;
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
- 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
6.2.1 配置过程
导入依赖包
1
2
3
4
5
6<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>在resources文件夹下建立
log4j.properties
文件进行配置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger = DEBUG,console ,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold = DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = [%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File = ./log/kuang.log
log4j.appender.file.MaxFileSize = 10mb
log4j.appender.file.Threshold = DEBUG
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = [%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUGmybatis-config.xml 配置
1
2
3<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>测试结果:
6.2.2 简单使用
导包及生成日志对象
1
2
3
4
5import org.apache.log4j.Logger;
public class UserMapperTest {
static Logger logger = Logger.getLogger(UserMapperTest.class);
}日志级别
1
2
3logger.info("info:进入了testLog4j");
logger.debug("DEBUG:进入了testLog4j");
logger.error("erro:进入了testLog4j");可在对应位置查看日志文件信息
7. 分页
作用:减少数据的处理量
7.1 使用Limit分页
1 | SELECT * from user limit startIndex,pageSize |
7.1.1 接口
1 | // 分页查询 |
7.1.2 Mapper.xml
1 | <!-- 分页查询 --> |
7.1.3 测试
1 |
|
- 测试结果:
7.2 RowBounds分页
不推荐
7.2.1 接口
1 | // 分页查询: RowBounds |
7.2.2 Mapper.xml
1 | <!-- 分页查询: RowBounds --> |
7.2.3 测试
1 |
|
7.3 分页插件
了解即可
8. 使用注解开发
8.1 面向接口编程
- 之前学过面向对象编程,也学习过接口,但在真正的开发中,很多时候会选择面向接口编程
- 根本原因:==解耦==,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好
- 在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;
- 而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。
8.1.1 面向接口的理解
- 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
- 接口的本身反映了系统设计人员对系统的抽象理解。
- 接口应有两类:
- 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
- 第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);
- 一个体有可能有多个抽象面。抽象体与抽象面是有区别的
8.1.2 三个面向的区别
- 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法.
- 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现.
- 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题更多的体现就是对系统整体的架构
8.2 使用注解开发
- 本质:反射机制实现
- 底层:动态代理!
1 | // UserMapper.java |
无需 Mapper.xml 文件
mybatis-config.xml
1
2
3
4<!-- 绑定接口 -->
<mappers>
<mapper class="com.bayyy.dao.UserMapper"/>
</mappers>测试
1
2
3
4
5
6
7
public void testSelectAll() {
// try-with-resources: try内部的资源会自动释放(且是先声明的资源先释放)
try (SqlSession sqlSession = MyBatisUtils.getSqlSession()) {
sqlSession.getMapper(UserMapper.class).getUserList().forEach(System.out::println);
}
}测试结果:
8.3 MyBatis执行流程
1 | --- |
8.4 CRUD注解
8.4.1 自动提交事务
1 | // MybatisUtils.java |
8.4.2 使用
- UserMapper.java
1 | // 查询全部用户 |
测试
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
public void testSelectAll() {
// try-with-resources: try内部的资源会自动释放(且是先声明的资源先释放)
try (SqlSession sqlSession = MyBatisUtils.getSqlSession()) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.getUserList().forEach(System.out::println);
}
}
public void testSelectById() {
try (SqlSession sqlSession = MyBatisUtils.getSqlSession()) {
System.out.println(sqlSession.getMapper(UserMapper.class).getUserById(1));
}
}
public void testAddUser() {
try (SqlSession sqlSession = MyBatisUtils.getSqlSession()) {
sqlSession.getMapper(UserMapper.class).addUser(new User(5, "长江五号", "123456"));
// openSession(true) 会自动提交事务
}
}
public void testUpdate() {
try (SqlSession sqlSession = MyBatisUtils.getSqlSession()) {
sqlSession.getMapper(UserMapper.class).updateUser(new User(5, "长江五号", ""));
System.out.println(sqlSession.getMapper(UserMapper.class).getUserById(5));
}
}
public void testDelete() {
try (SqlSession sqlSession = MyBatisUtils.getSqlSession()) {
sqlSession.getMapper(UserMapper.class).deleteUser(5);
sqlSession.getMapper(UserMapper.class).getUserList().forEach(System.out::println);
}
}
8.4.3 @Param
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型的话,可以忽略,但是建议都加上!
- 我们在SQL中引用的就是我们这里的@Param(“”)中设定的属性名!
#{}
和${}
的区别:#{}
可以很大程度上防止sql注入
9. Lombok
Lombok是一款Java开发插件,使得Java开发者可以通过其定义的一些注解来消除业务工程中穴长和繁琐的代码,尤其对于简单的Java模型对象(POJO)。在开发环境中使用Lombok插件后,Java开发人员可以节省出重复构建,诸如hashCode和equals这样的方法以及各种业务对象模型的accessor和ToString等方法的大量时间。对于这些方法,它能够在编译源代码期间自动帮我们生成这些方法,并没有如反射那样降低程序的性能。
- IDE 安装
Lombok
插件
1 | <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> |
9.1 典型的注解
1 | : 无参构造、get、set、toString、hashCode、equals |
9.2 插件简介中更多注解
1 | and |
10. 多对一处理
多对一
- 多个学生,对应一个老师
- 对于学生而言,关联–多个学生,关联一个老师【多对一】
- 对于老师而言,集合–一个老师,有很多个学生【一对多】
sql 建表
``sql 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,’秦老师’);CREATE TABLE
student
(`id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, `tid` INT(10) DEFAULT NULL, PRIMARY KEY (`id`), KEY `fktid`(`tid`), CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
)ENGINE = INNODB DEFAULT CHARSET=utf8
INSERT INTO
student
(id
,name
,tid
) VALUES (‘1’,’小明’,’1’);
INSERT INTOstudent
(id
,name
,tid
) VALUES (‘2’,’小红’,’1’);
INSERT INTOstudent
(id
,name
,tid
) VALUES (‘3’,’小张’,’1’);
INSERT INTOstudent
(id
,name
,tid
) VALUES (‘4’,’小李’,’1’);
INSERT INTOstudent
(id
,name
,tid
) VALUES (‘5’,’小王’,’1’);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
## 10.1 测试环境搭建
1. 实体类 Teacher, Student
2. Mapper 接口
3. Mapper.xml
4. mybatis-config.xml 核心配置文件绑定注册
- 此处放在 `resources/com/bayyy/dao` 中,要注意建包是否出问题
- ![image-20230716202205735](https://s2.loli.net/2023/07/16/nAN6SatmwxvWsgD.png)
5. 测试查询是否能够成功
## 10.2 按照查询嵌套处理
```xml
<!-- 按照查询嵌套处理 -->
<select id="getStudent" resultMap="studentTeacher">
select * from student
</select>
<resultMap id="studentTeacher" type="student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!-- 复杂的属性: 需要单独处理
对象: association
集合: collection -->
<association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="teacher">
select * from teacher where id = #{tid}
</select>
10.3 按照结果嵌套处理
1 | <!-- 按照结果嵌套处理 --> |
11 . 一对多处理
11.1 环境搭建
1 |
|
11.2 按照结果嵌套处理
1 | <!-- 按照结果嵌套查询 --> |
- 测试结果:
11.3 按照查询嵌套处理
1 | <!-- 按照查询嵌套结果 --> |
- 测试结果:
12. 动态SQL
根据不同的条件生成不同的SQL语句
在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
12.1 搭建环境
- 建表
1 | CREATE TABLE `blog`( |
pojo
1
2
3
4
5
6
7
8
public class Blog {
private int id;
private String title;
private String author;
private Date createTime; // 此处与数据库不完全相同 create_time(通过配置文件解决:mapUnderscoreToCamelCase)
private int views;
}1
2
3
4
5// mybatis-config.xml
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>IDutils: 生成UUID
1
2
3
4
5
6// 抑制警告
public class IDutils {
public static String getId() {
return UUID.randomUUID().toString().replace("-", "");
}
}
12.2 IF
这条语句提供了可选的查找文本功能
==where== 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
1 | // 查询博客 |
1 | <select id="selectBlogIf" parameterType="map" resultType="blog"> |
1 | HashMap map = new HashMap(); |
12.3 choose (when, otherwise)
choose 元素,它有点像 Java 中的 switch 语句
1 | <select id="selectBlogChoose" parameterType="map" resultType="blog"> |
12.4 trim (where, set)
可以通过自定义 ==trim== 元素来定制 where 元素的功能
覆盖前缀值设置
1
2
3 <trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>覆盖后缀值设置
1
2
3 <trim prefix="SET" suffixOverrides=",">
...
</trim>用于动态更新语句的类似解决方案叫做 ==set==。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列
- set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)
1 | <update id="updateBlogSet" parameterType="map"> |
12.5 Foreach
对集合进行遍历(尤其是在构建 IN 条件语句的时候)
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符
- :pushpin: 可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach
- 当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素
- 当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值
1 | <select id="selectBlogForeach" parameterType="map" resultType="blog"> |
1 |
|
12.6 SQL片段
允许插入一种语言驱动,并基于这种语言来编写动态 SQL 查询语句
1 | <sql id="if-title-author"> |
- :warning: 不要存在where标签
13. 缓存(了解)
13.1 简介
13.1.1 什么是缓存[Cache]?
- 存在内存中的临时数据。
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库查询文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
13.1.2 为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
13.1.3 什么样的数据能使用缓存?
经常查询并且不经常改变的数据
13.2 Mybatis缓存
Mybatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
Mybatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,它是基于namespace级别的缓存。
- 为了提高扩展性,Mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存。
13.3 一级缓存
13.3.1 简介
- 一级缓存也叫本地缓存:
- 与数据库同一次会话期间查询到的数据会放在本地缓存中。
- 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
13.3.2 缓存失效情况
- 查询不同的东西;
- 增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
- 查询不同的Mapper.xml
- 手动清理缓存!
一级缓存相当于一个Map
13.4 二级缓存
13.4.1 简介
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存;
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
- 工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据就会放在自己对应的缓存(map)中;
13.4.2 开启步骤
在mybatis-config.xml开启全局缓存
1
2<!--显示的开启全局缓存-->
<setting name="cacheEnabled" value="true"/>在要使用二级缓存的Mapper中开启
1
2<!--在当前Mapper.xml中使用二级缓存-->
<cache/>也可以自定义参数
1
2
3
4
5
6<!--在当前Mapper.xml中使用二级缓存: 以下为默认配置-->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
实体类序列化