这里写目录标题
- 1.Mybatis
- 1.1 JDBC的缺点
- 1.2 Mybatis的整体架构
- 1.3 入门案例
- 1.3.1 问题:无法连接到数据库服务器
- 1.4 动态代理实现Mapper
- 1.5 mybatis-config.xml配置
- 1.5.1 properties属性读取外部资源
- 1.5.2 settings设置
- 1.5.3 typeAliases
- 1.5.4 typeHandlers(类型处理器)
- 1.5.5 plugins(插件又称拦截器)
- 1.5.5.1 自定义拦截器
- 1.5.6 environments(环境)
- 1.5.7 Mappers
- 总结
- 1.6 Mapper.xml(映射文件)
- 1.6.1 CRUD操作
- 1.7 parameterType传入参数
- 1.7.1 ${}的用法
- 1.7.2 多个参数
- 1.7.3 HashMap
- 1.7.4 面试题(#、$区别)
- 1.8 resultMap
- 1.8.1 解决列名和属性名不一致
- 1.8.2 属性的作用
- 1.9 sql片段
- 1.9.1 用法一:内部定义
- 1.9.1 用法二:外部定义
- 1.10 动态sql
- 1.10.1 if
- 1.10.2 choose、when、otherwise
- 1.10.3 where
- 1.10.4 set
- 1.10.5 foreach
- 1.11 缓存
- 1.11.1 一级缓存
- 1.11.1.1 清除一级缓存 方式一:clearCache
- 1.11.1.2 清除一级缓存 方式二:执行update、insert、delete的时候,会清空缓存
- 1.11.2 二级缓存
- 2.Mybatis的高级查询
- 2.1 一对一查询
- 2.2 一对多查询
- 2.3 多对多查询
- 2.4 resultMap的继承
- 2.5 延迟加载
- 2.5.1 改造一对一查询
- 2.5.2 开启延迟加载
1.Mybatis
- MyBatis 是一款优秀的持久层框架,用于简化 JDBC 开发
JavaEE三层架构:表现层、业务层、持久层
持久层:负责将数据到保存到数据库的那一层代码
1.1 JDBC的缺点
1.2 Mybatis的整体架构
-
配置文件
- 全局配置文件mybatis-config.xml
作用:配置数据源,引入映射文件 - 映射文件:XxMapper.xml
作用:配置sql语句、参数、结果集封装类型等
- 全局配置文件mybatis-config.xml
-
SqlSessionFactory
作用:获取SqlSession -
SqlSession
作用:执行CRUD -
Executor
执行器,SqlSession通过调用它来完成具体的CRUD
它是一个接口,提供了两种实现:缓存的实现、数据库的实现 -
Mapped Statement
在映射文件里面配置,包含3部分内容:
具体的sql,sql执行所需的参数类型,sql执行结果的封装类型
1.3 入门案例
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">
<!-- namespace(命名空间):映射文件的唯一标识 -->
<mapper namespace="UserMapper">
<!-- 查询的statement,id:在同一个命名空间下的唯一标识,resultType:sql语句的结果集封装类型 -->
<select id="queryUserById" resultType="cn.itcast.mybatis.pojo.User">
select * from tb_user where id=#{id}
</select>
</mapper>
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>
<!-- 环境:说明可以配置多个,default:指定生效的环境 -->
<environments default="development">
<!-- id:环境的唯一标识 -->
<environment id="development">
<!-- 事务管理器,type:类型 -->
<transactionManager type="JDBC" />
<!-- 数据源:type-池类型的数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis-49" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<!-- 映射文件 -->
<mappers>
<mapper resource="UserMapper.xml" />
</mappers>
</configuration>
MybatisTest
public static void main(String[] args) throws IOException {
SqlSession sqlSession = null;
try {
// 指定mybatis的全局配置文件
String resource = "mybatis-config.xml";
// 读取mybatis-config.xml配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
// 构建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取sqlSession回话
sqlSession = sqlSessionFactory.openSession();
// 执行查询操作,获取结果集。参数:1-命名空间(namespace)+“.”+statementId,2-sql的占位符参数
User user = sqlSession.selectOne("UserMapper.queryUserById", 1l);
System.out.println(user);
} finally {
// 关闭连接
if (sqlSession != null) {
sqlSession.close();
}
}
}
1.3.1 问题:无法连接到数据库服务器
产生这个问题的原因有很多,此处只讲一个情况
因为当前mysql的版本是8.0.31,而引入的依赖包是5.132所以导致了这个问题,只要引入8.X版本的依赖即可
同时8.X版本的驱动也要改
1.4 动态代理实现Mapper
1.XxxMapper接口
public interface UserMapper {
/**
* 根据id获取用户信息
* @param id
* @return
*/
public User queryUserById(Long id);
/**
* 查询所有用户
* @return
*/
public List<User> queryUserAll();
/**
* 新增用户
* @param user
*/
public void insertUser(User user);
/**
* 更新用户信息
* @param user
*/
public void updateUser(User user);
/**
* 根据id删除用户信息
* @param id
*/
public void deleteUserById(Long id);
}
2.XxxMapper.xml
几个要求
- Mapper接口和SQL映射文件放置在同一目录下
- 名称空间必须改成XxxMapper接口的全路径
- StatementId必须和接口方法名一致
- 结果集的封装类型已经和方法的返回类型一致
<?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="cn.itcast.mybatis.mapper.UserMapper">
</mapper>
测试
public class UserMapperTest {
private UserMapper userMapper;
@Before
public void setUp() throws Exception {
// 读取mybatis的全局配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 构建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取sqlSession会话
SqlSession sqlSession = sqlSessionFactory.openSession();
// 初始化userDao
this.userMapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testQueryUserAll() {
List<User> userList = this.userMapper.queryUserAll();
for (User user : userList) {
System.out.println(user);
}
}
}
Mapper代理的底层,依旧是通过SqlSession来操作CRUD
为了实现在maven默认环境下打包时,Mybatis的接口和mapper文件在同一包中,可以通过将接口文件放在src/main/java某个包中,而在src/main/resources目录中建立同样的包,这是一种约定优于配置的方式,这样在maven打包的时候就会将src/main/java和src/main/resources相同包下的文件合并到同一包中。
1.5 mybatis-config.xml配置
mybatis-config.xml讲究严格的顺序,具体顺序遵循文档的顺序
1.5.1 properties属性读取外部资源
添加jdbc.properties资源文件:
jdbc.properties资源文件内容:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/mybatis-44
username=root
password=root
在Mybatis-config.xml中引入jdbc.properties资源文件:
<?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>
<!-- 引入外部资源文件,resource:相对路径,url:绝对路径 -->
<properties resource="jdbc.properties" />
......
</configuration>
通过properties引入外部资源文件之后,就可以通过${xxx}的方式使用资源文件里的参数了。
1.5.2 settings设置
- cacheEnabled(可以看mysql缓存章节的说明)
- 该配置影响的所有映射器中配置的缓存的全局开关。
- 默认值true
- lazyLoadingEnabled
- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。
- 默认值false
- aggressiveLazyLoading
- 当启用时,带有延迟加载属性的对象的加载与否完全取决于对任意延迟属性的调用;反之,每种属性将会按需加载。
- 默认值true
- mapUnderscoreToCamelCase
- 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。
- 默认值true
<?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>
<!-- 引入外部资源文件,resource:相对路径,url:绝对路径 -->
<properties resource="jdbc.properties" />
<settings>
<!-- 开启驼峰匹配:经典的数据库列名(多个单词下划线连接)映射到经典的java属性名(多个单词驼峰连接) -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
1.5.3 typeAliases
在映射文件中用到java类型时,都是使用类的全路径,书写起来非常麻烦
typeAliases的几种方式:
- 1.typeAlias 为每个pojo提供类型别名
缺点:每个pojo类都要去配置。
<typeAliases>
<!-- 引入外部资源文件,resource:相对路径,url:绝对路径 -->
<properties resource="jdbc.properties" />
<settings>
<!-- 开启驼峰匹配:经典的数据库列名(多个单词下划线连接)映射到经典的java属性名(多个单词驼峰连接) -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 类型别名:type-pojo类的全路径,alias-别名名称(可随便写,推荐和类名一致) -->
<typeAlias type="cn.itcast.mybatis.pojo.User" alias="user" />
</typeAliases>
- 2.package 扫描包下所有类,扫描之后别名就是类名,不区分大小写,建议使用时与类名一致
<typeAliases>
<!-- 类型别名:type-pojo类的全路径,alias-别名名称(可随便写,推荐和类名一致) -->
<!-- <typeAlias type="cn.itcast.mybatis.pojo.User" alias="user" /> -->
<!-- 开启别名包扫描,name:包路径,扫描的别名就是类名,并且大小写不敏感 -->
<package name="cn.itcast.mybatis.pojo"/>
</typeAliases>
内置别名:
已经为普通的 Java 类型内建了许多相应的类型别名。它们都是大小写不敏感的,需要注意的是由于重载原始类型的名称所做的特殊处理。
1.5.4 typeHandlers(类型处理器)
- 在预处理语句(PreparedStatement)中设置参数
java类型 > 类型处理器 > jdbc类型 - 从结果集取出一个值
jdbc类型 > 类型处理器 > java类型
由于Java 类型和数据库的 JDBC 类型不是一一对应的(比如 String 与 varchar), 所以我们把 Java 对象转换为数据库的值,和把数据库的值转换成 Java 对象,需要经过 一定的转换,这两个方向的转换就要用到 TypeHandler
下表描述了一些Mybatis内置的类型处理器。
Mybatis标签之 typeHandlers 使用及解析
1.5.5 plugins(插件又称拦截器)
Mybatis 定义了四个处理器,用于做 SQL 执行的默认处理;如果我们不添加拦截器,则 Mybatis 会按默认操作进行处理。
- Executor
- ParameterHandler
- StatementHandler
- ResultSetHandler
执行顺序如下
1.5.5.1 自定义拦截器
package com.itheima.mybatis.interceptor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
/**
* Intercepts:拦截器注解
* 作用:标明此类为mybatis的拦截类
* 前提:需要手动将此类注册到mybatis的拦截器链InterceptorChain中,才能生效
* Signature:方法签名注解 (底层是通过反射实现)
* 作用:表明拦截哪个处理器的哪个方法,即三个属性:
* type:处理器类
* method:方法名
* args:方法参数
*/
@Intercepts({@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class myExecutor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
Object obj = (Object) invocation.getArgs()[1];
RowBounds rb = (RowBounds) invocation.getArgs()[2];
ResultHandler rh = (ResultHandler) invocation.getArgs()[3];
return null;
}
}
配置自定义拦截器(也可以使用spring容器管理的方式来配置)
<?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>
<plugins>
<plugin interceptor="com.itheima.mybatis.interceptor.myExecutor"></plugin>
</plugins>
</configuration>
作用:
- 分页查询
- 打印sql日志
- 对返回结果,过滤掉审计字段,敏感字段
- 对返回结果中的加密数据进行解密(全局解密)
- 对新增数据自动添加创建人,创建时间,更新时间,更新人 ,对更新数据自动新增更新时间,更新人
Mybatis拦截器说明和使用
Mybatis之4大插件管理
1.5.6 environments(环境)
- MyBatis 可以配置成适应多种环境,例如,开发、测试和生产环境需要有不同的配置;
- 尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。
几种方式:
-
方法一:default
-
方法二:build方法
1.5.7 Mappers
方式:
-
方式一:resource
缺点:每次都要在mybatis-config.xml中引入映射文件,麻烦 -
方式二: file
缺点:
1.硬盘的位置可能随着项目的部署或迁移,路径发生变化
2.每新增一个映射文件,就要在全局配置文件中引入 -
方式三: class
缺点:
1.java文件和xml映射文件耦合(需要在同一个文件夹下)
2.每新增一个映射文件,就要在全局配置文件中引入 -
方式四: package
缺点:
1、如果包的路径有很多
2、mapper.xml和mapper.java没有分离。
总结
- 1.properties:引入外部资源文件,jdbc.properties 两种引入方式:url resource
- 2.settings:行为参数, mapUnderscoreToCamelCase(驼峰匹配:从经典的数据库列名到经典的java属性名的映射)
- 3.typeAliases:类型别名,typeAlias(type:类的全路径,alias:别名名称) package(name:包的全路径)
- 4.typeHandlers:类型处理器
- 5.plugins:插件、拦截器
- 6.environments:环境,default:指定采用那个环境, environment:id-唯一标识,子标签:transactionManager:事务管理器 DataSource:数据源
1.6 Mapper.xml(映射文件)
1.6.1 CRUD操作
- select标签
- insert标签
- update标签
- delete标签
1.select标签
2.insert
3.update
4.delete
1.7 parameterType传入参数
CRUD标签都有一个属性parameterType,statement通过它指定接收的参数类型。
接收参数的方式有两种:
- #{}预编译
- ${}非预编译(直接的sql拼接,不能防止sql注入)
参数类型有三种:
- 1、基本数据类型
- 2、HashMap(使用方式和pojo类似)
- 3、Pojo自定义包装类型
1.7.1 ${}的用法
使用场景:数据库有两个一模一样的表。历史表,当前表
查询表中的信息,有时候从历史表中去查询数据,有时候需要去新的表去查询数据。希望使用1个方法来完成操作。
1.7.2 多个参数
当mapper接口要传递多个参数时,有两种传递参数的方法:
-
1、默认规则获取参数{0,1…}、{param1,param2…}
-
2、使用@Param注解指定参数名
1.7.3 HashMap
- 简单类型通过#{key}或者${key}
- 复杂类型通过${key.属性名}或者#{key.属性名}
1.7.4 面试题(#、$区别)
#{}的作用是防止sql注入(没有真实的预编译功能),所以我们每次执行sql,依旧要对sql语法进行检查和编译(可以参照PreparedStatement原理这一章节)
1.8 resultMap
1.8.1 解决列名和属性名不一致
查询数据的时候,查不到userName的信息,原因:数据库的字段名是user_name,而POJO中的属性名字是userName,两端不一致,造成mybatis无法填充对应的字段信息。
解决方案
- 1.在sql语句中使用别名
- 2.参考驼峰匹配 — mybatis-config.xml 的时候
- 3.resultMap自定义映射
resultMap的解决方式
1.8.2 属性的作用
- id
此resultMap的唯一标识 - type
结果集的封装类型 - autoMapping
开启自动匹配,默认为true,没有手动指定映射的字段就会使用自动匹配
如果开启了驼峰匹配,就会以驼峰匹配的形式进行
1.9 sql片段
在java代码中,为了提高代码的重用性,针对一些出现频率较高的代码,抽离出来一个共同的方法或者类
1.9.1 用法一:内部定义
1.9.1 用法二:外部定义
定义好sql片段的映射文件之后,接下来就该使用它了,首先应该把该映射文件引入到mybatis的全局配置文件中(mybatis-config.xml):
最后在需要使用该sql片段的地方通过include标签的refId属性引用该sql片段:<include refId=”名称空间.sql片段的id” />
在UserMapper.xml的映射文件中,进一步改造根据用户名查询用户信息
1.10 动态sql
1.10.1 if
<select id="queryUserListLikeUserName" resultType="User">
select * from tb_user where sex=1
<!-- if:判断
test:OGNL表达式
-->
<if test="userName!=null and userName.trim()!=''">
and user_name like '%' #{userName} '%'
</if>
</select>
1.10.2 choose、when、otherwise
<select id="queryUserListLikeUserNameOrAge" resultType="User">
select * from tb_user where sex=1
<!-- choose:条件选择
when:test-判断条件,一旦有一个when成立,后续的when都不再执行
otherwise:所有的when都不成立时,才会执行
-->
<choose>
<when test="userName!=null and userName.trim()!=''">and user_name like '%' #{userName} '%'</when>
<when test="age != null">and age = #{age}</when>
<otherwise>and user_name = 'zhangsan' </otherwise>
</choose>
</select>
1.10.3 where
<select id="queryUserListLikeUserNameOrAge" resultType="User">
select * from tb_user where sex=1
<!-- choose:条件选择
when:test-判断条件,一旦有一个when成立,后续的when都不再执行
otherwise:所有的when都不成立时,才会执行
-->
<choose>
<when test="userName!=null and userName.trim()!=''">and user_name like '%' #{userName} '%'</when>
<when test="age != null">and age = #{age}</when>
<otherwise>and user_name = 'zhangsan' </otherwise>
</choose>
</select>
1.10.4 set
<update id="updateUserSelective" >
UPDATE tb_user
<!--
set自动添加set关键字
也有一定的纠错功能:自动去掉sql语句块之后多余的一个逗号
-->
<set>
<if test="userName!=null and userName.trim()!=''">user_name = #{userName},</if>
<if test="password!=null and password.trim()!=''">password = #{password},</if>
<if test="name!=null and name.trim()!=''">name = #{name},</if>
<if test="age!=null">age = #{age},</if>
<if test="sex!=null">sex = #{sex}</if>
</set>
WHERE
(id = #{id});
</update>
1.10.5 foreach
/**
* 根据多个id查询用户信息
* @param ids
* @return
*/
public List<User> queryUserListByIds(@Param("ids")Long[] ids);
<select id="queryUserListByIds" resultType="User">
select * from tb_user where id in
<!--
foreach:遍历集合
collection:接收的集合参数
item:遍历的集合中的一个元素
separator:分隔符
open:以什么开始
close:以什么结束
-->
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
1.11 缓存
执行相同的sql语句
和参数
,mybatis不进行执行sql,而是从缓存中命中返回。
- 一级缓存的作用域是同一个sqlsession内;
- 二级缓存作用域是针对mapper进行缓存;
一级缓存是默认使用的,二级缓存需要手动开启。
1.11.1 一级缓存
在mybatis中,一级缓存默认是开启的,并且一直无法关闭,作用域:在同一个sqlSession下
前提:
- 同一个Mapper对象(底层使用sqlSession,本质上要求同一个sqlSession)
- 执行相同的sql语句,以及参数相同
参数不同,就不会走缓存
1.11.1.1 清除一级缓存 方式一:clearCache
使用:sqlSession.clearCache();可以强制清除缓存
@Test
public void testCache(){
User user1 = this.userMapper.queryUserById(1l);
System.out.println(user1);
this.sqlSession.clearCache();
System.out.println("=================第二次查询======================");
User user2 = this.userMapper.queryUserById(1l);
System.out.println(user2);
}
1.11.1.2 清除一级缓存 方式二:执行update、insert、delete的时候,会清空缓存
@Test
public void testCache(){
User user1 = this.userMapper.queryUserById(1l);
System.out.println(user1);
// this.sqlSession.clearCache();
System.out.println("================更新======================");
User user = new User();
user.setAge(18);
user.setName("柳岩");
user.setPassword("123456");
user.setUserName("yanyan2");
// user.setSex(3);
user.setBirthday(new Date());
user.setId(12l);
this.userMapper.updateUser (user);
System.out.println("=================第二次查询======================");
User user2 = this.userMapper.queryUserById(1l);
System.out.println(user2);
}
由于insert、update、delete会清空缓存,所以第二次查询时,依然会输出sql语句,即从数据库中查询。
1.11.2 二级缓存
mybatis 的二级缓存的作用域:
- 1、同一个mapper的nameSpace,同一个nameSpace中查询sql可以从缓存中命中
- 2、跨sqlSession,不同的sqlSession可以从二级缓存命中
怎么开启二级缓存:
-
1、在映射文件中,添加<cache />标签
-
2、在全局配置文件中,设置cacheEnabled参数,默认已开启。
注意:
- 由于缓存数据是在sqlSession调用close方法时,放入二级缓存的,所以第一个sqlSession必须先关闭
- 二级缓存的对象必须序列化,例如:User对象必须实现Serializable接口。
测试功能
@Test
public void testCache2(){
User user1 = this.userMapper.queryUserById(1l);
System.out.println(user1);
// 注意:关闭sqlSession
sqlSession.close();
System.out.println("=================第二次查询======================");
// 重新打开一个sqlSession会话
SqlSession sqlSession2 = this.sqlSessionFactory.openSession();
// 通过sqlSession2重新实例化UserMapper
this.userMapper = sqlSession2.getMapper(UserMapper.class);
User user2 = this.userMapper.queryUserById(1l);
System.out.println(user2);
}
日志
执行update、insert、delete的时候,会清空缓存
2.Mybatis的高级查询
2.1 一对一查询
基于订单编号查询订单信息,同时获取此订单的下单人
两种实现方式:
- 扩展订单(order类)信息
创建OrderUser继承Order类,同时提供User类的属性
将结果封装成OrderUser对象
- 添加User属性的方式
就是在Order对象中添加user属性。
<?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.itheima.mapper.OrderMapper">
<resultMap type="Order" id="orderUserMap" autoMapping="true">
<id column="id" property="id"/>
<!--
association:一对一的映射
property:属性名
javaType:属性对应的java类型
autoMapping:自动映射
子标签:参照resultMap
-->
<association property="user" javaType="User" autoMapping="true">
<id column="user_id" property="id"/>
</association>
</resultMap>
<!-- resultType不能完成user信息的映射,必须使用resultMap,resultMap的值对应resultMap标签的id,resultMap和resultType必须二选一 -->
<select id="queryOrderWithUser" resultMap="orderUserMap">
SELECT * FROM tb_order a
INNER JOIN tb_user b on a.user_id=b.id
where a.order_number=#{number}
</select>
</mapper>
2.2 一对多查询
订单 : 订单详情 = 1 : n
/**
* 查询订单,查询出下单人信息并且查询出订单详情。
* @param number
* @return
*/
public Order queryOrderWithUserDetail(@Param("number")String number);
<resultMap type="Order" id="orderUserDetailMap" autoMapping="true">
<id column="id" property="id"/>
<association property="user" javaType="User" autoMapping="true">
<id column="user_id" property="id"/>
</association>
<!--
collection:一对多的查询
property:属性名
javaType:集合类型
ofType:集合中的元素类型
autoMapping:开启自动映射
子标签:参照resultMap
-->
<collection property="detailList" javaType="list" ofType="Orderdetail" autoMapping="true">
<id column="detail_id" property="id"/>
</collection>
</resultMap>
<select id="queryOrderWithUserDetail" resultMap="orderUserDetailMap">
select *,c.id as detail_id from tb_order a
LEFT JOIN tb_user b on a.user_id=b.id
LEFT JOIN tb_orderdetail c on a.id=c.order_id
where a.order_number=#{number}
</select>
2.3 多对多查询
订单:订单详情 = 1 : n
订单详情:商品 = 1 : 1
<resultMap type="Order" id="orderUserDetailItemMap" autoMapping="true">
<id column="id" property="id"/>
<association property="user" javaType="User" autoMapping="true">
<id column="user_id" property="id"/>
</association>
<collection property="detailList" javaType="list" ofType="Orderdetail" autoMapping="true">
<id column="detail_id" property="id"></id>
<association property="item" javaType="Item" autoMapping="true">
<id column="item_id" property="id"/>
</association>
</collection>
</resultMap>
<select id="queryOrderWithUserDetailItem" resultMap="orderUserDetailItemMap">
select *,c.id as detail_id from tb_order a
LEFT JOIN tb_user b on a.user_id=b.id
LEFT JOIN tb_orderdetail c on a.id=c.order_id
LEFT JOIN tb_item d on c.item_id=d.id
where a.order_number=#{number}
</select>
2.4 resultMap的继承
2.5 延迟加载
采用之前的配置方式(参考高级查询),肯定是不能做到延迟加载的,因为咱是通过一个查询sql直接查询出所有的数据。为了测试延迟加载的效果,必须改造高级查询的配置,使Order的查询和User或者OrderDetail的查询分开。只有当我们访问Order对象的User或者OrderDetail属性时,才去执行User或者OrderDetail的查询。
2.5.1 改造一对一查询
处理组合键时,需要传递多个参数,可以使用column=”{prop1=col1, prop2=col2, prop3=col3…}”,设置多个列名传入到嵌套查询语句,mybatis会把prop1,prop2,prop3设置到目标嵌套的查询语句中的参数对象中。
子查询中,必须通过prop1,prop2,prop3获取对应的参数值,你也可以使用这种方式指定参数名例如:
2.5.2 开启延迟加载
在mybatis-config.xml中配置行为参数
执行,报错:
说明延迟加载需要cglib的支持。
在pom.xml中,添加cglib的依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>