目录:
一、MyBatis入门
1、什么是MyBatis
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
2、MyBatis、Hibernate 和 JDBC
-
和 Hibernate 相比,MyBatis 封装性低于 Hibernate,但性能优秀、小巧、简单易学、应用广泛
-
和 JDBC 相比,MyBatis 减少了 50% 以上的代码量,并且满足高并发和高响应的要求
3、优缺点
3.1 优点
- MyBatis 是免费且开源的。
- 与 JDBC 相比,减少了 50% 以上的代码量。
- MyBatis 是最简单的持久化框架,小巧并且简单易学。
- MyBatis 相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL 写在 XML 中,和程序逻辑代码分离,降低耦合度,便于统一管理和优化,提高了代码的可重用性。
- 提供 XML 标签,支持编写动态 SQL 语句。
- 提供映射标签,支持对象与数据库的 ORM 字段关系映射。
- 支持存储过程。MyBatis 以存储过程的形式封装 SQL,可以将业务逻辑保留在数据库之外,增强应用程序的可移植性、更易于部署和测试。
3.2 缺点
- 编写 SQL 语句工作量较大,对开发人员编写 SQL 语句的功底有一定要求。
- SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
3.3 使用场景
MyBatis 专注于 SQL 本身,是一个足够灵活的 DAO 层解决方案。适用于性能要求高,且需求变化较多的项目,如互联网项目
4、安装
要使用 MyBatis, 只需将 mybatis-x.x.x.jar
文件置于类路径(classpath)中即可,如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中
1.例如在Maven项目中,导入MyBatis3.5.2版本依赖
<!--MyBatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--数据库依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
二、第一个MyBatis程序
1、从XML中构建 SqlSessionFactory
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以由SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
1.1 XML配置
mybatis-config.xml
配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。后面会再探讨 XML 配置文件的详细内容,这里先给出一个简单的示例:
<?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配置内容时,有顺序限制-->
<configuration>
<!--mybatis环境,default属性指定一个环境-->
<environments default="development">
<!--id属性自定义的环境唯一标识符-->
<environment id="development">
<!--指定使用jdbc事务管理-->
<transactionManager type="JDBC"/>
<!--使用mybatis内部带连接池功能的数据源-->
<dataSource type="POOLED">
<!--获取配置驱动-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--获取配置url-->
<property name="url" value="jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8"/>
<!--获取配置账号-->
<property name="username" value="root"/>
<!--获取配置密码-->
<property name="password" value="112112"/>
</dataSource>
</environment>
</environments>
<!--映射器,映射器的 XML 映射文件包含了 SQL 代码和映射定义信息-->
<mappers>
<!--使用相对于类路径的资源引用 这是相对于类路径中-->
<mapper resource="mapper/UserMapper.xml"/>
<!--<mapper resource="com/hqyj/mapper/UserMapper.xml"/>-->
</mappers>
</configuration>
1.1.1 注意事项
1.注意 XML 头部的声明,它用来验证 XML 文档的正确性
2.mappers 元素则包含了一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息
3.对于映射器mappers,通过<mapper resource="mapper/UserMapper.xml"/>
引入XML文件的时候,要么XML文件在maven项目的resources下,要么就需要在pom.xml文件中导入以下内容
<build>
<!--resources配置解决mybatis 的mapperXml配置在java路径不被扫描的问题 -->
<resources>
<resource>
<directory>src/main/java</directory>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--跳过测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
1.2 获取SqlSessionFactory
// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
1.3 设计数据库表
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`age` int(255) NULL DEFAULT NULL,
`state` int(255) NULL DEFAULT 1,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
INSERT INTO `user` VALUES (1, 'admin', '111', 20, 1);
INSERT INTO `user` VALUES (2, '王五', '789', 24, 1);
INSERT INTO `user` VALUES (3, '小王', '123', 88, 1);
1.4 编写JavaBean
首先,在IDEA下载插件Lombok
然后在pom.xml中导入依赖
<!--Lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>
最后使用注解代替setter、getter、toString、构造函数
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String password;
private int age;
private int deptId;
private int state;
}
1.5 编写UserMapper
<?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.hqyj.mapper.UserMapper">
<select id="getUserList" resultType="com.hqyj.pojo.User">
select * from `user`
</select>
</mapper>
1.6 测试
// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行SQL---方式一
// SqlSession执行文件中定义的SQL,并返回映射结果,参数可以直接输入方法名
// List<User> userList = sqlSession.selectList("com.hqyj.cl.mapper.UserMapper.getUserList");
// 执行SQL---方式二
// 通过SqlSession对象获取StudentMapper的代理对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
// 使用代理mapper对象查询全部的用户信息
List<Student> studentList = mapper.selectAll();
// 输出内容
System.out.println(userList);
1.7 #{}和${}
#{}
方式传值是安全的,起占位符作用,为参数占位符 ?,即sql 预编译,能防止sql 注入
${}
为字符串替换,即 sql 拼接,不能防止sql 注入
1.7.1 注入案例
注意:
使用${}
的方式会取到jdbc.properties中的配置属性内容,所以要注意
如果UserMapper接口中的参数大于等于2个的时候,那么有3种方式解决参数找不到问题
1.封装成Map集合方式(暂时不讲,比较繁琐)
2.封装成对应pojo类对象(login方法的参数直接换成User对象)
3.使用注解@Param来指定参数名调整mapper接口方法
login(@Param(“username”)String username,@Param(“password”)String password)
官方推荐:参数个数小于5个用注解,大于5个用对象
List<Student> login(@Param("username")String username,@Param("password")String password);
<select id="login" resultType="com.hqyj.cl.pojo.Student">
select * from `student` where username='${username}' and password='${password}'
</select>
public class TestStudentDao {
public static void main(String[] args) throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("config/mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//可以调整login参数只有username以及mapper的sql 发现可以查出,但是2个参数不行
List<Student> student = mapper.login("admin","111222' or '1'='1");
System.out.println(student);
}
}
三、核心对象
MyBatis 有三个基本要素:
- 核心接口和类
- MyBatis核心配置文件(mybatis-config.xml)
- SQL映射文件(mapper.xml)
MyBatis 的核心接口和类,如下所示
bulid() openSession()
SqlSessionFactoryBuilder ---------> sqlSessionFactory ---------------> sqlSession
每个 MyBatis 应用程序都以一个 SqlSessionFactory 对象的实例为核心。
首先获取 SqlSessionFactoryBuilder 对象,可以根据 XML 配置文件或者 Configuration 类的实例构建该对象。
然后获取 SqlSessionFactory 对象,该对象实例可以通过 SqlSessionFactoryBuilder 对象来获取。
有了 SqlSessionFactory 对象之后,就可以进而获取 SqlSession 实例。SqlSession 对象中完全包含以数据库为背景的所有执行 SQL 操作的方法,用该实例可以直接执行已映射的 SQL 语句。
**需要注意的是:**每个线程都有自己的 SqlSession 实例,SqlSession 实例不能被共享,也不是线程安全的。因此 SqlSession 的作用域范围是 request 作用域或方法体作用域内。
提及request回想一下上个阶段学习过的重定向和转发。重定向不是同一个请求,是两个请求,不会共享request作用域。
1、SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 会根据配置信息或者代码生成 SqlSessionFactory,并且提供了多个 build() 方法重载,这些方法都是在调用同一签名方法,即:
build(Reader reader, String environment, Properties properties)
由于参数 environment 和 properties 都可以为 null,去除重复的方法,真正的重载方法其实只有如下三种:
- build(InputStream inputStream, String environment, Properties properties)
- build(Reader reader, String environment, Properties properties)
- build(Configuration config)
通过上述分析,发现配置信息可以以三种形式提供给 SqlSessionFactoryBuilder 的 build() 方法,分别是 InputStream(字节流)、Reader(字符流)、Configuration(类)。
由于字节流和字符流都属于读取配置文件的方式,所以就很容易想到构建一个 SqlSessionFactory 有两种方式,即:读取 XML 配置文件和编写代码。一般习惯为采取 XML 配置文件的方式来构造 SqlSessionFactory,这样一方面可以避免硬编码,另一方面方便日后配置人员修改,避免重复编译代码。
1.1 SqlSessionFactoryBuilder的生命周期和作用域
SqlSessionFactoryBuilder 的最大特点就是用过即丢。创建 SqlSessionFactory 对象后,这个类就不存在,因SqlSessionFactoryBuilder 的最佳范围就是存在于方法体内,也就是局部变量。
2、SqlSessionFactory
SqlSessionFactory 是工厂接口而不是现实类,他的任务就是创建 SqlSession;所有的 MyBatis 应用都以 SqlSessionFactory 实例为中心,SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 对象来获取
2.1 SqlSessionFactory的生命周期和作用域
SqlSessionFactory 对象一旦创建,就会在整个应用程序过程中始终存在。没有理由去销毁或再创建它,并且在应用程序运行中也不建议多次创建 SqlSessionFactory。因此 SqlSessionFactory 的最佳作用域是 Application,即随着应用程序的生命周期一直存在。这种“存在于整个应用运行期间,并且只存在一个对象实例”的模式就是所谓的单例模式(指在运行期间有且仅有一个实例)。
3、SqlSession
SqlSession 是用于执行持久化操作的对象,类似于 JDBC 中的 Connection。它提供了面向数据库执行 SQL 命令所需的所有方法,可以通过 SqlSession 实例直接运行已映射的 SQL 语句。
SqlSession 的用途主要有两种:
- 获取映射器。让映射器通过命名空间和方法名称找到对应的 SQL,并发送给数据库,执行后返回结果。
- 直接通过“命名空间(namespace)+SQL id”的方式执行 SQL,不需要获取映射器。
3.1 SqlSession生命周期和作用域
SqlSession 对应一次数据库会话。由于数据库会话不是永久的,因此 SqlSession 的生命周期也不是永久的,每次访问数据库时都需要创建 SqlSession 对象。建议封装方法回收资源。
四、配置文件
在第一个MyBatis程序中,我们已经编写了 MyBatis 的配置文件mybatis-config.xml
,下面我们详细的说一下该配置文件中的内容
1、MyBatis 配置文件的结构
**注意事项:**mybatis-config.xml 文件中的元素节点是有一定顺序的,节点位置必须按以上位置排序,否则会编译错误。
<?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 /><!-- 属性 -->
<settings /><!-- 设置 -->
<typeAliases /><!-- 类型命名 -->
<typeHandlers /><!-- 类型处理器 -->
<objectFactory /><!-- 对象工厂 -->
<plugins /><!-- 插件 -->
<environments><!-- 配置环境 -->
<environment><!-- 环境变量 -->
<transactionManager /><!-- 事务管理器 -->
<dataSource /><!-- 数据源 -->
</environment>
</environments>
<databaseIdProvider /><!-- 数据库厂商标识 -->
<mappers /><!-- 映射器 -->
</configuration>
1.1 configuration标签
configuration 元素是整个 XML 配置文件的根节点,其角色就相当于是 MyBatis 的总管,MyBatis 所有的配置信息都会存放在它里面。
1.2 properties标签
properties 标签可以通过 resource 属性指定外部 properties 文件(dbconfig.properties),也可以通过 properties 子元素配置。如下
<!--驱动-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--获取连接对象地址-->
<property name="url" value="jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8"/>
<!--数据库用户名-->
<property name="username" value="root"/>
<!--数据库密码-->
<property name="password" value="112112"/>
在 environments 节点中引用以上变量
<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>
</environments>
mybatis3.4.2版本开始,提供属性占位符的功能(了解)
默认是关闭状态,手动开启设置
1、设置开启<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
如果你在属性名中使用了 “:” 字符(如:db:username),或者在 SQL 映射中使用了 OGNL 表达式的三元运算符
(如: t a b l e N a m e ! = n u l l ? t a b l e N a m e : ′ g l o b a l c o n s t a n t s ′ ),就需要设置特定的属性来修改分隔属性名和默认值的字符 2 、设置其他配置 ‘ < p r o p e r t y n a m e = " o r g . a p a c h e . i b a t i s . p a r s i n g . P r o p e r t y P a r s e r . d e f a u l t − v a l u e − s e p a r a t o r " v a l u e = " ? : " / > ‘ 3 、修改回去账号 p r o p e r t y ‘ < p r o p e r t y n a m e = " u s e r n a m e " v a l u e = " {tableName != null ? tableName : 'global_constants'}),就需要设置特定的属性来修改分隔属性名和默认值的字符 2、设置其他配置`<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/>` 3、修改回去账号property` <property name="username" value=" tableName!=null?tableName:′globalconstants′),就需要设置特定的属性来修改分隔属性名和默认值的字符2、设置其他配置‘<propertyname="org.apache.ibatis.parsing.PropertyParser.default−value−separator"value="?:"/>‘3、修改回去账号property‘<propertyname="username"value="{username?:root}"/> 4、这样,如果没有设置
`,就是用root
1.3 settings标签
settings 标签用于配置 MyBatis 的运行时行为,它能深刻的影响 MyBatis 的底层运行,一般不需要大量配置,大部分情况下使用其默认值即可。大多数情况用它配置日志、开启自动驼峰命名规则映射、开启二级缓存(默认开启)。
1.3.1 开启自动驼峰命名规则映射
<settings>
<!-- 开启驼峰命名规则 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
1.3.2 配置日志
<settings>
<!-- 配置log4j日志 -->
<setting name="logImpl" value="LOG4J"/>
</settings>
1.3.3 开启二级缓存
<settings>
<!--开启二级缓存,默认是开启的,显示定义出来-->
<setting name="cacheEnabled" value="true"/>
</settings>
1.4 typeAliases标签
为了不在任何地方都指定类的全限定名,我们可以使用 typeAliases 标签定义一个别名。分为两种方式:
- 对某个类起别名
<typeAliases>
<!--方式一,逐个设置,大小写不受影响-->
<typeAlias type="com.hqyj.cl.pojo.User" alias="User"/>
<typeAlias type="com.hqyj.cl.pojo.Person" alias="Person"/>
</typeAliases>
- 对某个文件夹下的内容起别名
<typeAliases>
<!--方式二,统一设置
在没有注解@Alias()的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。
比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值-->
<package name="com.hqyj.cl.pojo"/>
</typeAliases>
1.5 typeHandlers标签
typeHandlers 主要将获取的值合理地转化为 Java 类型。在 typeHandler 中,分为 jdbcType 和 javaType,其中 jdbcType 用于定义数据库类型,而 javaType 用于定义 Java 类型,typeHandler 的作用就是承担 jdbcType 和 javaType 之间的相互转换。我们不需要手动设置,因为MyBatis有默认类型处理器。
1.6 environments标签
在 environments 标签中,可以配置 MyBatis 的多套运行环境,将 SQL 映射到多个不同的数据库上。必须指定一个默认的运行环境(通过default指定),目前我们只需要配置一套MySQL就行。
1.6.1 transactionManager标签
MyBatis 支持两个事务管理器,即 JDBC 和 MANAGED。我们选择JDBC。
1.6.2 dataSource标签
用于配置数据库的连接属性,例如要连接的数据库的驱动程序名称、URL、用户名和密码等。有三个值(UNPOOLED、POOLED、JNDI),我们选择POOLED
<!--数据库连接源-->
<dataSource type="POOLED">
<!--获取配置驱动-->
<property name="driver" value="${driver}"/>
<!--获取配置url-->
<property name="url" value="${url}"/>
<!--获取配置账号-->
<property name="username" value="${username!=null?username:root}"/>
<!--获取配置密码-->
<property name="password" value="${password}"/>
</dataSource>
1.7resultMap映射(Mapper.xml文件中)
<!--定义结果集映射-->
<resultMap id="userMap" type="com.hqyj.pojo.User">
<!--id主键 使用id标签-->
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
</resultMap>
1.9 mappers标签
mappers 标签用于指定 MyBatis SQL 映射文件的路径。
1.9.1 mapper标签
mapper 是 mappers 的子标签,mapper 中的 resource 属性用于指定 SQL 映射文件的路径(类资源路径),有多种方式,这里使用其中三种
<!--映射器,映射器的 XML 映射文件包含了 SQL 代码和映射定义信息-->
<mappers>
<!--使用相对于类路径的资源引用,这是相对于类路径,UserMapper.xml存在resources下的mapper包中-->
<mapper resource="mapper/UserMapper.xml"/>
<!-- 使用映射器接口实现类的完全限定类名 mapper文件必须和接口在同一个包下,类名和mapper文件名必须相同-->
<mapper class="com.hqyj.cl.mapper.UserMapper"/>
<!-- 将包内的映射器接口实现全部注册为映射器 只需要指定到包名即可,mapper文件必须和接口在同一个包下,接口名和mapper文件名必须相同-->
<package name="com.hqyj.cl.mapper"/>
</mappers>
五、Mapper(映射器)
映射器是 MyBatis 中最重要的文件,文件中包含一组 SQL 语句(例如查询、添加、删除、修改),这些语句称为映射语句或映射 SQL 语句。
映射器由 Java 接口和 XML 文件(或注解)共同组成,它的作用如下:
- 定义参数类型
- 配置缓存,默认是一级缓存
- 提供 SQL 语句和动态 SQL
- 定义查询结果和 POJO 的映射关系
映射器有以下两种实现方式
- 通过 XML 文件方式实现,比如我们在 mybatis-config.xml 文件中描述的 XML 文件,用来生成 mapper。推荐方式。
- 通过注解的方式实现,使用 Configuration 对象注册 Mapper 接口。
1、XML实现映射器
XML 定义映射器分为两个部分:接口和XML。
1.1 定义接口
public interface UserMapper {
List<User> getUserList();
}
1.2 定义UserMapper.xml
<?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.hqyj.cl.mapper.UserMapper">
<select id="getUserList" resultType="com.hqyj.cl.pojo.User">
select * from `user`
</select>
</mapper>
1.2.1 说明
- namespace 用来定义命名空间,该命名空间和定义接口的全限定名一致。
<select>
元素表明这是一条查询语句,属性 id 用来标识这条 SQL。resultType 表示返回的是一个 User类型的值。
1.3 修改 MyBatis 配置文件
在 MyBatis 配置文件中添加以下代码
<!--该语句用来引入 XML 文件,MyBatis 会读取 UserMapper.xml 文件,生成映射器-->
<mapper resource="com/hqyj/cl/mapper/UserMapper.xml"/>
1.4 测试代码
@Test
public void getUserList() throws IOException {
// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通过SqlSession对象获取StudentMapper的代理对象
StudentMapper mapper = sqlSession.getMapper(UserMapper.class);
// 使用代理mapper对象查询全部的用户信息
List<User> userList = mapper.getUserList();
System.out.println(userList);
}
2、注解实现映射器
使用注解的方式实现映射器,只需要在接口中使用 Java 注解,注入 SQL 即可
2.1 测试代码
public interface UserMapper {
@Select(value = "select * from user")
public List<User> getUserList();
}
2.1.1 说明
- 我们使用了 @Select 注解,并且注入了和 XML 中相同的 select 语句。
- 如果使用注解和 XML 文件两种方式同时定义,那么 XML 方式将覆盖掉注解方式。(只使用一种方式配置,有可能会报错)
- 简单的语句,可以使用注解方式,复杂语句,推荐使用XML方式。
3、MyBatis 映射器的主要元素
后续学习中会逐一讲解
元素名称 | 描述 | 备注 |
---|---|---|
mapper | 映射文件的根节点,只有 namescape 一个属性 | namescape 作用如下:用于区分不同的 mapper,全局唯一绑定DAO接口,即面向接口编程。当 namescape 绑定某一接口后,可以不用写该接口的实现类,MyBatis 会通过接口的完整限定名查找到对应的 mapper 配置来执行 SQL 语句。因此 namescape 的命名必须要跟接口同名。 |
select | 查询语句,最常用、最复杂的元素之一 | 可以自定义参数,返回结果集等 |
insert | 插入语句 | 执行后返回一个整数,代表插入的条数 |
update | 更新语句 | 执行后返回一个整数,代表更新的条数 |
delete | 删除语句 | 执行后返回一个整数,代表删除的条数 |
parameterMap | 定义参数映射关系 | 即将被删除的元素,不建议使用 |
sql | 允许定义一部分的 SQL,然后在各个地方引用它 | 例如,一张表列名,我们可以一次定义,在多个 SQL 语句中使用 |
resultMap | 用来描述数据库结果集与对象的对应关系,它是最复杂、最强大的元素 | 提供映射规则 |
cache | 配置给定命名空间的缓存 | - |
cache-ref | 其它命名空间缓存配置的引用 | - |
六、MyBatis执行SQL的两种方式
MyBatis 有两种执行 SQL 语句的方式,如下:
- 通过 SqlSession 发送 SQL
- 通过 SqlSession 获取 Mapper 接口,通过 Mapper 接口发送 SQL
1、SqlSession
有映射器之后就可以通过 SqlSession 发送 SQL 了,MyBatis 常用的查询方法有 2 种,分别为 selectOne 和 selectList。
1.1 selectOne
selectOne 方法表示使用查询并且只返回一个对象,必须指定查询条件。只能查询 0 或 1 条记录,大于 1 条记录则运行错误。常用格式如下(也有其它重载方法,根据需要选择)
sqlSession.selectOne(String arg0, Object arg1)
1.2 selectList
selectList 方法表示使用查询并且返回一个列表。可以查询 0 或 N 条记录。常用格式如下
sqlSession.selectList(String arg0, Object arg1)
1.3 说明
- 以上语法格式中,String 对象由一个命名空间加 SQL id 组合而成,它完全定位了一条 SQL,这样 MyBatis 就会找到对应的 SQL。Object 对象为需要传递的参数,也就是查询条件
- selectOne 实现的 selectList 都可以实现,即 list 中只有一个对象。但 selectList 能实现的,selectOne 不一定能实现
- 如果 MyBatis 中只有一个 id 为 getUserList 的 SQL,那么可以去掉包路径名
2、Mapper接口
SqlSession 还可以获取 Mapper 接口,通过 Mapper 接口发送 SQL,如下所示
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
2.1 说明
- 通过 SqlSession 的 getMapper 方法获取一个 Mapper 接口,然后就可以调用它的方法了。因为 XML 文件或者接口注解定义的 SQL 都可以通过“类的全限定名+方法名”查找,所以 MyBatis 会启用对应的 SQL 运行,并返回结果。
七、CRUD标签
只有 select 标签有 resultType 属性,因为只有查询操作才需要对返回结果类型进行相应的指定。
1、select标签
在 MyBatis 中,select 标签是最常用也是功能最强大的 SQL 语言标签,用于执行查询操作。格式如下
<select id="queryUserByUsername" resultType="user" parameterType="string">
select * from `user` where `username` like concat ('%',#{username},'%')
</select>
1.1 说明
- 执行 SQL 语句时可以定义参数,参数可以是一个简单的参数类型,例如 int、float、String;也可以是一个复杂的参数类型,例如JavaBean、Map 等。MyBatis 提供了强大的映射规则,执行 SQL 后,MyBatis 会将结果集自动映射到 JavaBean 中。
- 为了使数据库的查询结果和返回值类型中的属性能够自动匹配,通常会对 MySQL 数据库和 JavaBean 采用同一套命名规则,即 Java 命名驼峰规则,这样就不需要再做映射了(数据库表字段名和属性名不一致时需要手动映射)
- 参数的传递使用
#{参数名}
,相当于告诉 MyBatis 生成 PreparedStatement 参数。
1.2 常用属性
属性名称 | 描 述 | 备注 |
---|---|---|
id | 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 | 如果命名空间+id不唯一,那么 MyBatis 抛出异常 |
parameterType | 表示传入 SQL 语句传入参数类型的全限定名或别名。它是一个可选属性,MyBatis 能推断出具体传入语句的参数 | 支持基本数据类型和 JavaBean、Map 等复杂数据类型 |
resultType | SQL 语句执行后返回的类型(全限定名或者别名)。如果是集合类型,返回的是集合元素的类型,返回时可以使用 resultType 或 resultMap 之一 | - |
resultMap | 它是映射集的引用,与 <resultMap> 元素一起使用,返回时可以使用 resultType 或 resultMap 之一 |
是 MyBatis 最复杂的元素,可以配置映射规则、级联、typeHandler 等 |
flushCache | 用于设置在调用 SQL 语句后是否要求 MyBatis 清空之前查询的本地缓存和二级缓存 | 默认值为 false,如果设置为 true,则任何时候只要 SQL 语句被调用都将清空本地缓存和二级缓存 |
useCache | 启动二级缓存的开关,默认值为 true,表示将査询结果存入二级缓存中 | - |
timeout | 用于设置超时参数,单位是秒(s),超时将抛出异常 | - |
fetchSize | 获取记录的总条数设定 | 默认值是数据库厂商提供的 JDBC 驱动所设置的条数 |
statementType | 告诉 MyBatis 使用哪个 JDBC 的 Statement 工作,取值为 STATEMENT(Statement)、 PREPARED(PreparedStatement)、CALLABLE(CallableStatement) | - |
resultSetType | 这是针对 JDBC 的 ResultSet 接口而言,其值可设置为 FORWARD_ONLY(只允许向前访问)、SCROLL_SENSITIVE(双向滚动,但不及时更新)、SCROLLJNSENSITIVE(双向滚动,及时更新) | - |
1.3 多参数
有的时候,我们需要传递多个参数来执行SQL语句,比如用户登录;这个时候,有三种方式可以实现,分别是使用Map传递参数、使用注解传递参数、使用JavaBean传递参数;考虑到性能等问题,不推荐使用Map传递参数,所以,我们现在对后面两种情况进行分析
1.3.1 使用注解传递参数
使用 MyBatis 的注解 @Param() 传递参数,如下所示
1.3.1.2 案例
- 接口
//用户登录
User login(@Param("username")String username,@Param("password")String password);
- Mapper
<!--用户登录-->
<select id="login" resultType="User">
select * from user where username = #{username} and password = #{password}
</select>
1.3.1.2 说明
当我们把参数传递给后台时,MyBatis 通过 @Param 提供的名称就会知道 #{name} 代表 name 参数,提高了参数可读性。但是如果这条 SQL 拥有 10 个参数的查询,就会造成可读性下降,增强了代码复杂性。
1.3.2 使用JavaBean传递参数
在参数过多的情况下,MyBatis 允许组织一个 JavaBean,通过简单的 setter 和 getter 方法设置参数,提高可读性。如下所示
1.3.2.1 案例
- 接口
//增加用户
int selectUser(User user);
- Mapper
<!--增加用户-->
<select id="selectUser" resultType="User">
select * from user where username = #{username} and password = #{password} and position = #{position}
</select>
1.3.3 区别
以上 2 种方式的区别如下。
- 使用 @Param 注解传递参数会受到参数个数的影响。当 n≤5 时,它是最佳的传参方式,因为它更加直观;当 n>5 时,多个参数将给调用带来困难。
- 当参数个数大于 5 个时,建议使用 JavaBean 方式。
2、insert标签
MyBatis insert 标签用来定义插入语句,执行插入操作。当 MyBatis 执行完一条插入语句后,就会返回其影响数据库的行数。
2.1 案例
通过insert标签实现添加用户功能
- 接口
//增加用户
int addUser(User user);
- Mapper
<!--增加用户-->
<insert id="addUser">
insert into user values (null,#{username},#{password},#{userPhone},#{createTime},#{position})
</insert>
测试类
sqlSession.commit(); 需要commit
2.2 常用属性
属性名称 | 描述 | 备注 |
---|---|---|
id | 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 | 如果命名空间+ id 不唯一,那么 MyBatis 抛出异常 |
parameterType | 传入 SQL 语句的参数类型的全限定名或别名,它是一个可选属性。 | 支持基本数据类型和 JavaBean、Map 等复杂数据类型 |
keyProperty | 该属性的作用是将插入操作的返回值赋给 PO 类的某个属性,通常为主键对应的属性。如果是联合主键,可以将多个值用逗号隔开。 | - |
useGeneratedKe | 该属性用来设置,是否使用 JDBC 提供的 getGenereatedKeys() 方法,获取数据库内部产生的主键并赋值到 keyProperty 属性设置的请求对象的属性中,例如 MySQL、SQL Server 等自动递增的字段,其默认值为 false。 | 该属性值设置为 true 后,会将数据库生成的主键回填到请求对象中,以供其他业务使用。 |
flushCache | 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。 | - |
timeout | 该属性用于设置执行该操作的最大时限,如果超时,就抛异常。 | - |
databaseId | 取值范围 oracle、mysql 等,表示数据库厂家;元素内部可通过 <if test="_databaseId = 'oracle'"> 来为特定数据库指定不同的 sql 语句。 |
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。 |
keyColumn | 该属性用于设置第几列是主键,当主键列不是表中的第 1 列时,就需要设置该属性。如果是联合主键,可以将多个值用逗号隔开。 | - |
2.3 多参数
和select标签规则一样
3、update标签
MyBatis update 标签用于定义更新语句,执行更新操作。当 MyBatis 执行完一条更新语句后,会返回一个整数,表示受影响的数据库记录的行数。
3.1 案例
- 接口
//通过用户姓名修改用户
int updateUserByUserName(User user);
- Mapper
<!--通过用户姓名修改用户-->
<update id="updateUserByUsername">
update user set phone = #{phone},password = #{password} where username = #{username}
</update>
3.2 常用属性
属性名称 | 描述 | 备注 |
---|---|---|
id | 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 | 如果命名空间+ id 不唯一,那么 MyBatis 抛出异常 |
parameterType | 传入 SQL 语句的参数类型的全限定名或别名,它是一个可选属性。 | 支持基本数据类型和 JavaBean、Map 等复杂数据类型 |
flushCache | 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。 | - |
timeout | 该属性用于设置 SQL 执行的超时时间,如果超时,就抛异常。 | - |
statementType | 执行 SQL 时使用的 statement 类型, 默认为 PREPARED,可选值:STATEMENT,PREPARED 和 CALLABLE。 | - |
3.3 多参数
和select标签规则一样
4、delete标签
MyBatis delete 标签用于定义 delete 语句,执行删除操作。当 MyBatis 执行完一条更新语句后,会返回一个整数,表示受影响的数据库记录的行数。
4.1 案例
- 接口
//删除某个用户
int delUserById(int id);
- Mapper
<!--删除某个用户-->
<delete id="delUserById">
delete from user where id = #{id}
</delete>
4.2 常用属性
属性名称 | 描述 | 备注 |
---|---|---|
id | 它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用 | 如果命名空间+ id 不唯一,那么 MyBatis 抛出异常 |
parameterType | 传入 SQL 语句的参数类型的全限定名或别名,它是一个可选属性。 | 支持基本数据类型和 JavaBean、Map 等复杂数据类型 |
flushCache | 该属性用于设置执行该操作后,是否会清空二级缓存和本地缓存,默认值为 true。 | - |
timeout | 该属性用于设置 SQL 执行的超时时间,如果超时,就抛异常。 | - |
statementType | 执行 SQL 时使用的 statement 类型, 默认为 PREPARED,可选值:STATEMENT,PREPARED 和 CALLABLE。 | - |
4.3 多参数
和select标签规则一样
八、resultMap
resultMap 是 MyBatis 中最复杂的元素,主要用于解决实体类属性名与数据库表中字段名不一致的情况,可以将查询结果映射成实体对象。现有的 MyBatis 版本只支持 resultMap 查询,我们通过案例分析
1、构成
<resultMap id="" type="">
<constructor><!-- 类再实例化时用来注入结果到构造方法 -->
<idArg/><!-- ID参数,结果为ID -->
<arg/><!-- 注入到构造方法的一个普通结果 -->
</constructor>
<id/><!-- 用于表示哪个列是主键 -->
<result/><!-- 注入到字段或JavaBean属性的普通结果 -->
<association property=""/><!-- 用于一对一关联 -->
<collection property=""/><!-- 用于一对多、多对多关联 -->
<discriminator javaType=""><!-- 使用结果值来决定使用哪个结果映射 -->
<case value=""/><!-- 基于某些值的结果映射 -->
</discriminator>
</resultMap>
1.1 说明
<resultMap>
元素的 type 属性表示需要的 POJO,id 属性是 resultMap 的唯一标识。- 子元素
<constructor>
用于配置构造方法。当一个 POJO 没有无参数构造方法时使用。一般不用 - 子元素
<id>
用于表示哪个列是主键。允许多个主键,多个主键称为联合主键。 - 子元素
<result>
用于表示 POJO 和 SQL 列名的映射关系。 - 子元素
<association>
、<collection>
和<discriminator>
用在级联的情况下。后续学习
id 和 result 元素都有以下属性
元素 | 说明 |
---|---|
property | 映射到列结果的字段或属性。如果 POJO 的属性和 SQL 列名(column元素)是相同的,那么 MyBatis 就会映射到 POJO 上 |
column | 对应 SQL 列 |
javaType | 配置 Java 类型。可以是特定的类完全限定名或 MyBatis 上下文的别名 |
jdbcType | 配置数据库类型。这是 JDBC 类型,MyBatis 已经为我们做了限定,基本支持所有常用数据库类型 |
typeHandler | 类型处理器。允许你用特定的处理器来覆盖 MyBatis 默认的处理器。需要指定 jdbcType 和 javaType 相互转化的规则 |
1.2 获取结果集的两种方式
1.2.1 使用Map存储结果集
不推荐使用该方式
- 接口
// 用户列表
List<Map<String,Object>> queryAllUser();
- Mapper
<select id="selectAllUser" resultType="map">
select * from `user`
</select>
**注意事项:**Map 的 key 是 select 语句查询的字段名(必须完全一样),而 Map 的 value 是查询返回结果中字段对应的值,一条记录映射到一个 Map 对象中。
1.2.2 使用POJO存储结果集
推荐使用该方式
- POJO类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String username;
private String password;
}
- 接口
public interface UserMapper {
List<User> getUserList();
}
- Mapper
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hqyj.cl.dao.UserDao">
<resultMap id="userMap" type="User">
<id column="id" property="id"/>
<result column="username" property="userName"/>
<result column="password" property="password"/>
</resultMap>
<!--查询用户列表-->
<select id="getUserList" resultMap="userMap">
select * from `user`
</select>
</mapper>
1.2.2.1 说明
- resultMap 元素的属性 id 代表这个 resultMap 的标识,type 标识需要映射的 POJO。我们可以使用 MyBatis 定义好的类的别名或自定义类的全限定名。
- 使用 property 元素指定 User的属性名称 username,column 表示数据库中 user表的 SQL 列名 username,将 POJO 和 SQL 的查询结果一 一对应。
- MyBatis 的每一个查询映射的返回类型都是 resultMap,只是当我们提供的返回类型是 resultType 时,MyBatis 会自动把对应的值赋给 resultType 所指定对象的属性,而当我们提供的返回类型是 resultMap 时,MyBatis 会将数据库中的列数据复制到对象的相应属性上,可用于复制查询。
- resultMap 和 resultType 不能同时使用。
九、注解
为了简化 XML 的配置,MyBatis 提供了注解,这些注解主要分为三大类,即 SQL 语句映射、结果集映射和关系映射。我们就逐一学习
1、SQL 语句映射
1.1 @Insert
实现新增功能
@Insert("insert into user(name,sex,age) values(#{name},#{sex},#{age})")
int addUser(User user);
1.2 @SelectKey
插入后,获取id的值
@Insert("insert into user(id,name) values(#{id},#{name})")
@SelectKey(statement = "select last_insert_id()", keyProperty = "id", keyColumn = "id", resultType = int.class,before = false)
int insert(User user);
@SelectKey 各个属性含义如下
- statement:表示要运行的 SQL 语句;
- keyProperty:可选项,表示将查询结果赋值给代码中的哪个对象;
- keyColumn:可选项,表示将查询结果赋值给数据表中的哪一列;
- resultType:指定 SQL 语句的返回值;
- before:默认值为 true,在执行插入语句之前,执行 select last_insert_id()。值为 flase,则在执行插入语句之后,执行 select last_insert_id()。
1.3 @Select
实现查询功能
增加templateMapper.xml文件在mapper包中 namespace属性空着
mybatis-config.xml中要扫描包
@Select("Select * from user")
@Results({
@Result(id = true, column = "id", property = "id"),
@Result(column = "name", property = "name"),
@Result(column = "sex", property = "sex"),
@Result(column = "age", property = "age")
})
List<User> queryAllUser();
@Results配置映射关系,如果mybatis-config.xml中关闭了驼峰命名,数据库字段与pojo类成员变量不能自动映射可以使用该注解
1.4 @Update
实现更新功能
@Update("update user set name= #{name},sex = #{sex},age =#{age} where id = #{id}")
void updateUserById(User user);
1.5 @Delete
实现删除功能
@Delete("delete from user where id =#{id}")
void deleteById(Integer id);
1.6 @Param
用于在 Mapper 接口中映射多个参数,之前已经说明并演示
2、结果集映射
@Result、@Results、@ResultMap 是结果集映射的三大注解。
2.1 案例
@Select({
"select id, name, class_id from student"})
@Results(id="studentMap", value={
@Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
@Result(column="name", property="name", jdbcType=JdbcType.VARCHAR),
@Result(column="class_id ", property="classId", jdbcType=JdbcType.INTEGER)
})
List<Student> selectAllStudent();
@Results 各个属性的含义
- id:表示当前结果集声明的唯一标识,可以通过id重复利用;
- value:表示结果集映射关系;
- @Result:代表一个字段的映射关系。其中,column 指定数据库字段的名称,property 指定实体类属性的名称,jdbcType 数据库字段类型,id 为 true 表示主键,默认 false。
使用 @ResultMap 来引用映射结果集,其中 value 可省略
@Select({
"select id, name, class_id from student where id = #{id}"})
@ResultMap(value="studentMap")
Student selectById(int id);
3、关系映射
基础学生类增加成员变量修改数据类型,记得要重构有参构造方法。
3.1 @one
用于一对一关系映射
@Select("select * from stu")
@Results({
@Result(id=true,property="id",column="id"),
@Result(property="student_name",column="name"),
@Result(property="addr",column="fk_addr_id",one=@One(select="com.hqyj.cl.mapper.StudentMapper.getAddr"))
})
List<Student> getAllStudents();
@Select("select * from addr where id = #{id}")
@Results({
@Result(id=true,property="id",column="id"),
@Result(property="addrName",column="addr_name")
})
Addr getAddr(int id);
3.2 @many
用于一对多关系映射
@Select("select * from stu")
@Results({
@Result(id=true,property="id",column="id"),
@Result(property="student_name",column="name"),
@Result(property="teacher",column="fk_teacher_id",many=@Many(select="com.hqyj.cl.mapper.StudentMapper.getTeacher"))
})
List<Student> getAllStudents();
@Select("select * from teacher where id = #{id}")
@Results({
@Result(id=true,property="id",column="id"),
@Result(property="teacherName",column="teacher_name")
})
List<Teacher> getTeacher(int id);
3.3 多对多
在一对多的基础上,增加一个外表student_teacher,实现对student表和teacher表的关联
@Select("select * from stu")
@Results({
@Result(id=true,property="id",column="id"),
@Result(property="student_name",column="name"), @Result(property="teacher",column="fk_teacher_id",many=@Many(select="com.hqyj.cl.mapper.StudentMapper.getTeacher"))
})
List<Student> getAllStudents();
@Select("select * from teacher t,student_fk_teacher st where t.id = st.teacher_id and st.student_id = #{id}")
@Results({
@Result(id=true,property="id",column="id"),
@Result(property="teacherName",column="teacher_name"),
})
List<Teacher> getTeacher(int id);
十、MyBatis关联(级联)查询
级联关系是一个数据库实体的概念,有 3 种级联关系,分别是一对一级联、一对多级联以及多对多级联。比如一个人对应一张身份证,一张身份证对应一个人、一个班级拥有多个学生,一个学生只能够属于某个班级、 一个学生可以选修多门课程,一个课程可以被多个学生选修。
1、一对一
在 MyBatis 中,通过 <resultMap>
元素的子元素 <association>
处理一对一级联关系。示例代码如下
POJO类和数据库设计,Student、Teacher、Addr三个类
- Student类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String studentName;
private Addr addr;
private List<Teacher> teacher;
}
- Addr类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Addr {
private int id;
private String addr;
}
- 接口
List<Student> selectAllStudent();
- Mapper
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hqyj.cl.mapper.StudentMapper">
<resultMap id="studentMap" type="Student">
<id property="id" column="id"/>
<result property="studentName" column="student_name"/>
<association property="addr">
<id property="id" column="id"/>
<result property="addr" column="addr"/>
</association>
</resultMap>
<select id="selectAllStudent" resultMap="studentMap">
select * from `stu` s,`addr` a where s.fk_addr_id = a.id
</select>
</mapper>
- 测试
@Test
public void getAllUser() throws IOException {
// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.selectAllStudent();
// SqlSession执行文件中定义的SQL,并返回映射结果
System.out.println(studentList);
}
在 <association>
元素中通常使用以下属性。
- property:指定映射到实体类的对象属性。
- column:指定表中对应的字段(即查询返回的列名)。
- javaType:指定映射到实体对象属性的类型。
- select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。
2、一对多
在 MyBatis 中,通过 <resultMap>
元素的子元素 <collection>
处理一对多级联关系,collection 可以将关联查询的多条记录映射到一个 list 集合属性中。示例代码如下
- Teacher类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String teacherName;
}
- 接口
List<Student> selectAllStudent();
- Mapper
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hqyj.cl.mapper.StudentMapper">
<resultMap id="studentMap" type="Student">
<id property="id" column="id"/>
<result property="studentName" column="student_name"/>
<collection property="teacher" ofType="teacher">
<id property="id" column="id"/>
<result property="teacherName" column="teacher_name"/>
</collection>
</resultMap>
<select id="selectAllStudent" resultMap="studentMap">
select * from `stu` s,`addr` a,`teacher` t where s.fk_teacher_id = t.id
</select>
</mapper>
- 测试
@Test
public void getAllUser() throws IOException {
// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.selectAllStudent();
// SqlSession执行文件中定义的SQL,并返回映射结果
System.out.println(studentList);
}
在 <collection>
元素中通常使用以下属性。
- property:指定映射到实体类的对象属性。
- column:指定表中对应的字段(即查询返回的列名)。
- javaType:指定映射到实体对象属性的类型。
- select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。
3、多对多
实际应用中,由于多对多的关系比较复杂,会增加理解和关联的复杂度,所以应用较少。MyBatis 没有实现多对多级联,推荐通过两个一对多级联替换多对多级联,以降低关系的复杂度,简化程序。
十一、MyBatis动态SQL
动态 SQL 是 MyBatis 的强大特性之一。在 JDBC 或其它类似的框架中,开发人员通常需要手动拼接 SQL 语句。根据不同的条件拼接 SQL 语句是一件极其痛苦的工作。动态 SQL 大大减少了编写代码的工作量,更体现了 MyBatis 的灵活性、高度可配置性和可维护性。MyBatis 也可以在注解中配置 SQL,但是由于注解功能受限,且对于复杂的 SQL 语句来说可读性差,所以使用较少。
1、动态SQL元素
MyBatis 的动态 SQL 包括以下几种元素
元素 | 作用 | 备注 |
---|---|---|
if | 判断语句 | 单条件分支判断 |
choose(when、otherwise) | 相当于 Java 中的 switch case 语句 | 多条件分支判断 |
trim、where | 辅助元素 | 用于处理一些SQL拼装问题 |
foreach | 循环语句 | 在in语句等列举条件常用 |
bind | 辅助元素 | 拼接参数 |
1.1 if标签
MyBatis if 类似于 Java 中的 if 语句,是 MyBatis 中最常用的判断语句。
1.1.1 格式
<if test="判断条件">
<!--条件为true时执行SQL语句-->
SQL语句
</if>
1.1.2 案例
- 接口
List<User> selectAllUser(User user);
- Mapper
<select id="selectAllUser" resultMap="userMap">
select * from user where 1=1
<if test="name != null">
and `name` like #{name}
</if>
<if test="state != 0">
and state like #{state}
</if>
</select>
- 测试
@Test
public void selectAllUser() throws IOException {
// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setName("王五");
List<User> userList = mapper.selectAllUser(user);
// SqlSession执行文件中定义的SQL,并返回映射结果
System.out.println(userList);
}
1.2 choose、when和otherwise标签
MyBatis 中动态语句 choose-when-otherwise 类似于 Java 中的 switch-case-default 语句。由于 MyBatis 并没有为 if 提供对应的 else 标签,如果想要达到<if>
…<else>
…</else> </if>
的效果,可以借助 <choose>
、<when>
、<otherwise>
来实现。
1.2.1 格式
choose 标签按顺序判断其内部 when 标签中的判断条件是否成立,如果有一个成立,则执行相应的 SQL 语句,choose 执行结束;如果都不成立,执行 otherwise 中的 SQL 语句。这类似于 Java 的 switch 语句,choose为switch,when 为 case,otherwise则为default。
<choose>
<when test="判断条件1">
SQL语句1
</when >
<when test="判断条件2">
SQL语句2
</when >
<when test="判断条件3">
SQL语句3
</when >
<otherwise>
SQL语句4
</otherwise>
</choose>
1.2.1 案例
- 接口
List<User> selectAllUser(User user);
- Mapper
<select id="selectAllUser" resultMap="userMap">
select * from user where 1=1
<choose>
<when test="name != null and name !=''">
AND name LIKE CONCAT('%',#{name},'%')
</when>
<when test="state != 0 and state == 1">
AND state = #{state}
</when>
<otherwise>
AND password is not null
</otherwise>
</choose>
</select>
- 测试
@Test
public void selectAllUser() throws IOException {
// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
List<User> userList = mapper.selectAllUser(user);
// SqlSession执行文件中定义的SQL,并返回映射结果
System.out.println(userList);
}
1.3 where标签
在上述标签SQL代码的编写中,我们不难发现,每一条SQL 语句中加入了一个条件“1=1”,如果没有加入这个条件,那么可能就会变成下面这样一条错误的语句。
SELECT * FROM user AND name LIKE CONCAT('%',#{name},'%')
显然以上语句会出现 SQL 语法异常,但加入“1=1”这样的条件又非常奇怪,所以 MyBatis 提供了 where 标签。
1.3.1 格式
where 标签主要用来简化 SQL 语句中的条件判断,可以自动处理 AND/OR 条件,语法如下
<where>
<if test="判断条件">
AND/OR ...
</if>
</where>
**注意事项:if 语句中判断条件为 true 时,where 关键字才会加入到组装的 SQL 里面,否则就不加入。where 会检索语句,它会将 where 后的第一个 SQL 条件语句**的 AND 或者 OR 关键词去掉。
1.3.2 案例
- 接口
List<User> selectAllUser(User user);
- Mapper
<select id="selectAllUser" resultMap="userMap">
select * from user
<where>
<if test="name != null">
name like #{name}
</if>
<if test="state != 0">
AND state like #{state}
</if>
</where>
</select>
- 测试
@Test
public void selectAllUser() throws IOException {
// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setName("admin");
List<User> userList = mapper.selectAllUser(user);
// SqlSession执行文件中定义的SQL,并返回映射结果
System.out.println(userList);
}
1.4 set标签
在 Mybatis 中,update 语句可以使用 set 标签动态更新列。set 标签可以为 SQL 语句动态的添加 set 关键字,剔除追加到条件末尾多余的逗号。
1.4.1 案例
- 接口
int updateUser(User user);
- Mapper
<update id="updateUser">
UPDATE user
<set>
<if test="password!=null">
password=#{password}
</if>
<if test="state==0 || state==1">
state=#{state}
</if>
</set>
WHERE name=#{name}
</update>
- 测试
@Test
public void updateUser() throws IOException {
// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setName("admin");
user.setState(1);
int result = mapper.updateUser(user);
// SqlSession执行文件中定义的SQL,并返回映射结果
System.out.println(result);
}
**注意事项:**if判断中必须成立一个,并且where后面的条件对应的值必须传递,否则报错
1.5 foreach标签
对于一些 SQL 语句中含有 in 条件,需要迭代条件集合来生成的情况,可以使用 foreach 来实现 SQL 条件的迭代。 Mybatis foreach 标签用于循环语句,它很好的支持了数据和 List、set 接口的集合,并对此提供遍历的功能。语法格式如下
<foreach item="item" index="index" collection="list|array|map key" open="(" separator="," close=")">
参数值
</foreach>
foreach 标签主要有以下属性,说明如下。
- item:表示本次迭代获取的元素,若collection为List、Set或者数组,则表示其中的元素;若collection为map,则代表key-value的value,该参数为必选
- index:在list、Set和数组中,index表示当前迭代的位置,在map中,index代指是元素的key,该参数是可选项。
- open:表示该语句以什么开始(既然是 in 条件语句,所以必然以
(
开始)。 - separator:表示在每次进行迭代之间以什么符号作为分隔符(既然是 in 条件语句,所以必然以
,
作为分隔符)。 - close:表示该语句以什么结束(既然是 in 条件语句,所以必然以
)
开始)。 - collection表示迭代集合的名称,可以使用@Param注解指定,有以下四种情况
- 如果传入的是单参数且参数类型是一个 List,collection 属性值为 list;
- 如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array;
- 如果如果传入的是单参数且参数类型是一个Set集合的时候,可以通过注解
@Param("set")
指定key值; - 如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。必须加上注解@Param指定。
1.5.1 案例
- 接口
List<User> selectUser(List<Integer> ageList);
- Mapper
<select id="selectUser" resultMap="userMap">
SELECT * FROM user WHERE age in
<foreach item="age" index="index" collection="list" open="("
separator="," close=")">
#{age}
</foreach>
</select>
- 测试
@Test
public void selectUser() throws IOException {
// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<Integer> ageList = new ArrayList<>();
ageList.add(1);
ageList.add(12);
ageList.add(13);
List<User> result = mapper.selectUser(ageList);
// SqlSession执行文件中定义的SQL,并返回映射结果
System.out.println(result);
}
1.6 bind标签
每个数据库的拼接函数或连接符号都不同,例如 MySQL 的 concat 函数。这样 SQL 映射文件就需要根据不同的数据库提供不同的实现,显然比较麻烦,且不利于代码的移植。幸运的是,MyBatis 提供了 bind 标签来解决这一问题。
MyBatis bind 标签可以通过 OGNL(对象导航图语言) 表达式自定义一个上下文变量。
bind 元素属性如下
- value:对应传入实体类的某个字段,可以进行字符串拼接等特殊处理。
- name:给对应参数取的别名。
1.6.1 案例
- 接口
List<User> queryUser(String name);
- Mapper
<select id="queryUser" resultMap="userMap">
<bind name="pattern" value="'%'+_parameter+'%'" />
SELECT * FROM `user`
WHERE name like #{pattern}
</select>
- 测试
@Test
public void queryUser() throws IOException {
// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.queryUser("a");
// SqlSession执行文件中定义的SQL,并返回映射结果
System.out.println(userList);
}
**注意事项:**以上代码中的“_parameter”代表传递进来的参数,它和通配符连接后,赋给了 pattern,然后就可以在 select 语句中使用这个变量进行模糊查询,提高了可移植性。
1.7 trim标签
在 MyBatis 中除了使用 if+where 实现多条件查询,还有一个更为灵活的元素 trim 能够替代之前的做法。trim 一般用于去除 SQL 语句中多余的 AND 关键字、逗号,
或者给 SQL 语句前拼接 where、set 等后缀,可用于选择性插入、更新、删除或者条件查询等操作。
1.7.1 格式
<trim prefix="前缀" suffix="后缀" prefixOverrides="忽略前缀字符" suffixOverrides="忽略后缀字符">
SQL语句
</trim>
1.7.1.1 属性说明
属性 | 描述 |
---|---|
prefix | 给SQL语句拼接的前缀,为 trim 包含的内容加上前缀 |
suffix | 给SQL语句拼接的后缀,为 trim 包含的内容加上后缀 |
prefixOverrides | 去除 SQL 语句前面的关键字或字符,该关键字或者字符由 prefixOverrides 属性指定。 |
suffixOverrides | 去除 SQL 语句后面的关键字或者字符,该关键字或者字符由 suffixOverrides 属性指定 |
1.7.2 案例
- 接口
List<User> selectAllUser(User user);
- Mapper
<select id="selectAllUser" resultMap="userMap">
select * from user
<trim prefix="where" prefixOverrides="and">
<if test="name != null">
and `name` like #{name}
</if>
<if test="state != 0">
and state like #{state}
</if>
</trim>
</select>
- 测试
@Test
public void selectAllUser() throws IOException {
// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
List<User> userList = mapper.selectAllUser(user);
// SqlSession执行文件中定义的SQL,并返回映射结果
System.out.println(userList);
}
十二、MyBatis分页功能
MyBatis 的分页功能是基于内存的分页,即先查询出所有记录,再按起始位置和页面容量取出结果。
1、pageHelper插件
在使用MyBatis分页功能时,通常和pageHelper插件配合使用
1.1 参数
PageInfo{
pageNum=1, //页数
pageSize=2, //页大小
size=2, 大小
startRow=1, //开始行
endRow=2, //结束行
total=6, //总条数
pages=3, //总页数
list=Page{
count=true, pageNum=1, pageSize=2, startRow=0, endRow=2, total=6, pages=3, reasonable=false, pageSizeZero=false},
prePage=0, //首页
nextPage=2, //下一页
isFirstPage=true, //是第一页
isLastPage=false, // 是最后一页
hasPreviousPage=false, //有上一页
hasNextPage=true, //有下一页
navigatePages=2, //导航页数
navigateFirstPage=1, //导航到第一页
navigateLastPage=2, //导航到最后一页
navigatepageNums=[1, 2]} //导航 页数 页码
1.2 使用步骤
- 导入依赖
<!--pageHelper-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.4</version>
</dependency>
- 配置mybatis-config.xml
在mybatis-config.xml中配置,注意顺序问题
<!-- 配置分页插件 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 设置数据库类型-->
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
- 接口
List<User> queryAllUser();
- Mapper
一定注意,不要在SQL语句后面加分号,否则MyBatis不能动态的正确拼接limt 分页语句
<select id="queryAllUser" resultMap="userMap">
select * from user
</select>
- 测试
@Test
public void queryAllUser() throws IOException {
// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 开启分页,pageNum表示当前页码,pageSize表示每页数据条数
PageHelper.startPage(2,2);
// 获取数据
List<User> userList = mapper.queryAllUser();
// 把数据封装在pageInfo对象中,后期可以通过这种方式返回给前端,进行分页展示
PageInfo<User> userPageInfo = new PageInfo<>(userList);
System.out.println(userPageInfo);
}
十三、log4j
在使用SSM进行开发时, 随着代码量的增大, 配置信息的繁杂, 排错也随之变得越来越难. 而日志就是我们debug的有力工具,常用的日志有很多, 我们选择使用log4j,下文介绍log4j在MyBatis下的简单应用
1、使用步骤
- 导入依赖
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 配置mybatis-config.xml
在mybatis-config.xml中配置,注意顺序问题
<settings>
<!-- 配置log4j日志 -->
<setting name="logImpl" value="LOG4J"/>
</settings>
- 创建 文件(直接在resource文件夹下创建)
log4j.rootLogger=DEBUG, Console
#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
十四、MyBatis缓存
缓存可以将数据保存在内存中,MyBatis 提供了一级缓存和二级缓存的支持。默认情况下,MyBatis 开启二级缓存,但是并不是意味着我们之前写的所有sql都是二级缓存,要使用二级缓存需要在xml文件中使用<cache/>
标签指出。
下面案例是演示配置了日志系统后,当你查询的条件一摸一样的时候只执行了一次sql。
1、一级缓存
- 一级缓存是基于 PerpetualCache(MyBatis自带)的 HashMap 本地缓存,作用范围为 session 域内。当 session flush(刷新)或者 close(关闭)之后,该 session 中所有的 cache(缓存)就会被清空。
- 在参数和 SQL 完全一样的情况下,我们使用同一个 SqlSession 对象调用同一个 mapper 的方法,往往只执行一次 SQL。因为使用 SqlSession 第一次查询后,MyBatis 会将其放在缓存中,再次查询时,如果没有刷新,并且缓存没有超时的情况下,SqlSession 会取出当前缓存的数据,而不会再次发送 SQL 到数据库。
- 由于 SqlSession 是相互隔离的,所以如果你使用不同的 SqlSession 对象,即使调用相同的 Mapper、参数和方法,MyBatis 还是会再次发送 SQL 到数据库执行,返回结果。
1.1 案例
- 保证日志开启,方便查看输出数据
- 接口
List<User> getUserList();
- Mapper
<select id="queryAllUser" resultMap="userMap">
select * from user
</select>
- 测试
@Test
public void getUserList() throws IOException {
// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// SqlSession执行文件中定义的SQL,并返回映射结果
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
System.out.println(userList);
List<User> userList1 = mapper.getUserList();
System.out.println(userList1);
System.out.println(userList1==userList);// true
}
日志打印信息
[com.hqyj.cl.mapper.UserMapper.getUserList]-==> Preparing: select * from `user`
[com.hqyj.cl.mapper.UserMapper.getUserList]-==> Parameters:
[com.hqyj.cl.mapper.UserMapper.getUserList]-<== Total: 3
[User(id=1, name=admin, password=111, age=12, deptId=0, state=1), User(id=2, name=王五, password=789, age=13, deptId=0, state=1), User(id=3, name=小王, password=123, age=14, deptId=0, state=1)]
===========================================================================================================
[User(id=1, name=admin, password=111, age=12, deptId=0, state=1), User(id=2, name=王五, password=789, age=13, deptId=0, state=1), User(id=3, name=小王, password=123, age=14, deptId=0, state=1)]
1.2 缓存失效
- 查询不同的东西,比如同一个SQL语句,但是传递的参数不同。
- 查询不同的Mapper.xml。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存,也就是说,在两条相同语句中间插入一条其他语句(比如inset),缓存会刷新。
- 手动清理缓存。
sqlSession.clearCache()
- 把第一个创建的sqlSession通过
sqlSession.close()
方法关闭后,再创建一个sqlSession,也无法实现缓存。
1.3 总结
一级缓存基本上没啥用,因为用户执行的操作出现一模一样的情况很少;通过debug可以发现,其实就是把查询出来的值存入Map集合。
2、二级缓存
二级缓存就是与namespace级别的缓存,一个名称空间对应一个缓存。
2.1 工作机制
一个会话查询一条数据,这个数据会存放在当前会话的一级缓存中,当会话结束时,一级缓存就会消失,但是我们可以把当前会话数据保存在二级缓存中,新的会话查询信息就可以从二级缓存中获取内容,当然,不同的mapper查询的数据都会放在自己对应的缓存中。
2.2 格式
要启用全局的二级缓存,只需要在你的 SQL 映射文件中(XXXMapper.xml)添加一行<cache/>
,比如
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
上述例子创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
配置详解
- flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
- size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
- readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
2.3 可用的清除策略
LRU
– 最近最少使用:移除最长时间不被使用的对象默认方式。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
2.4 注意事项
- 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新;
- 不能使用封装获取sqlSession对象的类;
2.5 案例
-
开启全局缓存(mybatis-config.xml)配置二级缓存后可以尝试不同的sqlsession执行方法,控制台只打印一条sql语句,证明
是从缓存中取出数据
<!--开启全局缓存,默认也是开启的,但是我们一般显示定义-->
<setting name="cacheEnabled" value="true"/>
**注意事项:**在CRUD标签中,可以使用useCache属性指定是否启用二级缓存,默认true。
- 接口
List<User> getUserList();
- mapper
<!--开启二级缓存-->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
- 测试
@Test
public void getUserList() throws IOException {
// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("config/mybatis-config.xml");
// 根据配置文件构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// SqlSession执行文件中定义的SQL,并返回映射结果
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
System.out.println(userList);
// 关闭第一个sqlSession连接,它会把缓存放入二级缓存中
sqlSession.close();
System.out.println("=======================================================");
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
List<User> userList1 = mapper1.getUserList();
System.out.println(userList1);
System.out.println(userList1==userList);// true
// 关闭第二个sqlSession连接
sqlSession1.close();
}
2.6 错误及解决
- 错误
如果我们在mapper文件中,直接使用<cache/>
方式开启二级缓存,会报错org.apache.ibatis.cache.CacheException: Error serializing object. Cause: java.io.NotSerializableException: com.hqyj.cl.pojo.User
- 解决
因为要使用二级缓存需要把对应的类序列化,所以解决办法如下
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private int id;
private String name;
private String password;
private int age;
private int deptId;
private int state;
}
十五、MyBatis逆向工程
Mybatis 提供了一个逆向工程工具,该工具可以根据数据表自动生成针对单表的 pojo 类、mapper 映射文件和 mapper 接口。
1、使用步骤
- 创建数据库
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`gender` varchar(255) DEFAULT NULL,
`phone` varchar(255) DEFAULT NULL,
`role` varchar(255) NOT NULL DEFAULT '用户',
`state` int(255) unsigned NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8
- 创建项目
新建项目,并且新建资源文件夹 config,在 config 文件夹下创建 generatorConfig.xml 文件,用于配置及指定数据库及表等。
generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- Mysql数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/servletdemo" userId="root"
password="112112" />
<!-- 默认为false,把JDBC DECIMAL和NUMERIC类型解析为Integer;为true时 把JDBC DECIMAL和NUMERIC类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetProject:生成POJO类的位置 -->
<javaModelGenerator
targetPackage="com.hqyj.cl.pojo" targetProject="D:\workspace\mybatis\mybatis_03\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.hqyj.cl.mapper"
targetProject="D:\workspace\mybatis\mybatis_03\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetProject:mapper接口生成的的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.hqyj.cl.mapper" targetProject="D:\workspace\mybatis\mybatis_03\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定数据表 -->
<table tableName="user"></table>
</context>
</generatorConfiguration>
注意事项:targetProject写成绝对路径,否则可能出现路径不对,加载不出文件的情况
- 导入依赖
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.4.0</version>
</dependency>
- GeneratorSqlmap 类
package net.biancheng;
import java.io.File;
import java.util.*;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
public class GeneratorSqlMap {
public void generator() throws Exception {
List<String> warnings = new ArrayList<>();
boolean overwrite = true;
// 指定配置文件
File configFile = new File("D:\\workspace\\mybatis\\mybatis_03\\src\\main\\resources\\config\\generator-config.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
// 执行main方法以生成代码
public static void main(String[] args) {
try {
GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
generatorSqlmap.generator();
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意事项:FILE文件路径写成绝对路径,相对路径可能存在路径不对的情况
2、说明
在 pojo 包中,有一部分是名字为 XxxExample 的类。类中包含以下 3 个成员变量
protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;;
以上变量说明如下:
- distinct 字段用于指定 DISTINCT 查询。
- orderByClause 字段用于指定 ORDER BY 条件,这个条件没有构造方法,直接通过传递字符串值指定。
- oredCriteria 字段用于自定义查询条件。