MyBatis 专题

MyBatis运行原理 (面试官说的)
SpringBoot里MyBatis - SQL执行过程
SpringBoot中整合MyBatis

Mybatis拦截器

Mybatis拦截器
Mybatis拦截器注册方式

常见QA

什么是MyBatis?
MyBatis是一个可以自定义SQL、存储过程和高级映射的数据访问层框架,底层是对 JDBC 的封装.


为什么说Mybatis是半自动ORM映射工具?与全自动的区别在哪里?
Mybatis在查询 关联对象关联集合对象 时,需要手动编写sql来完成,所以称之为半自动ORM映射工具。Hibernate可以根据对象关系模型直接获取,所以它是全自动的。


MyBatis与Hibernate有哪些不同?
1)Mybatis是半自动持久层框架,hibernate是全自动持久层框架
2)Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高。
3)Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。


Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
Mybatis仅支持 association关联对象和collection关联集合对象的延迟加载。

在Mybatis配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled = true | false

它的原理是:
使用CGLIB创建目标对象的代理对象后,当调用目标方法时,进入拦截器方法。
比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。


MyBatis的好处是什么?

  1. MyBatis把sql语句从Java源程序中独立出来,放在单独的XML文件中编写,维护便利。
  2. MyBatis封装了底层JDBC API的调用细节,并能自动将结果集转换成Java Bean对象,大大简化了Java数据库编程的重复工作。
  3. 因为MyBatis需要程序员自己去编写sql语句,程序员可以结合数据库自身的特点灵活控制sql语句,因此能够实现比Hibernate等全自动orm框架更高的查询效率,能够完成复杂查询。

MyBatis环境搭建

1. 导入 jar / Maven工程引入依赖

<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis</artifactId>
	<version>3.2.7</version>
</dependency>

2. 全局配置文件 (mybatis.xml)

  1. 没有名称和地址要求
  2. 在全局配置文件中引入 DTD 或 schema(如果导入 dtd 后没有提示 Window–> preference --> XML --> XMl catalog --> add 按钮)
  • DTD(Document Type Definition),文档类型定义 xml文件的一个语法检查器,帮我们来限制这个文件中写的内容,
  • XSD(XML Schema Definition) 描述 XML 文档的结构,网络应用程序中可以取代 DTD
<?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引用enviroment的id,表示当前所使用的环境 -->
	<environments default="default">
		<!-- 声明可以使用的换环境 -->
		<environment id="default">
			<!-- 1.1.1 JDBC – 这个配置直接简单使用了 JDBC 的提交和回滚设置 -->
			<!-- 1.1.2 MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接。而它会让容器来管理事务的整个生命周期(比如 Spring ) 
								 以后唯一能用到这个东西的时候也就是把mybatis的事物交给spring处理的时候用这个,其他时候基本用不上,都是JDBC。
								 如果用了这个参数的话,会把原生 JDBC 事务赋值false 即 setAutoMapping(false);-->
			<transactionManager type="JDBC"></transactionManager>
			<!-- 1.2.1 POOLED 使用数据库连接池 -->
			<!-- 1.2.2 UNPOOLED 不实用数据库连接池,和直接使用JDBC一样 -->
			<!-- 1.2.3 JNDI :这个数据源的实现是为了使用如 Spring 或应用服务器这类的容器, 容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用. -->
			<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="123456" />
			</dataSource>
		</environment>
	</environments>
	
	<mappers>
		<mapper resource="com/mapper/FlowerMapping.xml" />
	</mappers>
</configuration>

3. 新建XXX.mapper 包 添加 XXXMapper.xml

XXXMapper.xml 里编写需要执行的 SQL 命令,mybatis 底层就是把这个xml文件解析成了一个实现类
在这里插入图片描述


简述Mybatis的Xml映射文件和Mybatis内部数据结构之间的映射关系?
在这里插入图片描述在这里插入图片描述
Mybatis将所有 Xml 配置信息都封装到 All-In-One 重量级对象 Configuration 内部。

在Xml映射文件中,

  1. <parameterMap> 标签会被解析为 ParameterMap 对象,其每个子元素会被解析为ParameterMapping 对象。
  2. <resultMap> 标签会被解析为 ResultMap 对象,其每个子元素会被解析为 ResultMapping 对象。
  3. 一个 <select><insert><update><delete> 标签均会被解析为 MappedStatement 对象,标签内的 sql 会被解析为 BoundSql 对象。

Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?

protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");

namespace+id 是作为mappedStatementskey使用的;有了namespace,就算id重复,namespace+id 依旧不同。如果没有namespace 只剩下idid重复会导致数据互相覆盖。
因为namespace不是必须的,只是最佳实践而已。所以不同的Xml映射文件,如果配置了namespace,那么id可以重复,如果没有配置namespace,那么id不能重复。


4. 使用

<mapper namespace="a.b">

	<select id="selAll" resultType="com.pojo.Flower">
		select * from flower
	</select>

	<select id="selById" resultType="com.pojo.Flower">
		select * from flower where id=1
	</select>

	<select id="selCountById" resultType="int">
		select count(*) from flower
	</select>

	<select id="c" resultType="com.pojo.Flower">
		select * from flower
	</select>
</mapper>
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = factory.openSession();

session三种查询方式:第一种
selectList() 返回值为 List<resultType> 适用于返回结果为多行数据;

List<Flower> selectList = sqlSession.selectList("a.b.selAll");
for (Flower flower : selectList) {
	System.out.println(flower.toString());
}
System.out.println("-------------------------");
session三种查询方式:第二种
selectOne() 返回值 Object,适用于返回结果 只是变量 或 一行数据时 

Flower flower = sqlSession.selectOne("a.b.selById");
System.out.println(flower.toString());
System.out.println("-------------------------");

Integer count = sqlSession.selectOne("a.b.selCountById");
System.out.println(count);
System.out.println("-------------------------");
session三种查询方式:第三种
selectMap() 通过某列的值取到这行数据,数据库中的那个列的值当做 map 的 key;
key重复时候随map特性会覆盖上一条数据;

Map<Object, Object> map = sqlSession.selectMap("a.b.c", "name");
System.out.println(map);

不关闭的话一直和数据库长连接状态
sqlSession.close();

Xml映射文件中,除了常见的select | insert | updae | delete 标签之外,还有哪些标签?
还有很多其他的标签,<resultMap><parameterMap><sql><include><selectKey>
加上动态sql的9个标签,trim|where|set|foreach|if|choose|when|otherwise|bind


mybatis 整合 log4j

  1. pom.xml 文件引入依赖
		<!-- log4j 日志 -->
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>${log4j-version}</version>
		</dependency>
  1. mybatis.xml 中开启 log4j (在 mybatis 全局配置文件中通过<settings>标签控制 mybatis 全局开关)
  2. 在 src 下有配置文件 log4j.properties
    在这里插入图片描述

parameterType 属性

List<People> list = sqlSession.selectList("a.b.c"); //无参数查询
People p = sqlSession.selectOne("a.b.selById", 2);
List<People> peopleList = sqlSession.selectList("a.b.page", map);
Map<Object, Object> map = sqlSession.selectMap("a.b.c", "name");
sqlSession.insert("a.b.insert", person)
sqlSession.update("a.b.update", person);

Mapper.xml 中可以通过 parameterType 控制参数类型 , #{}获取参数内容

1. 参数为基本数据类型:

  1. 使用索引从 0 开始, #{0} 表示第一个参数
  2. 也可以使用#{param1}第一个参数
  3. 如果只有一个参数 (基本数据类型String),mybatis#{} 里面内容没有要求随便写
	<select id="selById" resultType="com.pojo.People" parameterType="int">
			<!-- select * from people where id=#{0} -->
			<!-- select * from people where id=#{param1} -->
			select * from people where id=#{sdadsa} 
	</select>

2. 参数是 map

  1. 写成#{key}
	<select id="page" resultType="People" parameterType="map">
		select *
		from people limit #{pageStart}, #{pageSize}
	</select>

