目录
10.6.2 创建EHCache的配置文件ehcache.xml
一、MyBatis简介
1.1 MyBatis历史
MyBatis最初是Apache的一个开源项目iBatis,2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下, iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github。
iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。 iBatis提供的持久层框架包括SQL Maps(SQL映射:数据库中的记录<——>Java的实体类对象)和Data Access Objects(DAO)。
1.2 什么是MyBatis?MyBatis有啥特点?
什么是MyBatis?
- MyBatis是一个轻量级的半自动ORM持久层框架,MyBatis封装了JDBC,主要作用是连接数据库、操作数据库中的数据
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及结果集解析操作
- MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的Pojo映射成数据库中的记录
Mybatis有啥特点?
- MyBatis具有较高的SQL灵活性,支持定制化SQL
- 存储过程
- 高级映射(一对一,一对多)
- 动态SQL
- 延迟加载
- 缓存
Mybatis的最大特点:轻量级。相对于Hibernate省略了大量不常用的功能,整体轻巧又高效。
1.3 什么是ORM?
ORM(Object Relation Mapping-对象关系映射)
对象关系映射,指的是实体对象和持久化数据的映射模式,为了解决面向对像与关系型数据库存在的互不匹配的现象。
具体的映射规则是:一张表对应一个类,表中的各个字段对应类中的属性,表中的一条数据对应类的一个对象
1.4 MyBatis下载
1.5 和其它持久层技术对比
- JDBC
-
- SQL 夹杂在Java代码中耦合度高,导致硬编码内伤
- 维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见
- 代码冗长,开发效率低
- Hibernate 和 JPA
-
- 操作简便,开发效率高
- 程序中的长难复杂 SQL 需要绕过框架
- 内部自动生成的 SQL,不容易做特殊优化
- 基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。
- 反射操作太多,导致数据库性能下降
- MyBatis
-
- 轻量级,性能出色
- SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
- 开发效率稍逊于 HIbernate,但是完全能够接受
- 底层采用动态代理技术创建持久层接口的代理对象,所以使用者不需要编写持久层的实现类了,也就解决了持久层代码的冗余和耦合问题
- 开发效率:Hibernate>Mybatis>JDBC
- 运行效率:JDBC>Mybatis>Hibernate
二、搭建MyBatis
2.0 开发环境
MySQL不同版本的注意事项:
1、驱动类driver-class-name
- MySQL 5版本使用jdbc5驱动,驱动类使用:com.mysql.jdbc.Driver
- MySQL 8版本使用jdbc8驱动,驱动类使用:com.mysql.cj.jdbc.Driver
2、连接地址url
- MySQL 5版本的url:
- jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8
- MySQL 8版本的url:
- jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
否则运行测试用例报告如下错误:
java.sql.SQLException: The server time zone value is unrecognized or represents more
2.1 数据库创建表
CREATE TABLE `user`(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
user_name VARCHAR(15),
pass_word VARCHAR(18),
age INT,
gender CHAR,
email VARCHAR(20)
)
2.2 创建Maven工程
pom.xml文件引入依赖包
<dependencies>
<!--MyBatis核心-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!--Junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!--MySQL驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
</dependencies>
2.3 加入Log4j日志功能
日志的级别
FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)
从左到右打印的内容越来越详细
1、加入依赖
<!--Log4j日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、加入Log4j的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" >
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L)\n"/>
</layout>
</appender>
<logger name="java.sql">
<level value="debug"/>
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
2.4 创建MyBatis配置文件
配置文件习惯上命名为mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。将来整合Spring 之后,这个配置文件可以省略,所以不必死记。
核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息
核心配置文件存放的位置是src/main/resources目录下
<?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.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssm"/>
<property name="username" value="root"/>
<property name="password" value="xxxx"/>
</dataSource>
</environment>
</environments>
<!--引入MyBatis的映射文件-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
2.5 创建Mapper接口
MyBatis中的mapper接口相当于以前的dao
但是区别在于mapper仅仅是接口,我们不需要提供实现类。
public interface User {
/*
* 添加用户信息
*/
int addUser();
}
2.6 创建MyBatis的映射文件
ORM(Object Relationship Mapping)对象关系映射。
- 对象:Java的实体类对象
- 关系:关系型数据库
- 映射:二者之间的对应关系
Java概念 | 数据概念 |
类 | 表 |
属性 | 字段 |
对象 | 行/记录 |
映射文件命名规则
- 表所对应的实体类的类名+Mapper.xml
- 如:表t_user,映射的实体类为User,所对应的映射文件为UserMapper.xml
- 因此一个映射文件对应一个实体类,对应一张表的操作
- MyBatis映射文件用于编写SQL,访问以及操作表中的数据
- MyBatis映射文件存放的位置是src/main/resources/mappers目录下
两个一致
MyBatis中可以面向接口操作数据,要保证两个一致:
- Mapper接口的全类名和映射文件的命名空间(namespace)保持一致
- Mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致
<?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.xiaoming.mybatis.mapper.UserMapper">
<insert id="addUser">
insert into user value (null,"刘大发",123321,22,'男',"8707085@qq.com")
</insert>
</mapper>
2.7 创建Pojo类
@Data
public class User {
private Integer id;
private String userName;
private String passWord;
private Integer age;
private String gender;
private String email;
//无参构造
//有参构造
//公共get\set方法
//toString
}
2.8 Junit测试功能
SqlSession:代表Java程序和数据库之间的会话(HttpSession是Java程序和浏览器之间的会话)
SqlSessionFactory:是“生产”SqlSession的“工厂”
工厂模式:
如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建这个对象的相关代码封装到一个“工厂类”中,以后都使用这个工厂类来“生产”我们需要的对象。
注意:
使用SqlSession进行insert操作时,
必须手动提交commit一下或
sqlSessionFactory.openSession(true)
才会插入数据,否则会出现执行成功数据没有的情况
通过MyBatis配置文件与MySQL数据库进行连接,再通过Mapper接口的方法对应映射文件的id执行SQL语句
MyBatis测试(使用SqlSession的代理实现类对象)
/*
* 通过MyBatis配置文件与MySQL数据库进行连接
* 再通过Mapper接口的方法对应映射文件的id执行SQL语句
* */
@Test
public void test01() throws IOException {
//读取MyBatis的核心配置文件
//通过资源Resources获取配置文件的输入流
InputStream inputStream = Resources.getResourceAsStream("mybatis_config.xml");
//创建SqlSessionFactoryBuilder(构建Sql会话工厂)的对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//sqlSessionFactoryBuilder(构建Sql会话工厂)的对象,
//通过MyBatis配置文件的字节输入流得到sqlSessionFactory(sql会话工厂对象)
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
//通过SqlSessionFactory(sql会话工厂对象)拿到一个SqlSession(sql会话)对象,执行映射文件的Sql语句
//注意:此时通过SqlSession对象所操作的sql都必须手动提交或回滚事务
SqlSession sqlSession = sqlSessionFactory.openSession();
//注意:此时通过SqlSession对象所操作的sql都会自动提交
//SqlSession sqlSession = sqlSessionFactory.openSession(true);
//通过代理模式创建UserMapper接口的代理实现类对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配映射文件,
//通过调用的方法名匹配映射文件中的SQL标签,并执行标签中的SQL语句
int result = mapper.addUser();
System.out.println("结果:"+result);
//手动提交事务
sqlSession.commit();
//关闭资源
sqlSession.close();
}
MyBatsi是如何执行SQL语句的?
SqlSession是MyBatis用于操作数据库的一个接口,通过SqlSession创建Mapper接口的代理类对象,执行接口中的方法对应映射文件id的SQL语句
- SqlSession创建代理类对象执行方法对应的映射文件的SQL
- SqlSession直接调用自己的方法找到映射文件执行SQL
SqlSession创建的接口代理类对象做了什么事情?
代理类对象首先肯定是要重写接口中的抽象方法,如果是插入的话,重写的内容其实就是:
sqlSession.insert("com.mapper.UserMapper.addUser【映射文件SQL语句的唯一标识】")
来找到映射文件中的SQL语句并执行。
SqlSession的方法
SqlSession在这个过程中最主要的任务就是找到映射文件中的SQL语句并执行,其实没有Mapper接口也可以
最原始的办法可以直接通过sqlSession.insert(【唯一标识namespace.sqlId】)
的方式来执行映射文件中的SQL语句。
这样就可以不用创建接口了,namespace
也不用写这么长了。换句话说namespace.sqlId
就可以随意写,也不用遵循namespace要和接口的全类名一致,id要和方法名一致了。
只要namespace.sqlId
和sqlSession.insert(namespace.sqlId)
对应上就行。
但是这就出现了硬编码的问题。如果Sqlid改了,对应sqlSession.insert(namespace.sqlId)
也要改,耦合度太高,不便于维护。
所以就利用了接口和方法动态的获取全类名加方法名对应【唯一标识namespace.sqlId】办法
在重写的过程中【映射文件SQL语句的唯一标识】是动态调用指定方法来获取Mapper接口的全类名,加上调用的方法名来组成的【唯一标识namespace.sqlId】
MyBatis测试(使用SqlSession.insert执行新增SQL语句)
@Test
public void test02() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis_config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory build = builder.build(inputStream);
SqlSession sqlSession = build.openSession(true);
//提供sql语句的唯一标识找到sql并执行,唯一标识是namespace.sqlId
int insert = sqlSession.insert("com.xiaoming.mybatis.mapper.UserMapper.addUser");
System.out.println("结果:"+insert);
}
总结:
一张表对应一个实体类对应当前Mapper接口对应映射文件
表——>实体类——>Mapper接口——>映射文件
Mapper接口中的方法对应映射文件的SQL语句
Mapper接口方法——>映射文件SQL
三、核心配置文件详解
<?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>
<!--MyBatis核心配置文件中,标签的顺序:
properties > settings > typeAliases > typeHandlers > objectFactory > objectWrapperFactory >
reflectorFactory > plugins > environments > databaseIdProvider > mappers> -->
<!--引入properties文件-->
<properties resource="jdbc.properties"/>
<!-- 使用settings对Mybatis全局进行设置 -->
<settings>
<!-- 将xxx_xxx这样的列名自动映射成xxXxx这样驼峰式命名的属性名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启延迟加载,配置两个配置项-->
<!-- 1、将lazyLoadingEnabled设置为true,开启懒加载功能 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 2、将aggressiveLazyLoading设置为false,关闭“积极的懒加载” -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<!--设置别名-->
<typeAliases>
<!-- typeAlias:设置某个类型的别名,给某个具体的类型设置一个别名,在MyBatis的范围中就可以使用别名表示一个具体的类型
属性 type:设置需要设置别名的类型
alias:设置某个类型的别名,若不设置该属性,那么该类型拥有默认的别名,即类名,且不区分大小写 -->
<typeAlias type="com.xiaoming.mybatis.pojo.User" alias="User"/>
<!--以包为单位,将包下所有的类型设置默认的别名,即类名,且不区分大小写-->
<package name="com.xiaoming.mybatis.pojo"/>
</typeAliases>
<!--设置连接数据库的环境
environments:配置多个连接数据库的环境
属性:default:设置默认使用的环境的id -->
<environments default="test">
<!-- environment:配置某个具体的环境
属性:id:表示连接数据库的环境的唯一标识,不能重复 -->
<environment id="development">
<!-- <事务管理器>
transactionManager:设置事务管理方式
属性:type:设置事务管理方式类型 type="JDBC\MANAGED"
JDBC:表示当前环境中,执行SQL时,使用的是JDBC中原生的事务管理方式,事务的提交或回滚需要手动处理
MANAGED:被管理,例如Spring -->
<transactionManager type="JDBC"/>
<!-- <数据源>
dataSource:配置数据源
属性:type:设置数据源的类型 type="POOLED\UNPOOLED\JNDI"
POOLED:表示使用数据库连接池缓存数据库连接
UNPOOLED:表示不使用数据库连接池
JNDI:表示使用上下文中的数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--引入MyBatis的映射文件-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
<!-- 以包为单位引入映射文件,但必须满足两个条件:
1、mapper接口所在的包(全类名)要和映射文件所在的包(全类名)一致
2、mapper接口要和映射文件的名字一致 -->
<package name="com.xiaoming.mybatis.mapper"/>
</mappers>
</configuration>
四、CRUD
4.1 新增用户信息
1. 在UserMapper接口中添加方法
Mapper接口
public interface UserMapper {
/*
* 添加用户信息
*/
int addUser();
}
2. 在UserMapper.xml中添加SQL语句
<insert id="addUser">
insert into user value (null,"刘大发",123321,22,'男',"6936111@qq.com")
</insert>
3. 测试
MyBatis测试
@Test
public void insert() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis_config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory build = builder.build(inputStream);
SqlSession sqlSession = build.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.addUser();
sqlSession.commit();
sqlSession.close();
}
4.2 删除用户信息
步骤和以上相同,下面只写映射文件的SQL语句
<delete id="deleteUser">
DELETE FROM USER WHERE id = 1
</delete>
4.3 修改用户信息
<update id="updateUser">
UPDATE USER SET user_name = "李二狗",pass_word = "root" WHERE id = 2
</update>
4.3 查询用户信息
★自动映射
注意:
1、查询的标签select必须设置属性resultType或resultMap,用于设置实体类和数据库表的映射关系
resultType:自动映射,用于属性名和表中字段名一致的情况
Mybatis在做结果集与POJO类的映射关系的时候,会自动将结果集的字段名与POJO的属性名(其实是和getXXX方法)进行对应映射,结果集的数据会自动映射给POJO对象中同名的属性;所以当我们遇到表的字段名和POJO属性名不一致的情况,我们可以在编写查询语句的时候,给结果集的字段取别名,让别名与POJO的属性名一致以保证结果集的正确映射
resultMap:自定义映射,用于一对多或多对一或字段名和属性名不一致的情况
2、这里的返回结果类型的实体类`User`已经在配置文件中设置了别名了才可以直接用类名,如果没有在配置文件中设置别名必须写全类名
<select id="selectUser" resultType="User">
SELECT id,user_name AS `userName`,pass_word AS `passWord`,age,gender,email FROM USER WHERE id = 3
</select>
五、MyBatis获取参数的两种方式
在BS架构中传统的方式是需要通过浏览器的前端页面、视图,获取用户的数据,传入服务器中,服务器再把数据给到Service业务层处理业务逻辑,再传入Dao中在Dao的实现类中把数据拼接到SQL语句中然后执行SQL语句,把数据保存在数据库中。
MyBatis如何获取参数值的?
- MyBatis用的是面向接口编程,原来写的是Dao,现在写的是Mapper接口,没有实现类。
- 底层是采用动态代理技术创建Mapper接口的代理对象
- 通过调用Mapper接口中的方法,其中这个方法是有参数的。因为没有实现类,这个Mapper接口中的方法直接对应的就是映射文件中的一个SQL语句,在映射文件中通过指定的方式(#{}、${})来获取对应方法中的参数,然后把获取到的参数拼接在SQL语句中
MyBatis会自动将参数放在一个Map集合中
MyBatis获取参数的两种方式:${}、#{}
- ${}的本质就是字符串拼接的方式拼接SQL,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号
- 在SQL语句中,如果数据库表的表名不确定,需要外部动态传入、此时不能使用#{},因为数据库不允许表名位置使用问号占位符,此时只能使用${}
- 批量删除中必须用${}
- 模糊查询中也能用到(#{}也可以)
- #{}的本质就是占位符赋值的方式拼接SQL,#{}转换为“?”占位符,若此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号
- #{}是通过预编译对象的set方法来给占位符进行赋值。
- 我们一般都用#{}的方式因为有一下优点:
- 安全,可以防止SQL注入的问题
- 简单方便,不需要手动添加单引号
5.1 单个字面量类型参数传参
字面量包括:字符串类型、基本数据类型、包装类型
若mapper接口中的方法参数为单个的字面量类型
此时可以使用${}和#{}以任意的名称获取参数的值,注意${}需要手动加单引号
5.2 多个字面量类型参数
若mapper接口中的方法参数为多个时,MyBatis会自动将这些参数放在一个Map集合中。
一共提供两种方式存储:
1、以arg0,arg1...为键,以参数为值
2、以 param1,param2...为键,以参数为值
因此只需要通过${}和#{}访问Map集合的键就可以获取相对应的值,注意${}需要手动加单引号
混合使用也可以,如果存两个参数会提供四个键。但是不推荐混合使用
Mapper接口
public interface UserMapper {
/**
* 多个参数查询数据
*/
User getUserByParam(String userName,String passWord);
}
Sql映射文件
<!--多个参数查询-->
<select id="getUserByParam" resultType="user">
select * from users where userName = #{param1} and passWord = #{param2}
</select>
测试
自定义工具类
public class SqlSessionUtil {
public static SqlSession getSqlSession(){
SqlSession sqlSession = null;
try {
//读取MyBatis配置文件
InputStream in = Resources.getResourceAsStream("mybatis_config.xml");
//创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//获取SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = builder.build(in);
//获取sqlqSession对象
sqlSession = sqlSessionFactory.openSession(true);
} catch (IOException e) {
System.out.println(e.getMessage());
}
return sqlSession;
}
}
public class ParametersDemo {
@Test
public void morePara(){
//SqlSessionUtil是自己封装的工具类
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserByParam("刘三八", "123321");
System.out.println(user);
}
}
5.3 使用@Param标识参数(最常用)
若mapper接口中的方法需要的参数为多个时,可以通过@Param注解标识Mapper接口中的方法参数;
此时,会将这些参数放在map集合中,以两种方式进行存储:
1、以@Param注解的value属性值为键,以参数为值
2、以param1,param2...为键,以参数为值
只需要通过${}和#{}访问Map集合的键就可以获取相对应的值,注意${}需要手动加单引号
Mapper接口
public interface UserMapper {
/**
* @Param标识传参
*/
User selectByParam(@Param("userName") String userName,
@Param("passWord") String passWord);
}
Sql映射文件
<!--@Param查询-->
<select id="selectByParam" resultType="user">
select * from users where userName = #{userName} and passWord = #{passWord}
</select>
测试
public class ParametersDemo {
@Test
public void morePara(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User userByMap = userMapper.selectByParam("刘大发", "123321");
System.out.println(userByMap);
}
}
5.4 map集合类型的参数
若mapper接口中的方法需要的参数为多个时,可以把参数放在自己创建的Map集合中。
只需要通过${}和#{}访问Map集合的键就可以获取相对应的值,注意${}需要手动加单引号
使用场景:有很多零散的参数需要传递,但是没有对应的实体类类型可以使用。使用@Param注解一个一个传入又太麻烦了。所以都封装到Map中。
Mapper接口
public interface UserMapper {
/**
* 多个参数查询数据(用自己创建的Map存)
* @param map
* @return
*/
User getUSerByMap(Map<String,Object> map);
}
Sql映射文件
<!--多个参数查询-->
<select id="getUSerByMap" resultType="user">
select * from users where userName = #{userName} and passWord = #{passWord}
</select>
测试
public class ParametersDemo {
@Test
public void morePara(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map map = new HashMap();
map.put("userName","刘大发");
map.put("passWord","123321");
User userByMap = userMapper.getUSerByMap(map);
System.out.println(userByMap);
}
}
5.5 实体类类型的参数
若mapper接口中的方法参数为实体类对象时
此时可以使用${}和#{},通过访问实体类对象中的属性名获取属性值,注意${}需要手动加单引号
Mybatis会根据#{}中传入的数据,加工成getXxx()方法,通过反射在实体类对象中调用这个方法,从而获取到对应的数据。填充到#{}这个位置。
Mapper接口
public interface UserMapper {
/**
* 实体类型传参,添加用户信息
* @param user
* @return
*/
void insertUser(User user);
}
Sql映射文件
<!--实体类型传参-->
<insert id="insertUser">
insert into user values(null,#{userName},#{passWord},#{age},#{gender},#{email})
</insert>
测试
public class ParametersDemo {
@Test
public void morePara(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User(1,"周建御","123321",21,"男","dsfs@qq.com");
userMapper.insertUser(user);
}
}
六、MyBatis查询
6.1 查询一个实体类对象
Mapper接口
public interface SelectMapper {
/**
* 根据id查询用户信息
* @param id
* @return
*/
User selectById(Integer id);
}
Sql映射文件
<select id="selectById" resultType="user">
select * from users where id = #{id}
</select>
测试
public class SelectTest {
/**
* 根据id查询用户信息
*/
@Test
public void selectOne(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
SelectMapper selectMapper = sqlSession.getMapper(SelectMapper.class);
User user = selectMapper.selectById(1);
System.out.println(user);
}
}
6.2 查询一个List集合
当查询的数据为多条时,不能使用实体类作为返回值,否则会抛出异常TooManyResultsException;
但是若查询的数据只有一条,可以使用实体类或集合作为返回值
Mapper接口
public interface SelectMapper {
/**
* 查询所有用户信息
* @return
*/
List<User> selectAll();
}
Sql映射文件
<select id="selectAll" resultType="user">
select * from users
</select>
测试
public class SelectTest {
/**
* 根据id查询用户信息
*/
@Test
public void selectOne(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
SelectMapper selectMapper = sqlSession.getMapper(SelectMapper.class);
List<User> users = selectMapper.selectAll();
for (User user : users) {
System.out.println(user);
}
}
}
6.3 查询单个数据
MyBatis在TypeAliasesRegistry类中内置注册了很多别名,对于Java中常用的类型都设置了类型别名
例如: java.lang.Integer-->int、integer
例如: int-->_int、_integer
例如: Map-->map,List-->list
Mapper接口
public interface SelectMapper {
/**
* 查询用户数量
* @return
*/
Integer selectNum();
}
Sql映射文件
<select id="selectNum" resultType="Integer">
SELECT COUNT(*) FROM users
</select>
6.4 查询一条数据为Map集合
Mapper接口
public interface SelectMapper {
/**
* 根据id查询用户信息,返回结果集为Map
* @return
*/
Map selectMap(int id);
}
Sql映射文件
<select id="selectMap" resultType="Map">
select * from users where id = #{id}
</select>
结果:
{passWord=123321, gender=男, id=1, userName=刘大发, age=22, email=870708548@qq.com}
6.5 查询多条数据为Map集合
方式一:List里面嵌套Map
将表中的数据以map集合的方式查询,一条数据对应一个map;
若有多条数据,就会产生多个map集合,此时可以将这些map放在一个list集合中获取
Mapper接口
public interface SelectMapper {
/**
* 查询所有信息,返回结果为Map集合
* @return
*/
List<Map> selectListMap();
}
Sql映射文件
<select id="selectListMap" resultType="map">
select * from users
</select>
结果:
{passWord=123321, gender=男, id=1, userName=刘大发, age=22, email=8707085@qq.com}
{passWord=123321, gender=男, id=2, userName=李二狗, age=22, email=870708548@qq.com}
方式二:使用注解
将表中的数据以map集合的方式查询,一条数据对应一个map;
若有多条数据,就会产生多个map集合,并且最终要以一个map的方式返回数据
可以将每条数据转换的map集合放在一个大的map中,但是必须通过@MapKey注解
将表中的某个字段的不重复值作为大的map的建,每条数据对应的map集合作为大的map的值
Mapper接口
public interface SelectMapper {
/**
* 查询所有信息,返回结果为Map集合
* @return
*/
@MapKey("id")
Map selectMaps();
}
Sql映射文件
<select id="selectMaps" resultType="map">
select * from users
</select>
结果:
{1={passWord=123321, gender=男, id=1, userName=刘大发, age=22, email=8707085@qq.com},
2={passWord=123321, gender=男, id=2, userName=李二狗, age=22, email=870708548@qq.com}}
七、特殊SQL的执行
7.1 模糊查询
在MySQL中的SQL语句中,“_”表示任意单个字符,“%”表示任意个数和任意字符、字符串拼接函数concat()
Mapper接口
public interface SpecialSQLMapper {
/**
* 模糊查询,查询用户邮箱为QQ邮箱
* @param email
* @return
*/
List<User> selectUser(String email);
}
Sql映射文件
三种方式:
<select id="selectUser" resultType="User">
select * from users where email like '%${email}%';
select * from users where email like concat('%',#{email},'%');
select * from users where email like "%"#{email}"%";
</select>
7.2 批量删除
Mapper接口
public interface SpecialSQLMapper {
/**
* 批量删除
* @param ids
*/
void deleteUser(String ids);
}
Sql映射文件
<delete id="deleteUser">
--使用#{1,2}MySQL5.5版本只删第一个,不会批量删除--
delete from user where id in(${ids});
</delete>
7.3 动态设置表名
Mapper接口
public interface SpecialSQLMapper {
/**
* 动态设置表名
* @param tabeName
* @return
*/
List<User> selectUserList(String tabeName);
}
Sql映射文件
<delete id="deleteUser">
<select id="selectUserList" resultType="user">
select * from ${tabeName};
</select>
</delete>
7.4 添加功能获取自增主键值
MyBatis封装了JDBC里面的获取自增主键值:
useGeneratedKeys:设置使用自增的主键
keyProperty:将添加数据的自增主键值放在实体类对象user的某个属性中去
因为增删改没有统一的返回值而是受影响的行数,因此只能把主键值放入实体类属性中
Mapper接口
public interface SpecialSQLMapper {
/**
* 新增用户信息获取自增主键值
* @param user
*/
void insertUser(User user);
}
Sql映射文件
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into users values(null,#{userName},#{passWord},#{age},#{gender},#{email})
</insert>
八、自定义映射resultMap
8.0 数据库字段名和实体类中的属性的映射关系
因为我们表中字段的命名规则采用"_",而POJO的属性名命名规则采用驼峰命名法,所以导致字段名和实体类中的属性名不一致,出现数据映射不上的问题
字段名和属性的映射,其实就是通过反射,根据字段名作为属性名获取属性给属性赋值的
此时也可通过以下三种方式处理:
1、可以通过为字段起别名的方式,保证和实体类中的属性名保持一致
2、在MyBatis的核心配置文件中设置一个全局配置信息,自动识别驼峰命名规则的配置,自动将"_"类型的字段名转换为驼峰
例如:字段名user_name,此时字段名就会转换为userName
在Mybatis全局配置文件加入如下配置:
<!-- 使用settings对Mybatis全局进行设置 -->
<settings>
<!-- 将xxx_xxx这样的列名自动映射为xxXxx这样驼峰式命名的属性名 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
3、使用resultMap手动映射
8.1 resultMap处理字段和属性的映射关系
若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射
resultMap:设置自定义映射
属性:
id:表示自定义映射的唯一标识
type:查询的数据要映射的实体类的类型
子标签:
id:设置主键的映射关系
result:设置普通字段的映射关系
association:设置多对一的映射关系 (处理实体类类型的属性)
column:设置映射关系中表中的字段名
property:设置映射关系中实体类中的属性名
javaType:设置要处理的属性的类型
collection:设置一对多的映射关系(处理集合类型的属性)
ofType:设置需要处理的属性集合中的类型
Mapper接口
public interface EmpMapper {
/**
* 根据id获取员工信息
* @param id
* @return
*/
public Emp getEmpById(int id);
}
Sql映射文件
<resultMap id="empMapper" type="Emp">
<id column="id" property="id"></id>
<result column="emp_name" property="empName"></result>
<result column="emp_age" property="empAge"></result>
<result column="emp_gender" property="empGender"></result>
<result column="dept_id" property="deptId"></result>
</resultMap>
<select id="getEmpById" resultMap="empMapper">
select * from emp where id = #{id};
</select>
8.2 多对一映射处理
以员工与部门表为例:
查询员工和员工对应的部门信息
员工表与部门表的关系是:一对一、多对一
部门表与员工表的关系是:一对多
创建员工和部门表
CREATE TABLE `dept`(
dept_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
dept_name VARCHAR(10)
)
CREATE TABLE `emp`(
emp_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
emp_name VARCHAR(20),
emp_age INT,
emp_gender CHAR,
dept_id INT,
CONSTRAINT fk_emp_dept FOREIGN KEY(dept_id) REFERENCES dept(id)
)
多对一查询有三种方式:
1、级联方式处理映射关系
2、使用association处理映射关系
3、分布查询
8.2.1 级联方式处理映射关系
在Pojo员工类中添加部门类
public class Emp {
private int empId;
private String empName;
private int empAge;
private String empGender;
private Dept dept;
}
Mapper接口
public interface EmpMapper {
/**
* @Date 2022/9/11 13:04
* @param empId
* @return com.xiaoming.pojo.Emp
* @Description 多对一查询、级联查询:查询员工信息以及对应员工部门信息
**/
Emp getEmpAndDeptById1(int empId);
}
Sql映射文件
<mapper namespace="com.xiaoming.mybatis.mapper.EmpMapper">
<!--
多对一查询、级联查询:查询员工信息以及对应员工部门信息
column:设置映射关系中表中的字段名
property:设置映射关系中实体类中的属性名
-->
<resultMap id="getEmpAndDeptByIdResultMap1" type="com.xiaoming.pojo.Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="emp_age" property="empAge"></result>
<result column="emp_gender" property="empGender"></result>
<result column="dept_id" property="dept.deptId"></result>
<result column="dept_name" property="dept.deptName"></result>
</resultMap>
<select id="getEmpAndDeptById1" resultMap="getEmpAndDeptByIdResultMap1">
select * from emp e left join dept d ON d.dept_id = e.dept_id where e.emp_id = #{empId};
</select>
</mapper>
8.2.2 association处理映射关系
Mapper接口
public interface EmpMapper {
/**
* @Date 2022/9/11 14:34
* @param empId
* @return com.xiaoming.pojo.Emp
* @Description 多对一查询、association查询:查询员工信息以及对应员工部门信息
**/
Emp getEmpAndDeptById2(int empId);
}
Sql映射文件
<mapper namespace="com.xiaoming.mybatis.mapper.EmpMapper">
<!--
多对一查询、association查询:查询员工信息以及对应员工部门信息
association:设置多对一的映射关系
property:设置映射关系中实体类中的属性名
javaType:设置要映射的实体类中自定义类属性的类型
-->
<resultMap id="getEmpAndDeptByIdResultMap2" type="com.xiaoming.pojo.Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="emp_age" property="empAge"></result>
<result column="emp_gender" property="empGender"></result>
<association property="dept" javaType="com.xiaoming.pojo.Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
</association>
</resultMap>
<select id="getEmpAndDeptById2" resultMap="getEmpAndDeptByIdResultMap2">
select * from emp e left join dept d on d.dept_id = e.dept_id where e.emp_id = #{empId};
</select>
</mapper>
8.2.3 分布查询
第一步:根据empId查询Emp表的员工信息
第二步:根据第一步员工信息中的deptId,查询员工对应Dept部门信息
映射过程:
通过association标签的property属性为dept(属性名),映射给Emp实体类中dept属性
再通过resultMap标签的type属性为Emp(实体类类型),把所有数据映射给Emp的所有属性
Mapper接口
public interface EmpMapper {
/**
* @Date 2022/9/11 14:48
* @param empId
* @return com.xiaoming.pojo.Emp
* @Description 多对一查询、分步查询:查询员工信息以及对应员工部门信息
* 分步查询第一步:查询员工信息
**/
Emp getEmpAndDeptByIdStepOne(int empId);
}
public interface DeptMapper {
/**
* @Date 2022/9/11 14:56
* @param deptId
* @return com.xiaoming.pojo.Dept
* @Description 多对一查询、分步查询:查询员工信息以及对应员工部门信息
* 分步查询第二步:根据第一步查询到的deptId查询该员工的部门信息
**/
Dept getEmpAndDeptByIdStepTwo(int deptId);
}
Sql映射文件
<mapper namespace="com.xiaoming.mybatis.mapper.DeptMapper">
<!-- 分步查询第二步:根据第一步查询到的deptId查询该员工的部门信息 -->
<select id="getEmpAndDeptByIdStepTwo" resultType="com.xiaoming.pojo.Dept">
select * from dept where dept_id = #{deptId}
</select>
</mapper>
<!--
分步查询第一步:查询员工信息
property:设置映射关系中实体类中的属性名
select:设置分步查询sql,查询某些属性的值的sql的唯一标识(namespace.sqlId)
column:将sql以及查询结果中的某个字段作为第二步分步查询的条件
-->
<resultMap id="getEmpAndDeptByIdResultMap3" type="com.xiaoming.pojo.Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="emp_age" property="empAge"></result>
<result column="emp_gender" property="empGender"></result>
<association property="dept" fetchType="lazy"
select="com.xiaoming.mapper.DeptMapper.getEmpAndDeptByIdStepTwo"
column="dept_id"></association>
</resultMap>
<select id="getEmpAndDeptByIdStepOne" resultMap="getEmpAndDeptByIdResultMap3">
select * from emp where emp_id = #{empId}
</select>
8.3 一对多映射处理
一对多查询有两种方式:
1、使用collection处理映射关系
2、分步查询
8.3.1 collection处理映射关系
注意:
主表和明细表的id字段名不要设置相同的名字,不然在明细表中会引起后面一条数据覆盖前面一条数据的现象
在Pojo部门信息中添加属性
public class Dept {
private int deptId;
private String deptName;
private List<Emp> emps;
}
Mapper接口
public interface DeptMapper {
/**
* @Date 2022/9/11 17:05
* @param deptId
* @return com.xiaoming.pojo.Dept
* @Description 一对多查询、collection查询,查询部门信息以及部门所在的员工信息
**/
Dept getDeptAndEmpById1(int deptId);
}
SQL映射文件
<mapper namespace="com.xiaoming.mybatis.mapper.DeptMapper">
<!--
一对多查询、collection查询,查询部门信息以及部门所在的员工信息
collection:设置一对多的映射关系(处理集合类型的属性)
ofType:设置需要处理的属性集合中的类型
-->
<resultMap id="getDeptAndEmpByIdResultMap" type="com.xiaoming.pojo.Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
<collection property="emps" ofType="com.xiaoming.pojo.Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="emp_age" property="empAge"></result>
<result column="emp_gender" property="empGender"></result>
</collection>
</resultMap>
<select id="getDeptAndEmpById1" resultMap="getDeptAndEmpByIdResultMap">
select * from dept d left join emp e on d.dept_id = e.dept_id where d.dept_id = #{deptId}
</select>
</mapper>
8.3.2 分步查询
第一步,根据deptId查询Dept表的部门信息
第二步,根据第一步查询到的部门信息中的deptId在Emp员工表查询部门对应的所有员工
Mapper接口
public interface DeptMapper {
/**
* @Date 2022/9/11 17:18
* @param deptId
* @return com.xiaoming.pojo.Dept
* @Description 一对多查询、分步查询,查询部门信息以及部门所在的员工信息
* 分步查询第一步,根据DeptId查询部门信息
**/
Dept getDeptAndEmpByIdSteptOne(int deptId);
}
public interface EmpMapper {
/**
* @Date 2022/9/11 17:23
* @param deptId
* @return java.util.List<com.xiaoming.pojo.Emp>
* @Description 一对多查询、分步查询,查询部门信息以及部门所在的员工信息
* 分步查询第二步,根据DeptId查询出部门信息后,再根据查询出的deptId在Emp中查询员工信息
**/
List<Emp> getDeptAndEmpByIdSteptTwo(int deptId);
}
SQL映射文件
EmpMapper.xml
<!-- 一对多查询,第二步根据DeptId查询出部门信息后,再根据查询出的deptId在Emp中查询员工信息 -->
<select id="getDeptAndEmpByIdSteptTwo" resultType="com.xiaoming.pojo.Emp">
select * from emp where dept_id = #{deptId}
</select>
DeptMapper.xml
<!-- 一对多查询、分步查询第一步,根据DeptId查询部门信息 -->
<resultMap id="getDeptAndEmpByIdSteptResultMap" type="com.xiaoming.pojo.Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
<collection property="emps"
select="com.xiaoming.mapper.EmpMapper.getDeptAndEmpByIdSteptTwo"
column="dept_id"></collection>
</resultMap>
<select id="getDeptAndEmpByIdSteptOne" resultMap="getDeptAndEmpByIdSteptResultMap">
select * from dept where dept_id = #{deptId}
</select>
8.4 延迟加载
延迟加载的概念:对于实体类关联的属性到需要使用时才查询。也叫懒加载。
查询到Emp的时候,不一定会使用Dept的数据。如果Dept数据始终没有使用,那么这部分数据占用的内存就浪费了。对此,我们希望不一定会被用到的数据,能够在需要使用的时候再去查询。
为了实现延迟加载,对Emp和Dept的查询必须分开,分成两步来做,才能够实现。为此,我们需要单独查询Emp和Dept。也就是需要在Mapper配置文件中,单独编写查询Emp和Dept数据的SQL语句,建立两个Mapper。
延迟加载的实现是建立在分步查询的基础上的
核心配置文件中设置全局配置信息,开启延迟加载功能需要配置两个配置项:
lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
aggressiveLazyLoading:默认不开启,每个属性会按需加载。如果开启,则任何方法的调用都会加载该对象的所有属性。
mybatis_config.xml
<settings>
<!--开启延迟加载,配置两个配置项-->
<!-- 1、将lazyLoadingEnabled设置为true,开启懒加载功能 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 2、将aggressiveLazyLoading设置为false,关闭“积极的懒加载” -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
8.4.1 局部延迟加载
associaation/collection标签中fetchType属性:
fetchType:在开启了延迟加载的环境中,通过该属性设置当前的分步查询是否使用延迟加载 ;
如果没有在全局配置中开启延迟加载,延迟加载只会对配置了局部延迟加载的地方进行延迟加载,未配置的地方无法进行延迟加载
fetchType=“eager(立即加载) / lazy(延迟加载)"
<!--多对一查询方法三:分步查询-->
<select id="getEmpAndDeptByEmpIdStepOne" resultMap="empAndDeptResultMapStepOne">
select * from emp where id = #{id}
</select>
<!--
property:设置映射关系中实体类中的属性名
select:设置分步查询sql,查询某些属性的值的sql的唯一标识(namespace.sqlId)
column:将sql以及查询结果中的某个字段设置为分步查询的条件
fetchType:在开启了延迟加载的环境中,通过该属性设置当前的分步查询是否使用延迟加载
fetchType=“eager(立即加载) / lazy(延迟加载)"
-->
<resultMap id="empAndDeptResultMapStepOne" type="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="emp_age" property="empAge"></result>
<result column="emp_gender" property="empGender"></result>
<association property="dept"
fetchType="eager"
select="com.xiaoming.mybatis.mapper.DeptMapper.getEmpAndDeptByEmpIdStepTwo"
column="dept_id"></association>
</resultMap>
九、动态SQL
Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题。
MyBatis的一个强大的特性之一通常是它的动态SQL能力。如果你有使用JDBC或其他相似框架的经验,你就明白条件地串联SQL字符串在一起是多么的痛苦,确保不能忘了空格或在列表的最后省略逗号。动态SQL可以彻底处理这种痛苦。
9.1 if标签
if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之
标签中的内容不会执行
POJO实体类
public class Emp {
private Integer empId;
private String empName;
private String empGender;
private Integer empAge;
}
Mapper接口
public interface DynamicSQLMapper {
/**
* @Date 2022/9/13 9:11
* @param emp
* @return java.util.List<com.xiaoming.pojo.Emp>
* @Description if动态SQL,根据不同的条件查询Emp(使用恒等式)
**/
List<Emp> getEmpByCondition(Emp emp);
}
SQL映射文件
<!--恒等式-->
<select id="getEmpByCondition" resultType="com.xiaoming.pojo.Emp">
select * from emp where 1=1
<if test="empName != null and empName != ''">
and emp_name = #{empName}
</if>
<if test="empAge != null and empAge != ''">
and emp_age = #{empAge}
</if>
<if test="empGender != null and empGender != ''">
and emp_gender = #{empGender}
</if>
</select>
9.2 where标签
where标签一般和if标签搭配使用:
- 如果where标签中的if条件都不满足,则不会添加where关键字
- 如果where标签中的if条件满足,则会自动添加where关键字,并把第一个条件多余的and关键字去掉
注意:where标签不能去掉条件最后多余的and
Mapper接口
public interface DynamicSQLMapper {
/**
* @Date 2022/9/13 10:49
* @param emp
* @return java.util.List<com.xiaoming.pojo.Emp>
* @Description if动态SQL,根据不同的条件查询Emp(加上where标签)
**/
List<Emp> getEmpByCondition2(Emp emp);
}
SQL映射文件
<!--加上where标签-->
<select id="getEmpByCondition2" resultType="com.xiaoming.pojo.Emp">
select * from emp
<where>
<if test="empName != null and empName != ''">
and emp_name = #{empName}
</if>
<if test="empAge != null and empAge != ''">
and emp_age = #{empAge}
</if>
<if test="empGender != null and empGender != ''">
and emp_gender = #{empGender}
</if>
</where>
</select>
9.3 trim标签
trim用于去掉或添加标签中的内容,常用属性:
- prefix:在trim标签中的内容的前面添加某些内容
- suffix:在trim标签中的内容的后面添加某些内容
- prefixOverrides:在trim标签中的内容的前面去掉某些内容
- suffixOverrides:在trim标签中的内容的后面去掉某些内容
Mapper接口
public interface DynamicSQLMapper {
/**
* @Date 2022/9/14 15:39
* @param emp
* @return java.util.List<com.xiaoming.pojo.Emp>
* @Description if动态SQL,根据不同的条件查询Emp(trim标签)
**/
List<Emp> getEmpByCondition3(Emp emp);
}
SQL映射文件
<!-- trim标签 -->
<select id="getEmpByCondition3" resultType="Emp">
select * from emp
<trim prefix="where" suffixOverrides="and" prefixOverrides="and">
<if test="empName != null and empName != ''">
and emp_name = #{empName}
</if>
<if test="empAge != null and empAge != ''">
and emp_age = #{empAge}
</if>
<if test="empGender != null and empGender != ''">
and emp_gender = #{empGender} and
</if>
</trim>
</select>
9.4 chooce、when、otherwise标签
when和otherwirse需要搭配chooce使用,多个when的条件必须是互斥的条件
chooce、when、when、otherwise就相当于if ... elseif ...else
,只要符合任意一个条件,下面的代码就不再执行
when至少设置一个,otherwise最多设置值一个
Mapper接口
public interface DynamicSQLMapper {
/**
* @Date 2022/9/14 16:00
* @param emp
* @return java.util.List<com.xiaoming.pojo.Emp>
* @Description if动态SQL,根据不同的条件查询Emp(chooce、when、otherwise标签)
**/
List<Emp> getEmpByCondition4(Emp emp);
}
SQL映射文件
<!-- chooce、when、otherwise标签 -->
<select id="getEmpByCondition4" resultType="com.xiaoming.pojo.Emp">
select * from emp
<where>
<choose>
<when test="empName != null and empName != ''">
emp_name = #{empName}
</when>
<when test="empAge != null and empAge != ''">
emp_age = #{empAge}
</when>
<when test="empGender != null and empGender != ''">
emp_gender = #{empGender}
</when>
</choose>
</where>
</select>
9.5 foreach标签
批量操作
使用foreach标签,它有如下一些属性:
- collection: 表示要遍历的对象或数组,如果要遍历的参数使用@Param注解取名了就使用该名字,如果没有取名但是要遍历的参数是List集合,就写list
- item: 表示遍历出来的每一个数据
- separator: 表示分隔符,每次循环的数据之间的分隔符
- open属性: 在遍历出来的第一个数据之前添加前缀
- close属性: 在遍历出来的最后一个数据之后添加后缀
9.5.1 批量添加
Mapper接口
public interface DynamicSQLMapper {
/**
* @Date 2022/9/14 16:35
* @param emps
* @Description 批量插入多条员工信息(foreach标签)
**/
void insertListByEmp(@Param("emps") List<Emp> emps);
}
SQL映射文件
<!-- foreach标签 -->
<insert id="insertListByEmp">
insert into emp values
<foreach collection="emps" item="emp" separator=",">
(null,#{emp.empName},#{emp.empAge},#{emp.empGender},null)
</foreach>
</insert>
测试类
public class DynamicSQLTest {
@Test
public void insertList(){
Emp emp1 = new Emp(null,"李琳","女",20);
Emp emp2 = new Emp(null,"林晓晓","女",23);
Emp emp3 = new Emp(null,"李虎","男",21);
List<Emp> empList = Arrays.asList(emp1, emp2, emp3);
getDynamicSQLMapper().insertListByEmp(empList);
}
}
9.5.2 批量删除
Mapper接口
public interface DynamicSQLMapper {
/**
* @Date 2022/9/14 17:09
* @param empIds
* @Description 批量删除员工信息
**/
void deleteArryEmp(@Param("empIds") Integer [] empIds);
}
SQL映射文件
<!-- foreach标签 -->
<delete id="deleteArryEmp">
delete from emp where emp_id in
<foreach collection="empIds" item="empId" open="(" close=")" separator=",">
#{empId}
</foreach>
delete from emp
<foreach collection="empIds" item="empId" separator="or" open="where">
emp_id = #{empId}
</foreach>
</delete>
5.6 sql标签
sql片段,可以记录一段公共sql片段,在使用的地方通过include标签进行引入
include标签引用SQL片段,它的refid属性就是需要引用的SQL片段的唯一标识
- 如果SQL片段和include在同一文件中,那么唯一标识就是SQL片段的id
- 如果SQL片段和include不再同一文件中,那么唯一标识就是SQL片段所在的那个映射配置文件的namespace的值+"."+SQL片段的id
<!-- 使用sql标签抽取重复出现的SQL片段 -->
<sql id="empColumns"> eid,ename,age,sex,did </sql>
<select id="select">
select <include refid="empColumns"></include> from t_emp
</select>
十、Mybatis缓存
10.1 MyBatis的一级缓存
使一级缓存失效的几种情况:
- 不同的SqlSession对应不同的一级缓存
- 同一个SqlSession但是查询条件不同
- 同一个SqlSession两次查询期间执行了任何一次增删改操作
- 同一个SqlSession两次查询期间手动清空了缓存
sqlSession.clearCache()
- 在不开启二级缓存的情况下,在同一个SqlSession两次查询期间SqlSession.commit()提交事务/SqlSession.close()销毁,会导致缓存失效
10.1.1 代码实现
Pojo实体类
public class Emp {
private Integer empId;
private String empName;
private String empGender;
private Integer empAge;
//get和set方法
}
Mapper接口
public interface CacheMapper {
/**
* @Date 2022/9/15 17:15
* @param empId
* @return com.xiaoming.pojo.Emp
* @Description 根据员工id获取员工信息
**/
Emp getEmpById(Integer empId);
}
Sql映射文件
<mapper namespace="com.xiaoming.mapper.CacheMapper">
<select id="getEmpById" resultType="com.xiaoming.pojo.Emp">
select * from emp where emp_id = #{empId}
</select>
</mapper>
Test测试
public class CacheMapperTest {
/**
* @Date 2022/9/16 9:26
* @Description 一级缓存
**/
@Test
public void getEmp() throws IOException {
//SqlSession工具类获取SqlSession1
SqlSession sqlSession1 = SqlSessionUtil.getSqlSession();
CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
//emp1和emp2都是同一个SqlSession,一级缓存生效,只执行一次SQL语句
Emp emp1 = mapper1.getEmpById(1);
System.out.println(emp1);
Emp emp2 = mapper1.getEmpById(1);
System.out.println(emp2);
//SqlSession工具类获取SqlSession2,这里是不同的SqlSession,一级缓存失效,会重新执行SQL语句
SqlSession sqlSession2 = SqlSessionUtil.getSqlSession();
CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
Emp emp3 = mapper2.getEmpById(1);
System.out.println(emp3);
}
}
效果图:
10.2 MyBatis的二级缓存
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取。二级缓存默认是不开启的,需要手动配置
二级缓存开启的条件:
- 在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置
- 在映射文件中设置标签<cache/>
- 查询的数据所转换的实体类类型必须实现序列化的接口
implements Serializable
- 二级缓存必须在SqlSession关闭或提交之后有效(使一级缓存中的数据存入二级缓存)
使二级缓存失效的情况:
- 两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
10.2.1 代码实现
Pojo实体类
public class Emp implements Serializable{
private Integer empId;
private String empName;
private String empGender;
private Integer empAge;
//get和set方法
}
Mapper接口
public interface CacheMapper {
/**
* @Date 2022/9/15 17:15
* @param empId
* @return com.xiaoming.pojo.Emp
* @Description 根据员工id获取员工信息
**/
Emp getEmpById(Integer empId);
}
Sql映射文件
<mapper namespace="com.xiaoming.mapper.CacheMapper">
<!-- 加入cache标签启用二级缓存功能 -->
<cache/>
<select id="getEmpById" resultType="com.xiaoming.pojo.Emp">
select * from emp where emp_id = #{empId}
</select>
</mapper>
Test测试
public class CacheMapperTest {
/**
* @Date 2022/9/16 10:22
* @Description 二级缓存
**/
@Test
public void getEmp2() throws IOException {
//二级缓存就不能使用工具类了,因为每调用一次会创建新的SqlSessionFactory,二级缓存就不生效了
//输入流加载MyBatis核心配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis_config.xml");
//创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取SqlSessionFactory对象
SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
/**
* //二级缓存,在同一个SqlSessionFactory创建的SqlSession查询结果会加入缓存
* //创建两个SqlSession,也只会执行一次SQL语句
* //创建第一个SqlSession
* emp1和emp2是一级缓存查询,如果在emp1和emp2之间提交,则会将一级缓存保存在二级缓存中
* 因为缓存查询是先查二级,再查一级,这样在查询的时候就会大大提高缓存命中率
*/
SqlSession sqlSession1 = build.openSession();
CacheMapper cacheMapper1 = sqlSession1.getMapper(CacheMapper.class);
Emp emp1 = cacheMapper1.getEmpById(1);
System.out.println(emp1);
//提交
sqlSession1.commit();
CacheMapper cacheMapper2 = sqlSession1.getMapper(CacheMapper.class);
Emp emp2 = cacheMapper2.getEmpById(1);
System.out.println(emp2);
/**
* 创建第二个SqlSession,在第一个SqlSession提交或结束后,一级缓存数据就会保存在二级缓存中
* 在执行查询操作的时候,就能直接在二级缓存中找到数据了
*/
SqlSession sqlSession2 = build.openSession();
CacheMapper cacheMapper3 = sqlSession2.getMapper(CacheMapper.class);
Emp emp3 = cacheMapper3.getEmpById(1);
System.out.println(emp3);
sqlSession2.commit();
}
}
效果图:
在emp1和emp2之后提交:
在emp1和emp2之间提交:
10.3 二级缓存的配置
在Mapper配置文件中添加的cache标签属性:
- eviction属性:缓存回收策略,默认的是 LRU
-
- LRU(Least Recently Used) 最近最少使用的:移除最长时间不被使用的对象。
- FIFO(First in First out) 先进先出:按对象进入缓存的顺序来移除它们。
- SOFT 软引用:移除基于垃圾回收器状态和软引用规则的对象。
- WEAK 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
- flushInterval属性:刷新间隔,单位毫秒
-
- 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
- size属性:引用数目,正整数
-
- 代表缓存最多可以存储多少个对象,太大容易导致内存溢出 (慎用)
- readOnly属性:只读, true/false
-
- true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改,这提供了很重要的性能优势。
- false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。(相当于每次重新获取新数据)
10.4 MyBatis缓存使用顺序
10.5 MyBatis缓存查询顺序
- 先查询二级缓存,因为二级缓存中可能会有其他SqlSession已经查出来的数据,可以拿来直接使用。
- 如果二级缓存没有命中,再查询一级缓存
- 如果一级缓存也没有命中,则查询数据库
- SqlSession关闭之前,一级缓存中的数据会写入二级缓存
10.6 整合第三方缓存EhCache
Ehcache 是一种开源的、基于标准的缓存,可提高性能、卸载数据库并简化可扩展性。它是最广泛使用的基于 Java 的缓存,因为它健壮、经过验证、功能齐全,并且与其他流行的库和框架集成。 Ehcache 从进程内缓存一直扩展到具有 TB 级缓存的混合进程内/进程外部署。 官网地址为: Ehcache
10.6.1 添加依赖
<dependencies>
<!-- Mybatis 和 EHCache整合包 -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
<!-- slf4j日志门面的一个具体实现 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- MyBatis框架 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!--MySql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
10.6.2 创建EHCache的配置文件ehcache.xml
<?xml version="1.0" encoding="utf-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="G:\JavaCode\EhcacheJournal"/>
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
10.6.3 加入logback日志
存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。 创建logback的配置文件logback.xml
<?xml version="1.0" encoding="UTF-8"?> <configuration debug="true">
<!-- 指定日志输出的位置 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 日志输出的格式 -->
<!-- 按照顺序分别是: 时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
<pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
</encoder>
</appender>
<!-- 设置全局日志级别;日志级别按顺序分别是: DEBUG、INFO、WARN、ERROR -->
<!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
<root level="DEBUG">
<!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
<appender-ref ref="STDOUT" />
</root>
<!-- 根据特殊需求指定局部日志级别 -->
<logger name="com.xiaoming.mapper" level="DEBUG"/>
</configuration>
10.6.4 设置二级缓存的类型
<mapper namespace="com.xiaoming.mapper.EmpEhCacheMapper">
<!-- 设置二级缓存类型 -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
<select id="getEmpGyId" resultType="com.xiaoming.pojo.Emp">
select * from emp where emp_id = #{empId}
</select>
</mapper>
10.6.5 EHCache配置文件说明
属性名 | 是否必须 | 作用 |
maxElementsInMemory | 是 | 在内存中缓存的对象的最大数目 |
maxElementsOnDisk | 是 | 在磁盘上缓存的对象的最大数目,若是0表示无 穷大 |
eternal | 是 | 设定缓存的对象s是否永远不过期。 如果为 true,则缓存的数据始终有效,如果为false,那么还要根timeToIdleSeconds、timeToLiveSeconds 判断 |
overflowToDisk | 是 | 设定当内存缓存溢出的时候是否将过期的对象缓存到磁盘上 |
timeToIdleSeconds | 否 | 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdle Seconds的属性取值时, 这些数据便会删除,默认值是0,也就是可闲置时间无穷大 |
timeToLiveSeconds | 否 | 缓存对象的有效生命期,默认是0.,也就是 对象存活时间无穷大 |
diskSpoolBufferSizeMB | 否 | DiskStore(磁盘缓存)的缓存区大小。默认是 30MB。每个Cache都应该有自己的一个缓冲区 |
diskPersistent | 否 | 在VM重启的时候是否启用磁盘保存EhCache中的数 据,默认是false。 |
diskExpiryThreadIntervalSeconds | 否 | 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作 |
memoryStoreEvictionPolicy | 否 | 当内存缓存达到最大,有新的对象加入的时候,移除缓存中对象的策略。 默认是LRU (最近最少使用),可选的有LFU (最不常使用)和FIFO (先进先出) |
十一、MyBatis逆向工程
正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。 Hibernate是支持正向工程的
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
- Java实体类
- Mapper接口
- Mapper映射文件
11.1 创建逆向工程步骤
11.1.1 Pom.xml添加依赖和插件
注意MySql版本和MySql驱动版本要对应上
<?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>com.xiaoming</groupId>
<artifactId>mybatis-mbg</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<!-- 控制Maven在构建过程中相关配置 -->
<build>
<!-- 构建过程中用到的插件 -->
<plugins>
<!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.0</version>
<!-- 插件的依赖 -->
<dependencies>
<!-- 逆向工程的核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
11.1.2 创建逆向工程的配置文件
注意:文件名必须是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>
<!-- targetRuntime: 执行生成的逆向工程的版本
MyBatis3Simple: 生成基本的CRUD(清新简洁版)
MyBatis3: 生成带条件的CRUD(奢华尊享版) -->
<context id="DB2Tables" targetRuntime="MyBatis3Simple">
<!-- 数据库的连接信息 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8"
userId="root"
password="xxxx">
</jdbcConnection>
<!-- JavaBean的生成策略
targetPackage:表示生成的JavaBean存放到哪个包中
targetProject:表示生成的JavaBean存放到哪个主目录中
-->
<javaModelGenerator targetPackage="com.xiaoming.pojo" targetProject=".\src\main\java">
<!-- 是否能够使用子包 -->
<property name="enableSubPackages" value="true" />
<!-- 修剪字符串,根据表的列名,自动修剪为实体类中驼峰命名 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="com.xiaoming.mapper" targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.xiaoming.mapper"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
<!-- domainObjectName属性指定生成出来的实体类的类名 -->
<table tableName="emp" domainObjectName="Emp"/>
<table tableName="dept" domainObjectName="Dept"/>
</context>
</generatorConfiguration>
11.1.3 创建MyBatis核心配置文件
<?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>
<!--MyBatis核心配置文件中,标签的顺序:
properties > settings > typeAliases > typeHandlers > objectFactory > objectWrapperFactory >
reflectorFactory > plugins > environments > databaseIdProvider > mappers> -->
<!--引入properties文件-->
<properties resource="jdbc.properties"/>
<settings>
<!--下划线自动映射为驼峰-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启延迟加载,配置两个配置项-->
<!-- 1、将lazyLoadingEnabled设置为true,开启懒加载功能 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 2、将aggressiveLazyLoading设置为false,关闭“积极的懒加载” -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<!--给某个类型设置别名-->
<typeAliases>
<package name="com.xiaoming.pojo"/>
</typeAliases>
<!--设置连接数据库的环境-->
<environments default="development">
<environment id="development">
<!--事务管理器-->
<transactionManager type="JDBC"/>
<!--数据源-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--引入MyBatis的映射文件-->
<!-- 以包为单位引入映射文件,但必须满足两个条件:
1、mapper接口所在的包(全类名)要和映射文件所在的包(全类名)一致
2、mapper接口要和映射文件的名字一致 -->
<mappers>
<package name="com.xiaoming.mapper"/>
</mappers>
</configuration>
11.1.4 执行插件的generate目标
效果:
11.2 QBC查询
QBC(Query By Criteria ):最大的特点就是将SQL语句中的WHERE子句进行了组件化的封装,让我们可以通过调用Criteria对象的方法自由的拼装查询条件。
package com.xiaoming.test;
import com.xiaoming.mapper.EmpMapper;
import com.xiaoming.pojo.Emp;
import com.xiaoming.pojo.EmpExample;
import com.xiaoming.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
/**
* @ClassName DeptTestPlus
* @Author Lei Jiang Ming
* @CreateTime 2022/9/18 17:32
* @Description 奢华尊享版测试
**/
public class DeptTestPlus {
/**
* @Date 2022/9/18 17:33
* @Description 通过SqlSessionUtil获取EmpMapper
**/
public EmpMapper getEmpMapper() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class);
return empMapper;
}
/**
* @Date 2022/9/18 17:55
* @Description 根据empId查询数据
**/
@Test
public void selectById() {
EmpMapper empMapper = getEmpMapper();
Emp emp = empMapper.selectByPrimaryKey(1);
System.out.println(emp);
}
/**
* @Date 2022/9/18 18:21
* @Description 查询所有数据
**/
@Test
public void selectAll(){
EmpMapper empMapper = getEmpMapper();
List<Emp> empsList = empMapper.selectByExample(null);
empsList.forEach(System.out::println);
}
/**
* @Date 2022/9/18 20:25
* @Description 条件查询,查询性别为女,或年龄是21
**/
@Test
public void select() {
EmpMapper empMapper = getEmpMapper();
EmpExample empExample = new EmpExample();
empExample.createCriteria().andEmpGenderEqualTo("女");
empExample.or().andEmpAgeEqualTo(21);
List<Emp> emps = empMapper.selectByExample(empExample);
emps.forEach(System.out::println);
}
/**
* @Date 2022/9/20 10:26
* @Description 普通更新/条件更新
**/
@Test
public void update(){
EmpMapper empMapper = getEmpMapper();
Emp emp = new Emp(3,"李华",29,null,null);
//普通更新,如果参数为null,则会把这个参数设置为null
// int i = empMapper.updateByPrimaryKey(emp);
//选择更新,如果参数为null,则不会对这个参数进行更改
empMapper.updateByPrimaryKeySelective(emp);
}
/**
* @Date 2022/9/20 10:42
* @Description 条件查询
**/
@Test
public void testQueryByExample(){
EmpExample example = new EmpExample();
//复杂条件:查询(名字中包含琳,并且年龄大于等于20)
EmpExample.Criteria criteria1 = example.createCriteria();
criteria1.andEmpNameLike("%琳%")
.andEmpAgeGreaterThanOrEqualTo(20);
//或者 (emp_id在2-10之间,并且名字中包含李)的所有数据
EmpExample.Criteria criteria2 = example.or();
criteria2.andEmpIdBetween(2,10)
.andEmpNameLike("%李%");
EmpMapper empMapper = getEmpMapper();
List<Emp> emps = empMapper.selectByExample(example);
emps.forEach(System.out::println);
}
}
十二、MyBatis分页插件
pageSize:每页显示的条数
pageNum:当前页的页码
index:当前页的起始索引(索引从0开始)
计算 index = pageSize * (pageNum - 1)
例子:pageSize=5,pageNum=3,index= 5 *( 3-1 ) = 10(第11条数据)
count:总记录数
totalPage:总页数
计算 totalPage = count % pageSize == 0 ? count / pageSize : (count / pageSize)+1
12.1 使用分页插件步骤
12.1.1 添加依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
12.1.2 配置分页插件
在
mybatis_config
配置文件中添加插件注意标签的顺序:
properties > settings > typeAliases > typeHandlers > objectFactory > objectWrapperFactory >
reflectorFactory > plugins > environments > databaseIdProvider > mappers
<!--设置分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
12.1.3 测试
在测试之前需要写好Mapper接口和对应的SQL映射文件、Pojo类
public class EmpTest {
/**
* @Date 2022/9/21 20:25
* @Description 分页插件
**/
@Test
public void test(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//查询之前开启分页功能
//通过拦截器,动态添加sql语句
Page<Object> objects = PageHelper.startPage(1, 6);
List<Emp> emps = mapper.selectAll();
//查询功能之后可以获取分页相关的所有数据,navigatePage:导航分页的页码数
PageInfo<Emp> empPageInfo = new PageInfo<>(emps, 5);
//分页数据
emps.forEach(System.out::println);
System.out.println("--------------------------------------------------");
//分页Page数据
System.out.println(objects);
System.out.println("--------------------------------------------------");
//分页的所有数据
System.out.println(empPageInfo);
}
}
12.1.4 效果
A:普通分页信息(展示一部分数据)
在查询功能之前使用
PageHelper.startPage(int pageNum, int pageSize)
开启分页功能参数:
pageNum:当前页的页码
pageSize:每页显示的条数
B:所有分页信息
在查询获取list集合之后,使用
PageInfo<T> pageInfo = new PageInfo<>(List<T> list, int navigatePages)
获取分页相关数据参数:
list:查询出来的数据(分页之后的数据)
navigatePages:导航分页的页码数
数据解释:
pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
startRow:开始行
endRow:结束行
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页的页码数
navigateFirstPage:导航分页第一页
navigateLastPage:导航分页最后一页
navigatepageNums:导航分页的页码,[1,2,3,4,5]