别人已经整理好的笔记博客
一、Mybatis简介
1. 历史
- 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和Data Access Objects(DAO)
2. 特性
- MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
- MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录
- MyBatis 是一个 半自动的ORM(Object Relation Mapping)框架
3. 下载
GitHub - mybatis/mybatis-3: MyBatis SQL mapper framework for Java
下载最新版本
4. 和其他持久层技术横向比较
- 原生JDBC
- SQL夹杂在Java代码中,造成代码的耦合度较高。
- 维护成本过高,且需要在代码中对SQL语句进行频繁的修改
- 代码冗长,开发效率比较低
- Hibernate和JPA
- 操作比较简便,开发效率较高
- 内部可以自动生成SQL,但是集成度太高,不容易做特殊的优化
- 基于全映射的全自动框架,大量字段的POJO进行部分映射的时候比较困难
- 反射操作太多,造成数据库的性能下降
- Mybatis
- 比较轻量,性能也很出色
- SQL和Java代码分离开来。Java代码专注于业务需求,SQL语句专注于对数据的管理
- 开发效率虽然比不上JPA,但是仍然可以接受
二、Mybatis环境的搭建
1. IDEA开发插件
- MybatisHelperPro:1.1.0
- MybatisX:1.5.2
2. 创建Maven工程
配置打包方式,并引入依赖
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
</dependencies>
3. 创建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.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/MyBatis"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
4. 创建数据库表和实体类
在数据库中创建我们要存储数据所对应的表,然后在Java中编写对应的实体类。
相关概念:ORM(Object Relationship Mapping)对象关系映射 对象:Java的实体类对象 关系:关系型数据库 映射:二者之间的对应关系
Java概念 | 数据库概念 |
---|---|
类 | 表 |
属性 | 字段/列 |
对象 | 记录/行 |
package com.clevesheep.mybatis2.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* Created By Intellij IDEA
*
* @author Xinrui Yu
* @date 2022/3/24 16:21 星期四
*/
@Data
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
private Integer age;
private String gender;
private String email;
}
5. 创建Mapper接口
MyBatis中的mapper接口相当于以前的dao。但是区别在于,mapper仅仅是接口,我们不需要提供实现类
package com.atguigu.mybatis.mapper;
public interface UserMapper {
/**
* 添加用户信息
*/
int insertUser();
}
6. 创建Mybatis的mapper映射文件
- 映射文件的命名规则:
- 表所对应的实体类名称+Mapper.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.atguigu.mybatis.mapper.UserMapper">
<!--int insertUser();-->
<insert id="insertUser">
insert into t_user values(null,'张三','123',23,'女')
</insert>
</mapper>
7. 通过Junit测试方法
SqlSession:代表的是Java程序和数据库之间的会话
- SqlSessionFactory:生成SqlSession的工厂类,使用的是工厂模式
- 工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建这个对象的相关代码封装到一个“工厂类”中,以后都使用这个工厂类来“生产”我们需要的对象
public class UserMapperTest {
@Test
public void testInsertUser() throws IOException {
//读取MyBatis的核心配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//获取SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//通过核心配置文件所对应的字节输入流创建工厂类SqlSessionFactory,生产SqlSession对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
//获取sqlSession,此时通过SqlSession对象所操作的sql都必须手动提交或回滚事务
//SqlSession sqlSession = sqlSessionFactory.openSession();
//创建SqlSession对象,此时通过SqlSession对象所操作的sql都会自动提交
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//通过代理模式创建UserMapper接口的代理实现类对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配元素文件,通过调用的方法名匹配映射文件中的SQL标签,并执行标签中的SQL语句
int result = userMapper.insertUser();
//提交事务
//sqlSession.commit();
System.out.println("result:" + result);
}
}
由于配置Mybatis的时候,使用到的事务管理器是原生的JDBC,因此,我们需要手动的来使用
sqlSession.commit()
来提交我们的事务。或者,在创建sqlSession对象的时候,传入一个boolean值的参数,来表示我们希望使用自动提交的方式提交事务SqlSession sqlSession = sqlSessionFactory.openSession(true);
8. 加入log4j的日志功能
- 引入依赖
- 加入配置文件
- 文件名为:log4j.xml,存放位置为:src/main/resources 目录下
- 日志的级别:FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试) 从左到右打印的内容越来越详细
```xml
<?xml version=”1.0” encoding=”UTF-8” ?>
<!DOCTYPE log4j:configuration SYSTEM “log4j.dtd”>
<a name="itC7O"></a>
# 三、核心配置文件详解
```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>
<!--
mybatis核心配置文件的标签具有一定的顺序,某些标签使用不到的时候可以不写
但是如果使用到了,就一定要按照对应的顺序编写
properties、settings、typeAliases、typeHandlers、
objectFactory、objectWrapperFactory、reflectorFactory、
plugins、environments、databaseIdProvider、mappers
-->
<!--
引入properties文件
-->
<properties resource="jdbc.properties"/>
<!--设置类型别名-->
<typeAliases>
<!--
typeAlias:设置类型别名
属性alias用来表示设置的别名,也可以不设置,那么默认的别名就是类的名称
-->
<!-- <typeAlias type="com.clevesheep.mybatis2.pojo.User" />-->
<!--
package:以包为单位,将其下所有类的别名都设置为默认的别名(类名)
-->
<package name="com.clevesheep.mybatis2.pojo"/>
</typeAliases>
<!--
environments:配置多个连接数据库的环境
属性:
default:默认数据库连接环境的id
-->
<environments default="development">
<!--
environment:配置单个连接数据库的环境
属性:
id: 数据库连接的唯一标识
-->
<environment id="development">
<!--
transactionManager:配置数据库事务的管理方式
属性:
type:JDBC|MANGED
JDBC:在当前环境下执行SQL的时候,使用的是原生JDBC进行事务管理,默认需要我们手动提交和回滚事务
MANGED:事务管理交由别处进行管理,例如Spring
-->
<transactionManager type="JDBC"/>
<!--
dataSource: 配置数据源信息
属性:
type: POOLED|UNPOOLED|JNDI
POOLED: 表示使用数据库连接池缓存数据库连接
UNPOOLED:表示不使用数据库连接池缓存
JNDI:使用上下文的数据源
-->
<dataSource type="POOLED">
<property name="driver" value="${datasource.driver}"/>
<property name="url" value="${datasource.url}"/>
<property name="username" value="${datasource.username}"/>
<property name="password" value="${datasource.password}"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
<!--
引入mapper的配置文件的时候,同样可以采用package的方式进行映射
这样可以不用再每次新建一个mapper的时候,都要重新修改核心配置文件
使用package引入配置文件的注意点:
1. mapper所在接口的包要和映射文件所在的包一致
2. mapper接口要和映射文件的名字一致
个人更喜欢使用一个个mapper标签引入
-->
<!-- <package name=""/>-->
</mappers>
</configuration>
四、默认的类型别名
五、Mybatis实现简单的增删改查
1. 增加
<!--int insertUser();-->
<insert id="insertUser">
insert into t_user values(null,'admin','123456',23,'男','12345@qq.com')
</insert>
2. 删除
<!--int deleteUser();-->
<delete id="deleteUser">
delete from t_user where id = 6
</delete>
3. 修改
<!--int updateUser();-->
<update id="updateUser">
update t_user set username = '张三' where id = 5
</update>
4. 查询一个实体类对象
<!--User getUserById();-->
<select id="getUserById" resultType="User">
select * from t_user where id = 2
</select>
5. 查询集合
<!--List<User> getUserList();-->
<select id="getUserList" resultType="User">
select * from t_user
</select>
查询的标签 select * from t_user where username = #{username}
```xml
<!--User getUserByUsername(String username);-->
<select id="getUserByUsername" resultType="User">
select * from t_user where username = '${username}'
</select>
2. 多个字面量类型的参数
- 如果mapper接口中参数是多个的时候,此时Mybatis会自动将这些参数放在一个Map集合中
- 以arg0,arg1…为键,参数为值
- 以param1,param2…为键,参数为值
- 因此只需要通过
${}
或#{}
来获取对应的值- 注意使用
${}
的时候,要手动加上单引号 ```xml
```xml
<!--User checkLogin(String username,String password);-->
<select id="checkLogin" resultType="User">
select * from t_user where username = '${param1}' and password = '${param2}'
</select>
3. Map类型的参数
- 若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号 ```xml
```java
@Test
public void checkLoginByMap() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("usermane","admin");
map.put("password","123456");
User user = mapper.checkLoginByMap(map);
System.out.println(user);
}
4. 实体类型的参数
若mapper接口中的方法参数为实体类对象时此时可以使用${}和#{},通过访问实体类对象中的属性名获取属性值,注意${}需要手动加单引号
<!--int insertUser(User user);-->
<insert id="insertUser">
insert into t_user values(null,#{username},#{password},#{age},#{sex},#{email})
</insert>
@Test
public void insertUser() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
User user = new User(null,"Tom","123456",12,"男","123@321.com");
mapper.insertUser(user);
}
5. 使用@Param注解标识参数
可以通过@Param注解标识mapper接口中的方法参数,此时,会将这些参数放在map集合中
- 以@Param注解的value属性值为键,以参数为值;
- 以param1,param2…为键,以参数为值;
- 只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号
<!--User CheckLoginByParam(@Param("username") String username, @Param("password") String password);-->
<select id="CheckLoginByParam" resultType="User">
select * from t_user where username = #{username} and password = #{password}
</select>
@Test
public void checkLoginByParam() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
mapper.CheckLoginByParam("admin","123456");
}
6. 总结
建议分成以下两种情况进行处理
- 实体类型的参数
- 使用@Param标识参数
七、Mybatis的各种查询功能
- 如果查询出的数据只有一条,可以通过
- 实体类对象接收
- List集合接收
- Map集合接收,结果
{password=123456, sex=男, id=1, age=23, username=admin}
- 如果查询出的数据有多条,一定不能用实体类对象接收,会抛异常TooManyResultsException,可以通过
- 实体类类型的LIst集合接收
- Map类型的LIst集合接收
- 在mapper接口的方法上添加@MapKey注解
1. 查询一个实体类对象
/**
* 根据用户id查询用户信息
* @param id
* @return
*/
User getUserById(@Param("id") int id);
<!--User getUserById(@Param("id") int id);-->
<select id="getUserById" resultType="User">
select * from t_user where id = #{id}
</select>
2. 查询一个List集合
/**
* 查询所有用户信息
* @return
*/
List<User> getUserList();
<!--List<User> getUserList();-->
<select id="getUserList" resultType="User">
select * from t_user
</select>
3. 查询单个数据
/**
* 查询用户的总记录数
* @return
* 在MyBatis中,对于Java中常用的类型都设置了类型别名
* 例如:java.lang.Integer-->int|integer
* 例如:int-->_int|_integer
* 例如:Map-->map,List-->list
*/
int getCount();
<!--int getCount();-->
<select id="getCount" resultType="_integer">
select count(id) from t_user
</select>
4. 查询一条数据为Map集合
/**
* 根据用户id查询用户信息为map集合
* @param id
* @return
*/
Map<String, Object> getUserToMap(@Param("id") int id);
<!--Map<String, Object> getUserToMap(@Param("id") int id);-->
<select id="getUserToMap" resultType="map">
select * from t_user where id = #{id}
</select>
<!--结果:{password=123456, sex=男, id=1, age=23, username=admin}-->
5. 查询多条数据为Map集合
方法一:
/**
* 查询所有用户信息为map集合
* @return
* 将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,此时可以将这些map放在一个list集合中获取
*/
List<Map<String, Object>> getAllUserToMap();
<!--Map<String, Object> getAllUserToMap();-->
<select id="getAllUserToMap" resultType="map">
select * from t_user
</select>
<!--
结果:
[{password=123456, sex=男, id=1, age=23, username=admin},
{password=123456, sex=男, id=2, age=23, username=张三},
{password=123456, sex=男, id=3, age=23, username=张三}]
-->
方法二:
/**
* 查询所有用户信息为map集合
* @return
* 将表中的数据以map集合的方式查询,一条数据对应一个map;
* 若有多条数据,就会产生多个map集合,并且最终要以一个map的方式返回数据,
* 此时需要通过@MapKey注解设置map集合的键,值是每条数据所对应的map集合
*/
@MapKey("id")
Map<String, Object> getAllUserToMap();
<!--Map<String, Object> getAllUserToMap();-->
<select id="getAllUserToMap" resultType="map">
select * from t_user
</select>
<!--
结果:
{
1={password=123456, sex=男, id=1, age=23, username=admin},
2={password=123456, sex=男, id=2, age=23, username=张三},
3={password=123456, sex=男, id=3, age=23, username=张三}
}
-->
八、特殊SQL的执行
1. 模糊查询
/**
* 根据用户名进行模糊查询
* @param username
* @return java.util.List<com.atguigu.mybatis.pojo.User>
* @date 2022/2/26 21:56
*/
List<User> getUserByLike(@Param("username") String username);
<!--List<User> getUserByLike(@Param("username") String username);-->
<select id="getUserByLike" resultType="User">
<!--select * from t_user where username like '%${mohu}%'-->
<!--select * from t_user where username like concat('%',#{mohu},'%')-->
select * from t_user where username like "%"#{mohu}"%"
</select>
其中
select * from t_user where username like "%"#{mohu}"%"
是最常用的2. 批量删除
只能使用${},如果使用#{},则解析后的sql语句为
delete from t_user where id in ('1,2,3')
,这样是将1,2,3
看做是一个整体,只有id为1,2,3
的数据会被删除。 正确的语句应该是delete from t_user where id in (1,2,3)
,或者delete from t_user where id in ('1','2','3')
/**
* 根据id批量删除
* @param ids
* @return int
* @date 2022/2/26 22:06
*/
int deleteMore(@Param("ids") String ids);
<delete id="deleteMore">
delete from t_user where id in (${ids})
</delete>
//测试类
@Test
public void deleteMore() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
SQLMapper mapper = sqlSession.getMapper(SQLMapper.class);
int result = mapper.deleteMore("1,2,3,8");
System.out.println(result);
}
3. 动态设置表名
只能使用${},因为表名不能加单引号
/**
* 查询指定表中的数据
* @param tableName
* @return java.util.List<com.atguigu.mybatis.pojo.User>
* @date 2022/2/27 14:41
*/
List<User> getUserByTable(@Param("tableName") String tableName);
<!--List<User> getUserByTable(@Param("tableName") String tableName);-->
<select id="getUserByTable" resultType="User">
select * from ${tableName}
</select>
4. 添加功能获取自动递增的主键
在mapper.xml中设置两个属性
- useGeneratedKeys:设置使用自增的主键
- keyProperty:因为增删改有统一的返回值是受影响的行数,因此只能将获取的自增的主键放在传输的参数user对象的某个属性中
/**
* 添加用户信息
* @param user
* @date 2022/2/27 15:04
*/
void insertUser(User user);
<!--void insertUser(User user);-->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into t_user values (null,#{username},#{password},#{age},#{sex},#{email})
</insert>
//测试类
@Test
public void insertUser() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
SQLMapper mapper = sqlSession.getMapper(SQLMapper.class);
User user = new User(null, "ton", "123", 23, "男", "123@321.com");
mapper.insertUser(user);
System.out.println(user);
//输出:user{id=10, username='ton', password='123', age=23, sex='男', email='123@321.com'},自增主键存放到了user的id属性中
}
九、自定义映射resultMap
1. 通过resultMap来处理字段和属性之间的映射关系
- resultMap:设置自定义映射
- 属性:
- id:表示自定义映射的唯一标识,不能重复
- type:查询的数据要映射的实体类的类型
- 子标签:
- id:设置主键的映射关系
- result:设置普通字段的映射关系
- 子标签属性:
- property:设置映射关系中实体类中的属性名
- column:设置映射关系中表中的字段名
如果数据库表的字段名和类的属性名不一致,那么可以通过resultMap来设置自定义映射,那么此时,即使字段名和属性名一致也要映射,也就是全部的属性都要列出来
<resultMap id="empResultMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
</resultMap>
<!--List<Emp> getAllEmp();-->
<select id="getAllEmp" resultMap="empResultMap">
select * from t_emp
</select>
若字段名和实体类中的属性名不一致,但是字段名符合数据库的规则(使用_),实体类中的属性名符合Java的规则(使用驼峰)。此时也可通过以下两种方式处理字段名和实体类中的属性的映射关系
通过在SQL语句中,给字段起别名的方式,来保证实体类和数据表中的属性名保持一致
<!--List<Emp> getAllEmp();-->
<select id="getAllEmp" resultType="Emp">
select eid,emp_name empName,age,sex,email from t_emp
</select>
在Mybatis的核心配置文件的
setting
中,进行配置
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
2. 多对一映射处理
2.1 associationl来处理映射关系
association:处理多对一的映射关系
- property:需要处理多对的映射关系的属性名
javaType:该属性的类型
<resultMap id="empAndDeptResultMapTwo" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<association property="dept" javaType="Dept">
<id property="did" column="did"></id>
<result property="deptName" column="dept_name"></result>
</association>
</resultMap>
<!--Emp getEmpAndDept(@Param("eid")Integer eid);-->
<select id="getEmpAndDept" resultMap="empAndDeptResultMapTwo">
select * from t_emp left join t_dept on t_emp.eid = t_dept.did where t_emp.eid = #{eid}
</select>
2.2 分步查询
先查员工对应的部门id,然后通过部门id去部门表中查询部门的信息
select:对应SQL方法的唯一标识=>全类名.方法
column:被传入下一步查询的字段
<resultMap id="empAndDeptByStepResultMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<association property="dept"
select="com.atguigu.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="did"></association>
</resultMap>
分步查询的优点:可以实现懒加载模式,但是必须在核心配置文件中设置全局的配置信息
2.3 延迟加载(懒加载模式)
需要在核心配置文件中进行配置
lazyLoadingEnabled
:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载aggressiveLazyLoading
:当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType=“lazy(延迟加载)|eager(立即加载)”
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
@Test
public void getEmpAndDeptByStepOne() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptByStepOne(1);
System.out.println(emp.getEmpName());
}
关闭延迟查询,两条SQL会一起执行
- 开启延迟加载,只执行获取Employee的SQL
- 开启后,只有需要用到Employee中的Department时候才会调用相应查询的SQL语句
- fetchType:当开启了全局的延迟加载之后,可以通过该属性手动控制延迟加载的效果
fetchType=lazy(延迟加载)|eager(立即加载)
<resultMap id="empAndDeptByStepResultMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<association property="dept"
select="com.atguigu.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="did"
fetchType="lazy"></association>
</resultMap>
3. 一对多映射处理
3.1 collection
- collection:用来处理一对多的映射关系
ofType:表示该属性对应集合中存储的数据类型(或类名)
<resultMap id="getAllEmployeesByDeptIdResultMap" type="Department">
<id property="deptId" column="dept_id" />
<result property="deptName" column="dept_name" />
<result property="deptLocation" column="dept_location" />
<collection property="employees" ofType="Employee">
<id property="empId" column="emp_id" />
<result property="empName" column="emp_name"/>
<result property="empPhone" column="emp_phone" />
<result property="empGender" column="emp_gender" />
</collection>
</resultMap>
3.2 分步查询
先通过部门id查询部门的信息,然后通过部门id去员工表中查询所有的员工信息
<resultMap id="getAllEmployeesByDeptIdAndStepResultMap" type="Department">
<id property="deptId" column="dept_id" />
<result property="deptName" column="dept_name" />
<result property="deptLocation" column="dept_location" />
<!--同样可以设置为懒加载模式-->
<collection property="employees"
select="com.clevesheep.mybatis3.mapper.EmployeeMapper.getAllByDeptId"
column="dept_id"
fetchType="lazy"/>
</resultMap>
<select id="getAllByDeptId" resultType="Employee">
select * from t_emp where dept_id = #{dept_id}
</select>
十、动态SQL
1. IF标签
if标签可通过test属性(即传递过来的数据)的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之标签中的内容不会执行
在where后面添加一个恒成立条件
1=1
- 这个恒成立条件并不会影响查询的结果
- 这个
1=1
可以用来拼接and
语句,例如:当empName为null时
- 如果不加上恒成立条件,则SQL语句为
select * from t_emp where and age = ? and sex = ? and email = ?
,此时where
会与and
连用,SQL语句会报错- 如果加上一个恒成立条件,则SQL语句为
select * from t_emp where 1= 1 and age = ? and sex = ? and email = ?
,此时不报错
<!--List<Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultType="Emp">
select * from t_emp where 1=1
<if test="empName != null and empName !=''">
and emp_name = #{empName}
</if>
<if test="age != null and age !=''">
and age = #{age}
</if>
<if test="sex != null and sex !=''">
and sex = #{sex}
</if>
<if test="email != null and email !=''">
and email = #{email}
</if>
</select>
2. WHERE标签
where和if一般结合使用:
- 若where标签中的if条件都不满足,则where标签没有任何功能,即不会添加where关键字
- 若where标签中的if条件满足,则where标签会自动添加where关键字,并将条件最前方多余的and/or去掉
<!--List<Emp> getEmpByCondition(Emp emp);-->
<select id="getEmpByCondition" resultType="Emp">
select * from t_emp
<where>
<if test="empName != null and empName !=''">
emp_name = #{empName}
</if>
<if test="age != null and age !=''">
and age = #{age}
</if>
<if test="sex != null and sex !=''">
and sex = #{sex}
</if>
<if test="email != null and email !=''">
and email = #{email}
</if>
</where>
</select>
注意:where标签不能去掉条件后多余的and/or
<!--这种用法是错误的,只能去掉条件前面的and/or,条件后面的不行-->
<if test="empName != null and empName !=''">
emp_name = #{empName} and
</if>
<if test="age != null and age !=''">
age = #{age}
</if>
3. choose、when、otherwise
choose
、when
、otherwise
相当于if...else if..else
when至少要有一个,otherwise至多只有一个
<select id="getEmpByChoose" resultType="Emp">
select * from t_emp
<where>
<choose>
<when test="empName != null and empName != ''">
emp_name = #{empName}
</when>
<when test="age != null and age != ''">
age = #{age}
</when>
<when test="sex != null and sex != ''">
sex = #{sex}
</when>
<when test="email != null and email != ''">
email = #{email}
</when>
<otherwise>
did = 1
</otherwise>
</choose>
</where>
</select>
4. foreach标签
属性:
- collection:设置要循环的数组或集合
- item:表示集合或数组中的每一个数据
- separator:设置循环体之间的分隔符,分隔符前后默认有一个空格,如,
- open:设置foreach标签中的内容的开始符
- close:设置foreach标签中的内容的结束符
举例
批量删除
<!--int deleteMoreByArray(Integer[] eids);-->
<delete id="deleteMoreByArray">
delete from t_emp where eid in
<foreach collection="eids" item="eid" separator="," open="(" close=")">
#{eid}
</foreach>
</delete>
@Test
public void deleteMoreByArray() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
int result = mapper.deleteMoreByArray(new Integer[]{6, 7, 8, 9});
System.out.println(result);
}
批量添加
<!--int insertMoreByList(@Param("emps") List<Emp> emps);-->
<insert id="insertMoreByList">
insert into t_emp values
<foreach collection="emps" item="emp" separator=",">
(null,#{emp.empName},#{emp.age},#{emp.sex},#{emp.email},null)
</foreach>
</insert>
@Test
public void insertMoreByList() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
Emp emp1 = new Emp(null,"a",1,"男","123@321.com",null);
Emp emp2 = new Emp(null,"b",1,"男","123@321.com",null);
Emp emp3 = new Emp(null,"c",1,"男","123@321.com",null);
List<Emp> emps = Arrays.asList(emp1, emp2, emp3);
int result = mapper.insertMoreByList(emps);
System.out.println(result);
}
5. SQL标签
<sql />
标签用来记录一段SQL片段,在使用的地方可以使用<include />
标签进行引入
声明SQL标签:<sql>
使用SQL标签:<include>
十一、Mybatis的缓存
1. 一级缓存
一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会进行缓存保留,下次查询相同数据的时候就会直接从缓存中获取,而不会从数据库中重新访问。
- 一级缓存失效的四种情况
- 不同的SqlSession对应不同的一级缓存
- 同一个SqlSession但是查询条件不同
- 同一个SqlSession两次查询期间执行了任何一次增删改操作
- 同一个SqlSession两次查询期间手动清空了缓存
2. 二级缓存
- 二级缓存是SqlSessionFactory级别的,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
- 二级缓存的开启方式
- 核心配置文件中,设置全局配置属性
cacheEnabled="true"
,默认就是true,无需进行设置- 在映射文件中设置对应的标签
<cache />
- 二级缓存必须在SqlSession关闭或提交之后生效
- 查询的数据对应的实体类必须实现序列化接口
- 二级缓存失效的情况:两个查询之间进行了任意的增删改操作,会导致一级和二级缓存同时失效
3. Mybatis缓存查询的顺序
- 首先查询二级缓存,因为二级缓存中可能有已经查询出来的数据
- 如果二级缓存未命中,则再查询一级缓存
- 如果一级缓存也未命中,那么则查询数据库
- SqlSession关闭之后,对应的一级缓存会写入二级缓存,供其他程序调用