3. 参数是对象:

  1. #{属性名},属性名一定要有get/set方法
	<select id="selById4" resultType="com.pojo.People" parameterType="com.pojo.People">
		select * from people where id=#{id}
	</select>

#{}${} 的区别是什么?

#{}是预编译处理,${}是字符串替换。

  • Mybatis在处理#{}时,会将sql中的#{}替换为?占位符,调用PreparedStatementset方法来赋值;
  • Mybatis在处理${}时,就是把${变量} 替换成变量的值,默认找变量set 方法,如果是数字,传进来的就是这个数字

使用#{}可以有效的防止SQL注入,提高系统安全性。

<select id="selById4" resultType="com.pojo.People" parameterType="com.pojo.People">
	select * from people where id=#{id}
</select>

People p5 = new People();
p5.setId(1);
p5.setName("张三");
People p6 = sqlSession.selectOne("a.b.selById4", p5);
System.out.println(p6);

org.apache.ibatis.logging.jdbc.BaseJdbcLogger==>  Preparing: select * from people where id=? 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger==> Parameters: 1(Integer)
org.apache.ibatis.logging.jdbc.BaseJdbcLogger<==      Total: 1
<select id="selById3" resultType="com.pojo.People" parameterType="com.pojo.People">
	select * from people where id=${id}
</select>

People p3 = new People();
p3.setId(1);
p3.setName("张三");
People p4 = sqlSession.selectOne("a.b.selById3", p3);
System.out.println(p4);

org.apache.ibatis.logging.jdbc.BaseJdbcLogger==>  Preparing: select * from people where id=1 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger==> Parameters: 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger<==      Total: 1
<select id="selById2" resultType="com.pojo.People" parameterType="int">
select * from people where id=${1}
</select>
	
People p = sqlSession.selectOne("a.b.selById2", 2);
System.out.println(p);

org.apache.ibatis.logging.jdbc.BaseJdbcLogger==>  Preparing: select * from people where id=1 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger==> Parameters: 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger<==  Total: 1

Mybatis是如何进行分页的?分页插件的原理是什么?

1. 直接编写sql实现分页

不允许在关键字前后进行数学运算,需要在代码中计算完成 后传递到 mapper.xml

int pageSize = 2;
int pageNum = 2;
Map<String, Object> mapPage = new HashMap<>();
mapPage.put("pageStart", pageSize * (pageNum - 1));
mapPage.put("pageSize", pageSize);

List<People> peopleList = sqlSession.selectList("a.b.page", mapPage);

<!-- 分页查询 -->
<select id="page" resultType="People" parameterType="map">
	select * from people limit #{pageStart}, #{pageSize}
</select></select>

2. 使用RowBounds进行分页

Mybatis的逻辑分页类 RowBounds(int offset, int limit) 比较简单,简单来说就是取出所有满足条件的数据,然后舍弃掉前面offset条数据,然后再取剩下的数据的limit

int pageSize = 2;
int pageNum = 2;
Map<String, Object> map = new HashMap<>();
map.put("pageStart", pageSize * (pageNum - 1));
map.put("pageSize", pageSize);

RowBounds rowBounds = new RowBounds(pageSize * (pageNum - 1), pageSize);
List<People> pageInfoList = session.selectList("a.b.selByPage1", null, rowBounds);

<select id="selByPage1" resultType="People" parameterType="map">
	select * from people 
</select>

3. 使用Mybatis的分页插件

MyBatis插件开发:简单分页插件
分页插件的原理:实现Mybatis提供的接口来自定义插件,在插件的拦截方法内 拦截待执行的sql,然后重写sql
比如:举例:select * from student,拦截sql后重写为:select t.* from (select * from student)t limit 0,10


typeAliases 别名

1. 系统内置别名

部分别名
在这里插入图片描述

2. 自定义别名

<typeAliases>
	<typeAlias type="com.pojo.People" alias="peo"/>
	<package name="com.pojo"/>
</typeAliases>

alias= ”peo”,就可以在mapper.xml 中用 peo 引用 People 类;
直接给某个包下所有类起别名,别名为类名,不区分大小写

原来写法

<select id="selById3" resultType="com.pojo.People" parameterType="com.pojo.People">
	select * from people where id=${id}
</select>

现在写法

<select id="selById3" resultType="peo" parameterType="peo">
	select * from people where id=${id}
</select>

<select id="selById3" resultType="people" parameterType="People">
	select * from people where id=${id}
</select>

非自动提交事务

  1. session.commit() 来提交事务.

  2. mybatis 中默认是关闭了JDBC的自动提交功能,每一个 SqlSession 都默认不自动提交事务.

SqlSession session =factory.openSession();
public class DefaultSqlSessionFactory implements SqlSessionFactory {
...
public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
...
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
	...
	}
...
}
  1. 如何改成事务自动提交?

    1. java.sql.Connection.setAutoCommit(true);

    2. SqlSession session = sqlSessionFactory.openSession(true)

源码如下:

public interface SqlSessionFactory {
...
SqlSession openSession(boolean autoCommit);
...
}
public class DefaultSqlSessionFactory implements SqlSessionFactory {
...
public SqlSession openSession(boolean autoCommit) {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
  }
...
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
	...
	}
...
}

mybatis 底层是对 JDBC 的封装.

  1. JDBC 中 executeUpdate()执行新增,删除,修改的 SQL后 返回值是 int, 表示受影响的行数。所以mybatis< insert> < delete> < update>标签没有 resultType 属性, 只返回 int

  2. sqlSessionFactory.openSession()Mybatis 创建 SqlSession 时,同时创建一个 Transaction(事务对象)并且autoCommit 设为 false
    在这里插入图片描述3. session只要没关,那么session里都是同一个事务;如果出现异常,应该 session.rollback()回滚事务.

MyBatis实现新增的步骤

<insert id="insert" parameterType="People">
	insert into People values(default,#{name},#{age})
	<!--values(default,${name},${age}) --><!-- 会报错 原因还没搞懂-->
</insert>

People p8 = new People();
p8.setName("新增");
p8.setAge(18);
sqlSession.insert("a.b.insert", p8)

sqlSession.commit();
sqlSession.close();

MyBatis 实现修改

<update id="update" parameterType="People">
	update people set age = age +1 where  name =#{name}	
</update>

People p9 = new People();
p9.setName("王五");
p9.setAge(14);
sqlSession.update("a.b.update", p9);

sqlSession.commit();
sqlSession.close();

mybatis 实现删除

<delete id="del" parameterType="int"> 
	delete from people where id = #{0} 
</delete>

int del = session.delete("a.b.del",3); 
if(del>0) { 
	System.out.println("成功");
}else{ 
	System.out.println("失败"); 
}

session.commit();


Mybatis都有哪些Executor执行器?它们之间的区别是什么?

Mybatis有三种基本的Executor执行器,SimpleExecutorReuseExecutorBatchExecutor

1)SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
2)ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map
3)BatchExecutor:完成批处理。


Mybatis中如何指定使用哪一种Executor执行器?

  1. 默认使用ExecutorType.SIMPLE
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
...
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
  1. 也可以openSession时传递ExecutorType类型参数。
  public SqlSession openSession(ExecutorType execType) {
    return openSessionFromDataSource(execType, null, false);
  }
  1. 在Mybatis全局配置文件中,指定默认的ExecutorType执行器类型,
<settings>
  <setting name="defaultExecutorType" value="SIMPLE"/>
</settings>

Mybatis中如何执行批处理?

使用BatchExecutor完成批处理。


Mybatis执行批量插入,能返回数据库主键列表吗?

能,JDBC都能,Mybatis当然也能


什么是MyBatis的接口绑定,有什么好处?
接口绑定就是任意定义接口,然后把接口里面的方法SQL语句绑定,我们直接调用接口方法来执行SQL;
这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置.


接口绑定有几种实现方式?

通过xml里面写SQL来绑定

以前指点mapper的方式:

<mappers>
	<mapper resource="com/mapper/AccountMapper.xml" />
	<mapper resource="com/mapper/LogMapper.xml" />
</mappers>

现在指定mapper的方式:
在 mybatis.xml 中使用标签来进行扫描接口和 mapper.xml
在这里插入图片描述在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名.

  • Mapper.xml文件中的namespacemapper接口类路径相同。
  • mapper.xml文件中的sql的idMapper接口方法名相同。
  • mapper.xml文件中的sql 的parameterType类型Mapper接口方法的输入参数类型相同
  • 如果接口中方法为多个参数,可以省略 parameterType
  • mapper.xml文件中的sql 的resultType类型Mapper接口方法的输出参数类型相同

LogMapper.xml

<!-- 这里路径要和接口完全一致 -->
<mapper namespace="com.mapper.LogMapper">
	<!-- Id要和接口里面的方法完全一致 -->
	<select id="selAll" resultType="LogInfo">
		select * from log
	</select>
</mapper>

LogMapper.java

package com.mapper;
public interface LogMapper {
	List<LogInfo> selAll();
}

以前执行方法方式:

List<LogInfo> list = session.selectList("com.mapper.LogMapper.selLog", map)

接口绑定以后执行方法方式:

LogMapper logMapper = session.getMapper(LogMapper.class);
List<LogInfo> selAll = logMapper.selAll();

注解绑定

作用
为了简化配置文件.

  1. Mybatis 的注解简化mapper.xml 文件,
  2. 但是如果涉及动态SQL依然使用mapper.xml
  3. mapper.xml 和注解可以共存.
  4. 使用注解时mybatis.xml中<mappers>使用 <package/>或者 <mapper class=””/>
	<mappers>
<!-- 		<mapper class="com.mapper.TeacherMapper"/> -->
		<package name="com.mapper"/>
	</mappers>

在这里插入图片描述在接口的方法上面加上@Select @Update等注解里面包含Sql语句来绑定,

  1. 实现查询
  2. 实现新增
  3. 实现修改
  4. 实现删除
public interface TeacherMapper {
	@Insert("insert into teacher values(default,#{name})")
	int insTeacher(Teacher teacher);

	@Update("update teacher set name=#{name} where id =#{id}")
	int updTeacher(Teacher teacher);

	@Delete("delete from teacher where id=#{0}")
	int delTeacher(int id);

	@Select("select * from teacher")
	List<Teacher> selAll();
}
TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);

Teacher t = new Teacher();
t.setId(4);
t.setName("老师4");

int insTeacher = teacherMapper.insTeacher(t);

int insTeacher1 = teacherMapper.updTeacher(t);

List<Teacher> selAll = teacherMapper.selAll();
for (Teacher teacher : selAll) {
	System.out.println(teacher);
}

int insTeacher2 = teacherMapper.delTeacher(3);

什么情况下用注解绑定,什么情况下用xml绑定?
当Sql语句比较简单时候,用注解绑定;
当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多

通常一个Xml映射文件,都会写一个Mapper接口与之对应, Mapper接口的工作原理,是否可以重载?

不能重载,因为通过Mapper接口寻找Xml对应的sql的时候是 包名+类名+方法名 的保存和寻找策略,没有把参数考虑进来,所以不能重载。

接口工作原理
动态代理原理,运行时会为mapper接口生成proxy,代理对象会 拦截 接口方法,去执行对应的sql,返回数据。

后面 mybatisspring 整合时使用的是这个方案


在mapper中如何传递多个参数?

在接口中声明方法

List<LogInfo> selByAccOutAccIn1(String accOut, String accIn);

List<LogInfo> selByAccOutAccIn2(String accOut, String accIn);

List<LogInfo> selByAccOutAccIn3(@Param("accOut") String accOut, @Param("accIn") String accIn);

mapper.xml 中添加对应信息
#{}中使用 0,1,2param1,param2
第三种 mybatis 就把参数转换成map了,其中@Param("accOut")里的 accOut 为key; 后面的 accOut 为内容;

	<!-- 多参数是不需要写parameterType -->
	<select id="selByAccOutAccIn1" resultType="LogInfo">
		select* from log where accout=#{0} and accin=#{1}
	</select>
	
	<select id="selByAccOutAccIn2" resultType="LogInfo">
		select* from log where accout=#{param1} and accin=#{param2}
	</select>
	
	<select id="selByAccOutAccIn3" resultType="LogInfo">
		select* from log where accout=#{accOut} and accin=#{accIn}
	</select>

使用

List<LogInfo> selAll1 = logMapper.selByAccOutAccIn1("1", "2");
List<LogInfo> selAll2 = logMapper.selByAccOutAccIn2("1", "2");
List<LogInfo> selAll3 = logMapper.selByAccOutAccIn3("1", "2");

动态 SQL

定义
Mybatis动态sql是在mapper.xml 中添加逻辑判断等,根据不同的条件需要执行不同的SQL 命令.称为动态SQL。

OGNL表达式,直接由key 或者对象的属性,不需要添加任何特殊字符号


能简述一下动态sql的执行原理不?
mybatis中动态sql执行原理


都有哪些动态sql?

< if>

package com.mapper;
public interface LogMapper {
	List<LogInfo> selByAccOutAccIn1(@Param("accOut") String accOut, @Param("accIn") String accIn);
}
<mapper namespace="com.mapper.LogMapper">

	<select id="selByAccOutAccIn1" resultType="LogInfo">
		select * from log where 1=1
		<if test="accOut!=null and accOut !=''">
			and accOut=#{accOut}
		</if>
		<if test="accIn!=null and accIn !=''">
			and accIn=#{accIn}
		</if>
	</select>
</mapper>
List<LogInfo> selByAccOutAccIn1 = logMapper.selByAccOutAccIn1("1", "");

< where>

  • 当编写<where>标签时,如果内容中第一个是and则去掉第一个and
  • 如果<where>中有内容会生成where关键字,如果没有内容不生成where关键
  • <where>比直接使用<if>少写where 1=1
List<LogInfo> selByAccOutAccIn2(@Param("accOut") String accOut, @Param("accIn") String accIn);
	<select id="selByAccOutAccIn2" resultType="LogInfo">
		select * from log
		<where>
			<if test="accOut!=null and accOut !=''">
				and accOut=#{accOut}
			</if>
			<if test="accIn!=null and accIn !=''">
				and accIn=#{accIn}
			</if>
		</where>
	</select>
package com.mapper;
public interface LogMapper {
	List<LogInfo> selByAccOutAccIn2 = logMapper.selByAccOutAccIn2("1", "");
}

< choose> < when> < otherwise>

只有有一个成立,其他都不执行.
即使accinaccout 都成立,生成的 sql 也只有 where accin=?

package com.mapper;
public interface LogMapper {
	List<LogInfo> selByAccOutAccIn3(@Param("accOut") String accOut, @Param("accIn") String accIn);
}
	<!-- 只有有一个成立,其他都不执行 -->
	<select id="selByAccOutAccIn3" resultType="LogInfo">
		select * from log
		<where>                                           
			<choose>
				<when test="accIn!=null and accIn !=''">and accIn=#{accIn}</when>
				<when test="accOut!=null and accOut !=''">and accOut=#{accOut}</when>
			</choose>
		</where>
	</select>
List<LogInfo> selByAccOutAccIn3 = logMapper.selByAccOutAccIn3("1", "");

< set>

作用

  • 去掉最后一个逗号
  • 如果<set>里面有内容生成 set 关键字,没有就不生成
  • id=#{id} 目的防止<set>中没有内容,mybatis 不生成 set 关键字,如果修改中没有 set 从句 SQL 语法错误。
package com.mapper;
public interface LogMapper {
	int updLogInfo(LogInfo logInfo);
}
	<update id="updLogInfo" parameterType="LogInfo">
		update log
		<set>
			<!-- 写id=#{id}是防止不生成set语法错误 -->
			id=#{id},
			<if test="accOut!=null and accOut !=''">
				accOut=#{accOut},
			</if>
			<if test="accIn!=null and accIn !=''">
				accIn=#{accIn},
			</if>
		</set>
		where id=#{id}
	</update>
LogInfo LogInfo = new LogInfo();
LogInfo.setId(3);
LogInfo.setAccIn("8");
LogInfo.setAccOut("9");
LogInfo.setMoney(522);
logMapper.updLogInfo(LogInfo);

session.commit();
session.close();

< trim>

  • prefix 在前面添加内容
  • prefixOverrides 去掉前面内容
  • suffix 在后面添加内容
  • suffixOverrieds 去掉后面内容
  • 执行顺序去掉内容后添加内容
package com.mapper;
public interface LogMapper {
	List<LogInfo> selByAccOutAccIn4(@Param("accOut") String accOut, @Param("accIn") String accIn);
	
	int updLogInfo2(LogInfo logInfo);
}
	<select id="selByAccOutAccIn4" resultType="LogInfo">
		select * from log
		<trim prefix="where" prefixOverrides="and">
			and accOut=#{accOut}
		</trim>
	</select>

	<update id="updLogInfo2" parameterType="LogInfo">
		update log
		<!-- 多用于处理钱 ¥100 之类的操作 ,不过中间会自动加一个空格,要想没有空格参考下面的方法 -->
		<trim prefix="set" suffixOverrides=",">
			accOut=#{accOut},accIn=#{accIn},
		</trim>
		where id=#{id}
	</update>
List<LogInfo> selByAccOutAccIn4 = logMapper.selByAccOutAccIn4("1", "");

< bind>

作用
给参数重新赋值
场景:

  • 模糊查询
  • 在原内容前或后添加内容
package com.mapper;
public interface LogMapper {
	List<LogInfo> selByAccOutAccIn5(@Param("accOut") String accOut, @Param("accIn") String accIn);
}
	<select id="selByAccOutAccIn5" resultType="LogInfo">
		<!-- 传过来值的基础上重新设置一下值,主要用于模糊查询 -->
		<bind name="accOut" value="'%'+accOut+'%'" />
		select * from log
		<trim prefix="where" prefixOverrides="and">
			and accOut like #{accOut}
		</trim>
	</select>
List<LogInfo> selByAccOutAccIn5 = logMapper.selByAccOutAccIn5("1", "");

模糊查询like语句该怎么写?
1)在java中拼接通配符,通过#{}赋值
2)在Sql语句中拼接通配符 (不安全 会引起Sql注入)

< foreach>

循环参数内容,还具备在内容的前后添加内容,还具备添加分隔符功能.。
适用场景:

in 查询中.批量新增中(mybatis 中 foreach 效率比较 低)

  • 如果希望批量新增,SQL 命令
package com.mapper;
public interface LogMapper {
	List<LogInfo> selIn(List<Integer> list);
}
	<select id="selIn" parameterType="list" resultType="LogInfo">
		select * from log where id in
		<!-- foreach多用于in查询 -->
		<foreach collection="list" item="abc" open="(" close=")" separator=",">
			#{abc}
		</foreach>
	</select>
List<Integer> intList = new ArrayList();
intList.add(1);
intList.add(2);
intList.add(3);
List<LogInfo> selByAccOutAccIn6 = logMapper.selIn(intList);

- collectino=”” 要遍历的集合
item 迭代变量, #{迭代变量名}获取内容
open 循环后左侧添加的内容
close 循环后右侧添加的内容
separator 每次循环时,元素之间的分隔符

package com.mapper;
public interface LogMapper {
	int ins(List<Integer> list);
}
	<insert id="ins" parameterType="list">
		insert into log values
		<trim suffixOverrides=",">
			<foreach collection="list" item="abc">
				(default,#{abc},2,666),
			</foreach>
		</trim>
	</insert>
List<Integer> intList = new ArrayList();
intList.add(1);
intList.add(2);
intList.add(3);
List<LogInfo> selByAccOutAccIn6 = logMapper.selIn(intList);

< sql> 和< include>

代码复用

package com.mapper;
public interface LogMapper {
	List<LogInfo> selByAccOutAccIn6(@Param("accOut") String accOut, @Param("accIn") String accIn);
}

某些 SQL 片段如果希望复用,可以使用<sql>定义这个片段

	<sql id="mysql">
		id,accout,accin,money
	</sql>

<select><delete><update><insert>中使用<include> 引用

	<select id="selByAccOutAccIn6" resultType="LogInfo">
		select
		<include refid="mysql"></include>
		from log where accOut=#{accOut}
	</select>
List<LogInfo> selByAccOutAccIn7 = logMapper.selByAccOutAccIn6("1", "");

Mybatis映射文件中,如果A标签通过include引用了B标签的内容,请问,B标签能否定义在A标签的后面,还是说必须定义在A标签的前面?
虽然Mybatis解析Xml映射文件是按照顺序解析的,但是,被引用的B标签依然可以定义在任何地方,Mybatis都可以正确识别。
原理是,Mybatis解析A标签,发现A标签引用了B标签,但是B标签尚未解析到,尚不存在,此时,Mybatis会将A标签标记为未解析状态,然后继续解析余下的标签,包含B标签,待所有标签解析完毕,Mybatis会重新解析那些被标记为未解析的标签,此时再解析A标签时,B标签已经存在,A标签也就可以正常解析完成了。


MyBatis 缓存

应用程序数据库交互的过程是一个相对比较耗时的过程

缓存存在的意义:
让应用程序减少对数据库的访问,提升程序运行效率

1. SqlSession 缓存

MyBatis 中默认 SqlSession 缓存开启

  1. 同一个 SqlSession 对象调用同一个<select>时,只有第一次访问数据库,第一次之后把查询结果缓存到 SqlSession 缓存区(内存)中。
  2. 有效范围必须是同一个 SqlSession 对象
  3. 缓存的是 statement 对象.
  4. myabtis 时一个<select>对应一个 statement 对象

序列化:把内存中的临时数(瞬时数据)持久化到硬盘上
反序列化:把硬盘上的永久数据读取到内存中成为临时数据的过程

同一个session不同方法,缓存不可用

SqlSession session = factory.openSession();
List<LogInfo> selectList = session.selectList("com.mapper.LogMapper.selAll");
List<LogInfo> selectList1 = session.selectList("com.mapper.LogMapper.selAll1");
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:05:22,138  ==>  Preparing: select * from log 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:05:22,225  ==> Parameters: 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:05:22,306  <==      Total: 43
- org.apache.ibatis.cache.decorators.LoggingCache 2021-08-26 19:05:22,309  Cache Hit Ratio [com.mapper.LogMapper]: 0.0
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:05:22,310  ==>  Preparing: select * from log 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:05:22,310  ==> Parameters: 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:05:22,324  <==      Total: 43
- org.apache.ibatis.cache.decorators.LoggingCache 2021-08-26 19:05:22,325  Cache Hit Ratio [com.mapper.LogMapper]: 0.0

不同session同一个方法,缓存不可用

SqlSession session = factory.openSession();
List<LogInfo> selectList1 = session.selectList("com.mapper.LogMapper.selAll1");
SqlSession session1 = factory.openSession();
List<LogInfo> selectList11 = session1.selectList("com.mapper.LogMapper.selAll1");
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:05:22,310  ==>  Preparing: select * from log 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:05:22,310  ==> Parameters: 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:05:22,324  <==      Total: 43
- org.apache.ibatis.cache.decorators.LoggingCache 2021-08-26 19:05:22,325  Cache Hit Ratio [com.mapper.LogMapper]: 0.0
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:05:22,335  ==>  Preparing: select * from log 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:05:22,336  ==> Parameters: 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:05:22,356  <==      Total: 43

缓存流程

  • 步骤一: 先去缓存区中找是否存在 statement
  • 步骤二:返回结果
  • 步骤三:如果没有缓存 statement 对象,去数据库获取数据
  • 步骤四:数据库返回查询结果
  • 步骤五:把查询结果放到对应的缓存区中
    在这里插入图片描述
List<LogInfo> selectList2 = session.selectList("com.mapper.LogMapper.selAll2");//两次查询只有一个sql
List<LogInfo> selectList3 = session.selectList("com.mapper.LogMapper.selAll2");//两次查询只有一个sql

同一个session同一个方法用上了缓存

- org.apache.ibatis.cache.decorators.LoggingCache 2021-08-26 19:05:22,356  Cache Hit Ratio [com.mapper.LogMapper]: 0.0
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:05:22,357  ==>  Preparing: select * from log 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:05:22,357  ==> Parameters: 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:05:22,369  <==      Total: 43
- org.apache.ibatis.cache.decorators.LoggingCache 2021-08-26 19:05:22,371  Cache Hit Ratio [com.mapper.LogMapper]: 0.0

2. SqlSessionFactory 缓存 又叫:二级缓存

有效范围:
同一个 factory 内哪个 SqlSession 都可以获取

什么时候使用二级缓存
当数据频繁被使用,很少被修改

使用二级缓存步骤

  1. mapper.xml 中添加<cache readOnly="true"></cache>
    如果不加readOnly="true 就要把所有的pojo类都要实现序列化接口

  2. SqlSession.close()时或 commit()时会把 SqlSession 缓存的数据刷(flush)到 SqlSessionFactory 缓存区中

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);

SqlSession session2 = factory.openSession();
List<LogInfo> session2selectList2 = session2.selectList("com.mapper.LogMapper.selAll1");

session2.close();// 注意这里不关闭 以及缓存不会往二级缓存里面存

SqlSession session3 = factory.openSession();
List<LogInfo> session3selectList3 = session3.selectList("com.mapper.LogMapper.selAll1");

SqlSession session4 = factory.openSession();
List<LogInfo> session4selectList4 = session4.selectList("com.mapper.LogMapper.selAll2");
- org.apache.ibatis.cache.decorators.LoggingCache 2021-08-26 19:38:21,251  Cache Hit Ratio [com.mapper.LogMapper]: 0.0
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:38:21,786  ==>  Preparing: select * from log 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:38:21,858  ==> Parameters: 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:38:21,941  <==      Total: 43
~~~~~~~~~~~~~~~~~~~~~~~~~~~
- org.apache.ibatis.cache.decorators.LoggingCache 2021-08-26 19:38:21,946  Cache Hit Ratio [com.mapper.LogMapper]: 0.5
~~~~~~~~~~~~~~~~~~~~~~~~~~~
- org.apache.ibatis.cache.decorators.LoggingCache 2021-08-26 19:38:21,948  Cache Hit Ratio [com.mapper.LogMapper]: 0.3333333333333333
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:38:21,949  ==>  Preparing: select * from log 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:38:21,950  ==> Parameters: 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 19:38:21,966  <==      Total: 43

以上执行结果,同一个factory里,同方法哪怕不是同session也会查缓存,不同方法则不会查缓存

讲下MyBatis的缓存
MyBatis的缓存分为一级缓存和二级缓存

一级缓存
缓存存储位置
一级缓存放在SqlSession 里面。
缓存的内容
缓存的是statement对象。
缓存可见范围
同一个SqlSession 对象调用同一个<select >
缓存数据的来源
第一次访问数据库,把第一次之后把查询结果缓存到SqlSession缓存区(内存)中。
缓存启用的配置条件
默认就有。

二级缓存
缓存存储位置
二级缓存放在它的命名空间里,默认是不打开的。
缓存的内容
缓存的是statement对象。
缓存访问范围
同一个factory 内不同SqlSession同一个<select>
缓存数据的来源
SqlSession 对象close()时或commit()时会把SqlSession缓存的数据刷(flush)到SqlSessionFactory缓存区中。
缓存启用的配置条件
默认是不打开的,使用二级缓存需要满足一下条件

  • pojo需要实现Serializable序列化接口(可用来保存对象的状态)
  • 或者在mapper.xml中添加<cache readOnly="true">< /cache>

缓存流程
参照上面


MyBatis 实现多表查询

实现多表查询方式

  1. 业务装配:对两个表编写单表查询语句,在业务(Service)把查询 的两个结果进行关联.
  2. 使用 Auto Mapping 特性,在实现两表联合查询时通过别名完成射。
  3. 使用 <resultMap>标签进行实现.

多表查询时,类中包含另一个类的对象的分类?

  • 单个对象
  • 集合对象.

Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
当实体类中的属性名和表中的字段名不一样,如果将查询的结果封装到指定pojo?

  1. 使用<resultType>标签时,就是使用 Auto Mapping 特性;属性名和表中的字段名不一样时,sql语句中定义字段名的别名;MyBatis默认使用 Auto Mapping`特性.。

  2. 使用<resultMap>标签来映射字段名和实体类属性名的一 一对应的关系。

  3. 有了列名属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值返回,那些找不到映射关系的属性,是无法完成赋值的。

<resultType>标签

Auto Mapping 自动映射
通过同名方式,实体类属性名和数据库字段名要一样,如果不一样需要用 sql字段别名控制。

  • 3.2版本以前找的是set方法
  • 3.2版本以后直接找的是属性,没set方法也能找到

<resultMap>标签

由程序员逐一定义列名对象属性名之间的映射关系,控制SQL查询结果实体类的映射关系.。


使用 resultMap 实现单表映射关系

mybatis里配置

	<mappers>
		<mapper resource="com/mapper/TeacherMapper.xml" />
		<mapper resource="com/mapper/StudentMapper.xml" />
	</mappers>
<mapper namespace="com.mapper.TeacherMapper">
	<!-- teacher不是表名而是实体类名字 -->
	<resultMap id="mymap" type="com.pojo.Teacher" >
		<!-- 主键使用id标签配置映射关系 -->
		<id column="id" property="id1" />
		<!-- 其他列使用result标签来配置映射关系;如果不配置对应关系就找不到值,就在数据库中查不到然后赋值null -->
		<result column="name" property="name1" />
	</resultMap>
	
	<!--使用resultMap 实现单表映射关系 -->
	<select id="selAll" resultMap="mymap">
		select * from teacher
	</select>
</mapper>
SqlSession sqlSession = sqlSessionFactory.openSession();

List<Teacher> list = sqlSession.selectList("com.mapper.TeacherMapper.selAll");
for (Teacher teacher : list) {
	System.out.println(teacher);
}

sqlSession.close();
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 20:59:40,923  ==>  Preparing: select * from teacher 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 20:59:41,027  ==> Parameters: 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 20:59:41,082  <==      Total: 7
Teacher [id1=1, name1=老师1, studentList=null]
Teacher [id1=2, name1=老师2, studentList=null]
Teacher [id1=4, name1=老师4, studentList=null]
Teacher [id1=5, name1=老师4, studentList=null]
Teacher [id1=6, name1=老师4, studentList=null]
Teacher [id1=7, name1=老师4, studentList=null]
Teacher [id1=8, name1=老师4, studentList=null]

使用 resultMap 实现关联单个对象(N+1)

N+1 查询方式,先查询出某个表的全部信息,根据这个表的信息查询另一个表的信息

与业务装配的区别:
service 里面写的代码现在由 mybatis 完成装配。

实现步骤

  1. 在 Student 实现类中包含了一个 Teacher 对象
public class Student {
	private int id;
	private String name;
	private int age;
	private int tid;
	private Teacher teacher;
	}
public class Teacher {
	private int id;
	private String name;
	private List<Student> studentList;
	}

2.在 TeacherMapper.xml 中提供一个查询

<mapper namespace="com.mapper.TeacherMapper">
	<select id="selById" parameterType="int" resultType="com.pojo.Teacher"  >
		select * from teacher where id =#{0}
	</select>
</mapper>

3.在 StudentMapper.xml

<mapper namespace="com.mapper.StudentMapper">
	<!-- 使用<resultMap>实现关联单个对象 (多表N+1次查询方式)   -->
	<resultMap  id="stumap" type="Student"><!-- Student是实体类 -->
		<id column="id" property="id" />
		<result column="name" property="name" />
		<result column="age" property="age" />
		<result column="tid" property="tid" />
		<!-- 如果关联一个对象就是用这个标签 -->
		<association property="teacher" select="com.mapper.TeacherMapper.selById" column="tid"></association>
	</resultMap>
	<select id="selAll" resultMap="stumap">
		select * from student
	</select>
</mapper>

<association> 装配一个对象时使用
property: 指定类中的属性名
select:通过哪个查询查询出这个对象的信息
column: 把当前表的哪个列的值做为参数传递给另一个查询

大前提:使用N+1 方式时,如果列名和属性名相同可以不配置,使用Auto mapping 特性.
但是mybatis 默认只会给列专配一次。
所以以上代码可以简化

	<resultMap  id="stumap1" type="Student">
		<result column="tid" property="tid" />
		<association property="teacher" select="com.mapper.TeacherMapper.selById" column="tid"></association>
	</resultMap>
	<select id="selAll" resultMap="stumap1">
		select * from student
	</select>

使用

		SqlSession sqlSession = sqlSessionFactory.openSession();
		List<Student> list1 = sqlSession.selectList("com.mapper.StudentMapper.selAll");
		for (Student student : list1) {
			System.out.println(student);
		}
		sqlSession.close();

结果

- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-09-15 11:35:04,573  ==>  Preparing: select * from student 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-09-15 11:35:04,645  ==> Parameters: 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-09-15 11:35:04,701  ====>  Preparing: select * from teacher where id =? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-09-15 11:35:04,703  ====> Parameters: 1(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-09-15 11:35:04,716  <====      Total: 1
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-09-15 11:35:04,723  ====>  Preparing: select * from teacher where id =? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-09-15 11:35:04,724  ====> Parameters: 2(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-09-15 11:35:04,726  <====      Total: 1
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-09-15 11:35:04,727  <==      Total: 11
Student [id=1, name=学生1, age=1, tid=1, teacher=Teacher2 [id=1, name=老师1, studentList=null]]
Student [id=2, name=学生2, age=2, tid=1, teacher=Teacher2 [id=1, name=老师1, studentList=null]]
Student [id=3, name=学生3, age=3, tid=1, teacher=Teacher2 [id=1, name=老师1, studentList=null]]
Student [id=4, name=学生4, age=4, tid=1, teacher=Teacher2 [id=1, name=老师1, studentList=null]]
Student [id=5, name=学生5, age=5, tid=1, teacher=Teacher2 [id=1, name=老师1, studentList=null]]
Student [id=6, name=学生6, age=6, tid=1, teacher=Teacher2 [id=1, name=老师1, studentList=null]]
Student [id=7, name=学生7, age=7, tid=2, teacher=Teacher2 [id=2, name=老师2, studentList=null]]
Student [id=8, name=学生8, age=8, tid=2, teacher=Teacher2 [id=2, name=老师2, studentList=null]]
Student [id=9, name=学生9, age=9, tid=2, teacher=Teacher2 [id=2, name=老师2, studentList=null]]
Student [id=10, name=学生10, age=10, tid=2, teacher=Teacher2 [id=2, name=老师2, studentList=null]]
Student [id=11, name=学生11, age=11, tid=2, teacher=Teacher2 [id=2, name=老师2, studentList=null]]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

使用 resultMap 实现关联单个对象(联合查询)

<mapper namespace="com.mapper.StudentMapper">
	<!-- 使用resultMap 实现关联单个对象(联合查询方式) -->
	<resultMap type="Student" id="stuMap1">
		<id column="sid" property="id" />
		<result column="sname" property="name" />
		<result column="age" property="age" />
		<result column="tid" property="tid" />
		<association property="teacher" javaType="Teacher">
			<id column="tid" property="id1" />
			<result column="tname" property="name1" />
		</association>
	</resultMap>
	<select id="selAll2" resultMap="stuMap1">
		select s.id sid,s.name sname,age age,t.id tid,t.name tname 
		FROM student s 
		left outer join teacher t 
		on s.tid=t.id
	</select>
</mapper>

使用

SqlSession sqlSession = sqlSessionFactory.openSession();

List<Student> list2 = sqlSession.selectList("com.mapper.StudentMapper.selAll2");
for (Student student : list2) {
	System.out.println(student);
}
sqlSession.close();

结果

- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:24:39,305  ==>  Preparing: select s.id sid,s.name sname,age age,t.id tid,t.name tname FROM student s left outer join teacher t on s.tid=t.id 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:24:39,411  ==> Parameters: 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:24:39,465  <==      Total: 11
Student [id=1, name=学生1, age=1, tid=1, teacher=Teacher [id1=1, name1=老师1, studentList=null]]
Student [id=2, name=学生2, age=2, tid=1, teacher=Teacher [id1=1, name1=老师1, studentList=null]]
Student [id=3, name=学生3, age=3, tid=1, teacher=Teacher [id1=1, name1=老师1, studentList=null]]
Student [id=4, name=学生4, age=4, tid=1, teacher=Teacher [id1=1, name1=老师1, studentList=null]]
Student [id=5, name=学生5, age=5, tid=1, teacher=Teacher [id1=1, name1=老师1, studentList=null]]
Student [id=6, name=学生6, age=6, tid=1, teacher=Teacher [id1=1, name1=老师1, studentList=null]]
Student [id=7, name=学生7, age=7, tid=2, teacher=Teacher [id1=2, name1=老师2, studentList=null]]
Student [id=8, name=学生8, age=8, tid=2, teacher=Teacher [id1=2, name1=老师2, studentList=null]]
Student [id=9, name=学生9, age=9, tid=2, teacher=Teacher [id1=2, name1=老师2, studentList=null]]
Student [id=10, name=学生10, age=10, tid=2, teacher=Teacher [id1=2, name1=老师2, studentList=null]]
Student [id=11, name=学生11, age=11, tid=2, teacher=Teacher [id1=2, name1=老师2, studentList=null]]

N+1 方式和联合查询方式对比
N+1:需求不确定时.
联合查询:需求中确定查询时两个表一定都查询.

N+1 名称由来
举例:学生中有3 条数据
需求:查询所有学生以及授课老师信息
需要执行的SQL命令查询全部学生信息:select * from 学生,执行3 遍select * from 老师where id=学生的外键
使用多条SQl 命令查询两表数据时,如果希望把需要的数据都查询出来,需要执行N+1 条SQl 才能把所有数据库查询出来.

缺点:
效率低

优点:
如果有的时候只需要查询学生不查询老师.只需要执行一个select * from student;

适用场景:
有的时候需要查询学生同时查询老师,有的时候只需要查询学生.

如果解决N+1查询带来的效率低的问题
默认带的前提: 每次都是两个都查询。使用两表联合查询.


使用resultMap 实现关联集合对象(N+1)

  1. Teacher 中添加 List<Student>
public class Teacher {
	private int id1;
	private String name1;
	private List<Student> studentList;
	}

2.在 StudentMapper.xml 中添加通过 tid 查询

	<select id="selById" parameterType="int" resultType="student"><!-- resultMap="stumap" 会内存溢出 -->
		select * from student where tid =#{0}
	</select>

3.在 TeacherMapper.xml 中添加查询全部

	<!-- 使用<resultMap>查询关联集合对象(表N+1次查询方式) -->
	<resultMap type="teacher" id="mymap1">
		<id column="id" property="id1" />
		<result column="name" property="name1" />
		<!-- 如果关联一个集合对象就是用这个标签 -->
		<collection property="studentList" select="com.mapper.StudentMapper.selById" column="id"></collection>
	</resultMap>
	<select id="selAll1" resultMap="mymap1">
		select * from teacher
	</select>

使用

SqlSession sqlSession = sqlSessionFactory.openSession();

/* 使用<resultMap>查询关联 集合对象(表N+1次查询方式) */
List<Teacher> list3 = sqlSession.selectList("com.mapper.TeacherMapper.selAll1");
for (Teacher teacher : list3) {
		System.out.println(teacher);
}
sqlSession.close();

结果

- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,592  ==>  Preparing: select * from teacher 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,667  ==> Parameters: 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,779  ====>  Preparing: select * from student where tid =? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,782  ====> Parameters: 1(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,800  <====      Total: 6
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,801  ====>  Preparing: select * from student where tid =? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,802  ====> Parameters: 2(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,808  <====      Total: 5
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,810  ====>  Preparing: select * from student where tid =? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,811  ====> Parameters: 4(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,814  <====      Total: 0
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,816  ====>  Preparing: select * from student where tid =? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,818  ====> Parameters: 5(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,820  <====      Total: 0
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,823  ====>  Preparing: select * from student where tid =? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,824  ====> Parameters: 6(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,828  <====      Total: 0
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,829  ====>  Preparing: select * from student where tid =? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,830  ====> Parameters: 7(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,834  <====      Total: 0
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,835  ====>  Preparing: select * from student where tid =? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,837  ====> Parameters: 8(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,839  <====      Total: 0
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:45:08,841  <==      Total: 7
Teacher [id1=1, name1=老师1, studentList=[Student [id=1, name=学生1, age=1, tid=1, teacher=null], Student [id=2, name=学生2, age=2, tid=1, teacher=null], Student [id=3, name=学生3, age=3, tid=1, teacher=null], Student [id=4, name=学生4, age=4, tid=1, teacher=null], Student [id=5, name=学生5, age=5, tid=1, teacher=null], Student [id=6, name=学生6, age=6, tid=1, teacher=null]]]
Teacher [id1=2, name1=老师2, studentList=[Student [id=7, name=学生7, age=7, tid=2, teacher=null], Student [id=8, name=学生8, age=8, tid=2, teacher=null], Student [id=9, name=学生9, age=9, tid=2, teacher=null], Student [id=10, name=学生10, age=10, tid=2, teacher=null], Student [id=11, name=学生11, age=11, tid=2, teacher=null]]]
Teacher [id1=4, name1=老师4, studentList=[]]
Teacher [id1=5, name1=老师4, studentList=[]]
Teacher [id1=6, name1=老师4, studentList=[]]
Teacher [id1=7, name1=老师4, studentList=[]]
Teacher [id1=8, name1=老师4, studentList=[]]

使用resultMap 实现关联集合对象(联合查询)

  1. teacherMapper.xml 中添加
	<!-- 使用<resultMap>实现加载集合数据(联合查询) -->
	<resultMap id="mymap2" type="teacher" >
		<id column="tid" property="id1" />
		<result column="tname" property="name1" />
		<collection property="studentList" ofType="Student">
			<id column="sid" property="id" />
			<result column="sname" property="name" />
			<result column="age" property="age" />
			<result column="tid" property="tid" />
		</collection>
	</resultMap>
	<select id="selAll2" resultMap="mymap2">
		select t.id tid,t.name tname,s.id sid,s.name sname, age,tid 
		from teacher t 
		left join student s 
		on t.id=s.tid;
	</select>
  • mybatis 可以通过主键判断对象是否被加载过,不需要担心创建重复 Teacher

使用

SqlSession sqlSession = sqlSessionFactory.openSession();

/* 使用<resultMap>实现加载集合数据(联合查询) */
List<Teacher> list4 = sqlSession.selectList("com.mapper.TeacherMapper.selAll2");
for (Teacher teacher : list4) {
	System.out.println(teacher);
}
sqlSession.close();

结果

- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:49:59,221  ==>  Preparing: select t.id tid,t.name tname,s.id sid,s.name sname, age,tid from teacher t left join student s on t.id=s.tid; 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:49:59,363  ==> Parameters: 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:49:59,489  <==      Total: 16
Teacher [id1=1, name1=老师1, studentList=[Student [id=1, name=学生1, age=1, tid=1, teacher=null], Student [id=2, name=学生2, age=2, tid=1, teacher=null], Student [id=3, name=学生3, age=3, tid=1, teacher=null], Student [id=4, name=学生4, age=4, tid=1, teacher=null], Student [id=5, name=学生5, age=5, tid=1, teacher=null], Student [id=6, name=学生6, age=6, tid=1, teacher=null]]]
Teacher [id1=2, name1=老师2, studentList=[Student [id=7, name=学生7, age=7, tid=2, teacher=null], Student [id=8, name=学生8, age=8, tid=2, teacher=null], Student [id=9, name=学生9, age=9, tid=2, teacher=null], Student [id=10, name=学生10, age=10, tid=2, teacher=null], Student [id=11, name=学生11, age=11, tid=2, teacher=null]]]
Teacher [id1=4, name1=老师4, studentList=[Student [id=0, name=null, age=0, tid=4, teacher=null]]]
Teacher [id1=5, name1=老师4, studentList=[Student [id=0, name=null, age=0, tid=5, teacher=null]]]
Teacher [id1=6, name1=老师4, studentList=[Student [id=0, name=null, age=0, tid=6, teacher=null]]]
Teacher [id1=7, name1=老师4, studentList=[Student [id=0, name=null, age=0, tid=7, teacher=null]]]
Teacher [id1=8, name1=老师4, studentList=[Student [id=0, name=null, age=0, tid=8, teacher=null]]]

使用Auto Mapping 结合别名实现多表查询.

只能使用多表联合查询方式.

实现方式
在SQL是关键字符,两侧添加反单引号

<select id="selAll1" resultType="student">
	select t.id `teacher.id1`,t.name`teacher.name1`,s.id id,s.name name,age,tid
	from student s 
	left join teacher t 
	on t.id=s.tid;
</select>

使用

SqlSession sqlSession = sqlSessionFactory.openSession();

/* 使用auto mapping 结合别名实现多表查询(只能使用多表联合查询) 要求查询的列名和表字段名一样 只能单个对象,集合对象不行 */
List<Student> list5 = sqlSession.selectList("com.mapper.StudentMapper.selAll1");
for (Student student : list5) {
	System.out.println(student);
}

sqlSession.close();

结果

- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:54:52,100  ==>  Preparing: select t.id `teacher.id1`,t.name`teacher.name1`,s.id id,s.name name,age,tid from student s left join teacher t on t.id=s.tid; 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:54:52,200  ==> Parameters: 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 22:54:52,297  <==      Total: 11
Student [id=1, name=学生1, age=1, tid=1, teacher=Teacher [id1=1, name1=老师1, studentList=null]]
Student [id=2, name=学生2, age=2, tid=1, teacher=Teacher [id1=1, name1=老师1, studentList=null]]
Student [id=3, name=学生3, age=3, tid=1, teacher=Teacher [id1=1, name1=老师1, studentList=null]]
Student [id=4, name=学生4, age=4, tid=1, teacher=Teacher [id1=1, name1=老师1, studentList=null]]
Student [id=5, name=学生5, age=5, tid=1, teacher=Teacher [id1=1, name1=老师1, studentList=null]]
Student [id=6, name=学生6, age=6, tid=1, teacher=Teacher [id1=1, name1=老师1, studentList=null]]
Student [id=7, name=学生7, age=7, tid=2, teacher=Teacher [id1=2, name1=老师2, studentList=null]]
Student [id=8, name=学生8, age=8, tid=2, teacher=Teacher [id1=2, name1=老师2, studentList=null]]
Student [id=9, name=学生9, age=9, tid=2, teacher=Teacher [id1=2, name1=老师2, studentList=null]]
Student [id=10, name=学生10, age=10, tid=2, teacher=Teacher [id1=2, name1=老师2, studentList=null]]
Student [id=11, name=学生11, age=11, tid=2, teacher=Teacher [id1=2, name1=老师2, studentList=null]]

使用注解 关联单个对象(N+1方式)

@Results(value= {
		@Result(id=true,column="id",property="id"),
		@Result(property="name",column="name"),
		@Result(property="age",column="age"),
		@Result(property="tid",column="tid"),
		@Result(property="teacher",column="tid",one=@One(select="com.mapper.TeacherMapper.selById"))
	})
	@Select("select * from student")
	List<Student> selAll1();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
List<Student> selAll2 = studentMapper.selAll1();
for (Student student : selAll2) {
	System.out.println(student);
}
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:14:15,957  ==>  Preparing: select * from student 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:14:15,957  ==> Parameters: 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:14:15,961  ====>  Preparing: select * from teacher where id=? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:14:15,962  ====> Parameters: 1(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:14:15,965  <====      Total: 1
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:14:15,974  ====>  Preparing: select * from teacher where id=? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:14:15,978  ====> Parameters: 2(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:14:15,981  <====      Total: 1
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:14:15,984  <==      Total: 11
Student [id=1, name=学生1, age=1, tid=1, teacher=Teacher [id=1, name=老师1, studentList=null]]
Student [id=2, name=学生2, age=2, tid=1, teacher=Teacher [id=1, name=老师1, studentList=null]]
Student [id=3, name=学生3, age=3, tid=1, teacher=Teacher [id=1, name=老师1, studentList=null]]
Student [id=4, name=学生4, age=4, tid=1, teacher=Teacher [id=1, name=老师1, studentList=null]]
Student [id=5, name=学生5, age=5, tid=1, teacher=Teacher [id=1, name=老师1, studentList=null]]
Student [id=6, name=学生6, age=6, tid=1, teacher=Teacher [id=1, name=老师1, studentList=null]]
Student [id=7, name=学生7, age=7, tid=2, teacher=Teacher [id=2, name=老师2, studentList=null]]
Student [id=8, name=学生8, age=8, tid=2, teacher=Teacher [id=2, name=老师2, studentList=null]]
Student [id=9, name=学生9, age=9, tid=2, teacher=Teacher [id=2, name=老师2, studentList=null]]
Student [id=10, name=学生10, age=10, tid=2, teacher=Teacher [id=2, name=老师2, studentList=null]]
Student [id=11, name=学生11, age=11, tid=2, teacher=Teacher [id=2, name=老师2, studentList=null]]

使用注解 关联单个对象(联合查询方式)

@Select("select t.id  `teacher.id` ,t.name `teacher.name` ,s.id id ,s.name name ,age ,tid from student s left join teacher t on t.id=s.tid")
List<Student> selAll();

使用

StudentMapper studentMapper1 = sqlSession.getMapper(StudentMapper.class);
List<Student> selAll21 = studentMapper1.selAll();
for (Student student : selAll21) {
	System.out.println(student);
}

结果

- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:16:41,879  ==>  Preparing: select t.id `teacher.id`,t.name`teacher.name`,s.id id,s.name name, age,tid from student s left join teacher t on t.id=s.tid 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:16:41,879  ==> Parameters: 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:16:41,889  <==      Total: 11
Student [id=1, name=学生1, age=1, tid=1, teacher=Teacher [id=1, name=老师1, studentList=null]]
Student [id=2, name=学生2, age=2, tid=1, teacher=Teacher [id=1, name=老师1, studentList=null]]
Student [id=3, name=学生3, age=3, tid=1, teacher=Teacher [id=1, name=老师1, studentList=null]]
Student [id=4, name=学生4, age=4, tid=1, teacher=Teacher [id=1, name=老师1, studentList=null]]
Student [id=5, name=学生5, age=5, tid=1, teacher=Teacher [id=1, name=老师1, studentList=null]]
Student [id=6, name=学生6, age=6, tid=1, teacher=Teacher [id=1, name=老师1, studentList=null]]
Student [id=7, name=学生7, age=7, tid=2, teacher=Teacher [id=2, name=老师2, studentList=null]]
Student [id=8, name=学生8, age=8, tid=2, teacher=Teacher [id=2, name=老师2, studentList=null]]
Student [id=9, name=学生9, age=9, tid=2, teacher=Teacher [id=2, name=老师2, studentList=null]]
Student [id=10, name=学生10, age=10, tid=2, teacher=Teacher [id=2, name=老师2, studentList=null]]
Student [id=11, name=学生11, age=11, tid=2, teacher=Teacher [id=2, name=老师2, studentList=null]]

使用注解 关联集合对象(N+1方式)

public interface TeacherMapper {
	@Results(value= {
		@Result(id=true,column="id",property="id"),
		@Result(property="name",column="name"),
		@Result(property="studentList",column="id",many=@Many(select="com.mapper.StudentMapper.selById"))
	})
	@Select("select * from teacher")
	List<Teacher> selAll1();
	}
TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);
List<Teacher> selAll1 = teacherMapper.selAll1();
for (Teacher teacher : selAll1) {
	System.out.println(teacher);
}

结果

- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,078  ==>  Preparing: select * from teacher 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,165  ==> Parameters: 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,226  ====>  Preparing: select * from student where tid=? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,227  ====> Parameters: 1(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,242  <====      Total: 6
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,244  ====>  Preparing: select * from student where tid=? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,244  ====> Parameters: 2(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,248  <====      Total: 5
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,250  ====>  Preparing: select * from student where tid=? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,250  ====> Parameters: 4(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,252  <====      Total: 0
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,253  ====>  Preparing: select * from student where tid=? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,254  ====> Parameters: 5(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,255  <====      Total: 0
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,257  ====>  Preparing: select * from student where tid=? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,258  ====> Parameters: 6(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,261  <====      Total: 0
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,262  ====>  Preparing: select * from student where tid=? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,263  ====> Parameters: 7(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,264  <====      Total: 0
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,265  ====>  Preparing: select * from student where tid=? 
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,267  ====> Parameters: 8(Integer)
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,269  <====      Total: 0
- org.apache.ibatis.logging.jdbc.BaseJdbcLogger 2021-08-26 23:11:41,270  <==      Total: 7
Teacher [id=1, name=老师1, studentList=[Student [id=1, name=学生1, age=1, tid=1, teacher=null], Student [id=2, name=学生2, age=2, tid=1, teacher=null], Student [id=3, name=学生3, age=3, tid=1, teacher=null], Student [id=4, name=学生4, age=4, tid=1, teacher=null], Student [id=5, name=学生5, age=5, tid=1, teacher=null], Student [id=6, name=学生6, age=6, tid=1, teacher=null]]]
Teacher [id=2, name=老师2, studentList=[Student [id=7, name=学生7, age=7, tid=2, teacher=null], Student [id=8, name=学生8, age=8, tid=2, teacher=null], Student [id=9, name=学生9, age=9, tid=2, teacher=null], Student [id=10, name=学生10, age=10, tid=2, teacher=null], Student [id=11, name=学生11, age=11, tid=2, teacher=null]]]
Teacher [id=4, name=老师4, studentList=[]]
Teacher [id=5, name=老师4, studentList=[]]
Teacher [id=6, name=老师4, studentList=[]]
Teacher [id=7, name=老师4, studentList=[]]
Teacher [id=8, name=老师4, studentList=[]]

使用注解 关联集合对象(联合查询方式)

待定


MyBatis实现一对一有几种方式?具体怎么操作的?

联合查询:几个表联合查询,只查询一次,通过在resultMap里面配置;
N+1查询是先查一个表,根据这个表里面的结果作为外键id,去再另外一个表里面查询数据。

Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别? 还没理解
能,
Mybatis不仅可以执行一对一、一对多的关联查询,还可以执行多对一,多对多的关联查询

一对一,一个学生对一个老师
一对多,一个老师对多个学生
多对一查询,其实就是一对一查询,只需要把selectList()修改为selectOne()即可;
多对多查询,其实就是一对多查询,只需要把selectOne()修改为selectList()即可。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值