Mybatis学习笔记
简介
什么是Mybatis?
- MyBatis 是一款优秀的__持久层框架__
- 它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
持久层
数据持久化
- 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
- 内存:断电即失
- 数据库,io文件持久化
为什么需要持久化? - 有一些对象,不能让他丢失
- 内存太贵
持久层
Dao层,Service层,Controller层
- 完成持久化工作的代码块
- 层次很明显
为什么需要Mybatis?
-
传统的JDBC代码太复杂了,框架就是简化操作的
-
__hibernate__处理复杂业务时,灵活度差,复杂的HQL难写难理解,例如多表查询的HQL语句
-
spring的__JdbcTemplete__就是jdbc的封装,类似dbutils没什么特别的
-
不用Mybatis也可以。更容易上手
-
Mybatis的优点
- 简单易学
- 灵活
- 解除sql与程序代码的耦合
- 映射标签
- 提供xml标签
第一个Mybatis程序
搭建环境
建表
CREATE TABLE USER (
uid INT PRIMARY KEY auto_increment,
username VARCHAR ( 50 ),
PASSWORD VARCHAR ( 50 )
) ENGINE = INNODB DEFAULT CHARSET = utf8;
INSERT INTO USER ( username, PASSWORD )
VALUES
( "root", "111111" ),
( "admin", "222222" );
- 新建maven项目
- 删除src文件,作为父工程
- 配置pom.xml
<dependency>
<!-- mybatis-->
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!-- junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
- 创建一个模块
创建一个模块
- 创建mybatis核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--mybatis的核心配置文件-->
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&userUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="761020zy"/>
</dataSource>
</environment>
</environments>
</configuration>
- 建立工具类
//sqlSessionFactory-->sqlSession
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
//使用Mybatis第一步:获取sqlSessionFactory对象
inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
// 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
编写代码
-
实体类
public class User { private int uid; private String username; private String password; public User() { } public User(int uid, String username, String password) { this.uid = uid; this.username = username; this.password = password; } public int getUid() { return uid; } public void setUid(int uid) { this.uid = uid; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
-
Dao接口
public interface UserDao { List<User> getUserList(); }
-
接口实体类转化为Mapper配置文件(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">
<!--namsspace绑定一个对应的Dao/Mapper接口-->
<mapper namespace="cn.itcast.dao.UserDao">
<select id="getUserList" resultType="cn.itcast.domain.User">
# 查询语句
select *
from mybatis.user;
</select>
</mapper>
-
测试
- 报错:org.apache.ibatis.binding.BindingException: Type interface cn.itcast.dao.UserDao is not known to the MapperRegistry.
-
原因:Mapper文件没有引入mybatis的核心文件
- 解决:在mybatis引入Mapper文件
- 报错:java.lang.ExceptionInInitializerError
- 原因:资源过滤问题
-
解决:放在resources下或者配置pom文件让idea可以加载java下面的配置文件
@Test public void test01() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao userDao = sqlSession.getMapper(UserDao.class); List<User> list = userDao.getUserList(); for (User user : list) { System.out.println(user.toString()); } //关闭sqlSession sqlSession.close(); }
Mybatis中的事务是默认开启的,因此我们在完成操作以后,需要我们手动去提交事务!
测试结果
简单CRUD
1、namespace
namespace中的包名要和Dao/mapper接口的包名一致!
2、select
选择,查询语句;
- id:namespace中的唯一标识符就是方法名
- resultType:sql语句执行的返回值!
- parameterType:参数类型,方法中传的参数
3、insert
<!-- 插入-->
<insert id="addUser" parameterType="cn.itcast.domain.User">
insert into mybatis.user(uid, username, password)
values (#{uid}, #{username}, #{password})
</insert>
4、update
<update id="updateUser" parameterType="cn.itcast.domain.User">
update mybatis.user
set username=#{username},
password=#{password}
where uid = #{uid}
</update>
5、delete
<delete id="deleteUser" parameterType="Integer">
delete
from mybatis.user
where uid = #{uid};
</delete>
注意点:
- 增删改需要提交事务!
万能的Map
假设,我们的实体类,或者数据库中的表,字段或者参数过多,我们应当考虑使用Map!
Map传递参数,直接在ssql中取出key即可!
对象传递参数,直接在sql中取对象的属性即可!
只有一个基本参数,直接在sql中取到即可
多个参数用Map,或者__注解__
配置解析
核心配置文件
-
mybatis-config.xml
-
mybatis的配置文件包含了影响mybatis行为的设置和属性信息
- configuration(设置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- databaseIdProvider(数据库库厂标识)
- mappers(映射器)
环境变量(enevirnments)
Mybatis可以配置多个环境
但是sqlSessionFactory实例只能选择__一种环境__
<environments default="development">
<!-- 环境一:-->
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url"
value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
属性(properties)
-
可以直接引入外部文件
-
可以在其中增加一些属性配置
-
如果两个文件有同一个字段,优先使用外部配置文件
<properties resource="db.properties"/>
类型别名
-
给java设置一个短的名字,他只和XML配置相关
-
减少类全限定名的冗余
<typeAliases >
<typeAlias type="cn.itcast.domain。User" alias="User"/>
</typeAliases>
实例类比较少的时候使用,alias可以是任意值
<typeAliases >
<typeAlias type="cn.itcast.domain.User" />
</typeAliases>
实体类较多的时候使用,默认名称就是这个包下面类的类名,但是注意了__首字母要小写__,如果要改就要加在实体类前面加一个注解@Alias(“你要取的名称”)
映射器(mappers)
注册绑定我们的Mapper文件
resource
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
class
<mappers>
<mapper class="cn.itcast.dao.UserDao"/>
</mappers>
注意:
- 接口和Mapper配置文件必须同名
- 接口和Mapper配置文件必须在用一个包下
package
<mappers>
<package name="cn.itcast.dao"/>
</mappers>
注意:
- 接口和Mapper配置文件必须同名
- 接口和Mapper配置文件必须在用一个包下
生命周期和作用域
-
SqlSessionFactoryBuilder:
- 一旦创建了sqlSessionFactory,就不需要它了
- 局部变量
-
SqlSessionFactory:
- 一旦创建就应该一直存在,没有理由丢其他或者重新创建另一个实例
- 最简单就是使用单例或者静态单例模式
-
SqlSession
- SqlSession的实例不是线程安全的,不能被共享
- 用完要关闭
解决属性名和字段名不一致的问题
问题
<select id="getUserList" resultType="User">
select uid,username,password as pwd
from mybatis.user;
</select>
解决方法
-
起别名,让实体类属性的名称和数据库字段名称一致
-
resultMap=""(使用结果集映射)
结果集映射resultMap
和hibernate的xxx.hbm.xml差不多,都是建立对象和表的一个映射关系
<mapper namespace="cn.itcast.dao.UserDao">
<resultMap id="UserMap" type="User">
<result property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
</resultMap>
<!-- 显示全部-->
<select id="getUserList" resultType="User" resultMap="UserMap">
select uid,username,password
from mybatis.user;
</select>
- resultMap元素是Mybatis中最强大的元素
- 对于简单的语句,如果能和实体类属性对应起来就不用结果映射,对于复杂的语句就要建立映射了
日志
日志工厂
数据库操作出现异常,我们需要排错,所以我们需要日志
- SLF4J
- LOG4J
- NO_LOGGING
- STDOUT_LOGGING(标准日志输出)
在Mybatis中具体使用哪个日志实现,在设置中设定
<!-- 日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
控制台会打印很多日志信息
log4j
- 导入Log4j的maven
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 编写log4j的配置文件
log4j.rootLogger=DEBUG,console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
- 测试
//Log4j测试
@Test
public void test07() {
logger.info("info:进入Log4j");
logger.debug("debug:进入Log4j");
logger.error("error:进入Log4j");
}
分页
为什么使用分页技术?
- 减少数据的处理量
注意:我们的分页是需要多个参数的,并不是像我们之前的例子中只有一个参数。当需要接收多个参数的时候,我们使用Map集合来装载!
<select id="getUserByLimit" parameterType="map" resultType="user">
select *
from mybatis.user
limit ${startIndex},${pageSize}
</select>
@Test
public void test08() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 方式一:
try {
UserDao userDao = sqlSession.getMapper(UserDao.class);
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex", 1);
map.put("pageSize", 2);
List<User> list = userDao.getUserByLimit(map);
for (User user : list) {
System.out.println(user.toString());
}
} finally {
//关闭sqlSession
sqlSession.close();
}
}
注解开发
注解开发
// 获取全部用户
@Select("select * from mybatis.user")
List<User> getUserList();
<mappers>
<mapper class="cn.itcast.dao.UserDao"/>
</mappers>
- 本质:反射机制
- 底层:动态代理
mybatis执行原理
// 根据ID查询用户
@Select("select * from mybatis.user where uid=${id}")
User getUserById(@Param("id") Integer id);
// insert一个用户
@Insert("insert into mybatis.user(username,password) values(#{username},#{password})")
Integer addUser(User user);
关于@Param()注解
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型,可以省略,但是建议大家都加上
- 我们在SQL中引用的就是我们这里的@Param(“uid”)中设定的属性名
#{}和${}
#{}可以防止sql注入
多对一
- 多个学生对应一个老师
- 多个学生关联一个老师【多对一】
- 一个老师集合多个学生【一对多】
建表
create table teacher(
teacherId int not null primary key auto_increment,
teacherName varchar(30) not null
)engine=innodb default charset=utf8;
create table student(
studentId int primary key ,
studentName varchar(30) not null,
teacherId int,
CONSTRAINT t_s_fk foreign key (teacherId) references teacher(teacherId)
)engine=innodb default charset=utf8;
实体类
package cn.itcast.pojo;
public class Student {
private Integer id;
private String name;
private Teacher teacher;
Getter and Setter...
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", teacher=" + teacher +
'}';
}
}
package cn.itcast.pojo;
public class Teacher {
private Integer id;
private String name;
Getter and Setter...
@Override
public String toString() {
return "Teacher{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
public interface StudentMapper {
public List<Student> getStudent();
public Teacher getTeacher(Integer id);
}
按照查询嵌套处理
<select id="getStudent" resultType="Student" resultMap="StudentMap">
select *
from student;
</select>
<resultMap id="StudentMap" type="Student">
<!-- 主键-->
<id property="id" column="studentId"/>
<!-- 普通属性-->
<result property="name" column="studentName"/>
<!-- 对象使用association-->
<!-- 集合使用Collection-->
<association property="teacher" column="teacherId" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher" resultMap="TeacherMap">
select *
from teacher
where teacherId = #{id}
</select>
<resultMap id="TeacherMap" type="Teacher">
<id property="id" column="teacherId"/>
<result property="name" column="teacherName"/>
</resultMap>
按照结果嵌套处理
<select id="getStudent" resultMap="StudentMap">
select studentId,studentName,student.teacherId,teacherName
from student
inner join teacher on student.teacherId = teacher.teacherId;
</select>
<resultMap id="StudentMap" type="Student">
<id column="studentId" property="id"/>
<result column="studentName" property="name" />
<association property="teacher" javaType="Teacher">
<id property="id" column="teacherId"/>
<result property="name" column="teacherName"/>
</association>
</resultMap>
mysql多对一查询:
- 子查询
- 连接查询
一对多
按照结果嵌套处理
<select id="getTeacher" resultType="Teacher" resultMap="TeacherMap">
select *
from mybatis.teacher;
</select>
<resultMap id="TeacherMap" type="Teacher">
<id property="id" column="teacherId"/>
<result property="name" column="teacherName"/>
<collection property="studentList" column="teacherId" javaType="ArrayList" ofType="Student" select="getStudentById"/>
</resultMap>
<select id="getStudentById" resultType="Student" resultMap="StudentMap">
select *
from mybatis.student
where tid = #{tid}
</select>
<resultMap id="StudentMap" type="Student">
<id column="studentId" property="id"/>
<result column="studentName" property="name"/>
</resultMap>
按照查询嵌套处理
<select id="getTeacher" resultType="Teacher" resultMap="TeacherMap">
select *
from teacher
left join student on teacher.teacherId = student.tid;
</select>
<resultMap id="TeacherMap" type="Teacher">
<id property="id" column="teacherId"/>
<result property="name" column="teacherName"/>
<collection property="studentList" ofType="Student">
<id property="id" column="studentId"/>
<result property="name" column="studentName"/>
</collection>
</resultMap>
小结
- javaType和ofType的区别
- 保证sql的可读性,保证通俗易懂
- 属性名和字段的问题
- 使用好log4j日志
提高sql查询速度
- mysql引擎
- innoDB底层原理
- 索引
- 索引优化
动态SQL
什么是动态SQL:动态SQL就是根据不同的条件生成不同的SQL语句**【动态SQL就是自动拼接SQL语句】**!
if
List<Blog> queryBlog(Map map);
<select id="queryBlog" parameterType="map" resultType="blog">
select *
from mybatis.blog where 1=1
<if test="title!=null">
and title=#{title}
</if>
</select>
@Test
public void test02() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("title", "正则表达式");
List<Blog> list = blogMapper.queryBlog(map);
for (Blog blog : list) {
System.out.println(blog.toString());
}
sqlSession.commit();
sqlSession.close();
}
choose,where,otherwise
- where+if
<select id="queryBlog" parameterType="map" resultType="blog">
select *
from mybatis.blog
<where>
<if test="title!=null">
title=#{title}
</if>
<if test="author!=null">
and author=#{author}
</if>
<if test="views!=null">
and views=#{views}
</if>
</where>
</select>
@Test
public void test02() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("views", "125");
map.put("author","小赵");
List<Blog> list = blogMapper.queryBlog(map);
for (Blog blog : list) {
System.out.println(blog.toString());
}
sqlSession.commit();
sqlSession.close();
}
where+choose(when+otherwise)类似于java的switch+case
<select id="queryBlog" parameterType="map" resultType="blog">
select *
from mybatis.blog
<where>
<choose>
<when test="author!=null">
author=#{author}
</when>
<when test="views!=null">
and views=#{views}
</when>
<otherwise>
and title=#{title}
</otherwise>
</choose>
</where>
</select>
set+if
<set>
<if test="title!=null">
title=#{title},
</if>
<if test="author!=null">
author=#{author}
</if>
</set>
@Test
public void test05() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
map.put("id","8");
map.put("author","大佬");
Integer integer = blogMapper.updateBlog(map);
System.out.println(integer);
sqlSession.commit();
sqlSession.close();
}
trim
可以做set和when的事情
sql片段
- 有时候,我们会将一些公共的sql抽取出来,然后服用
<sql id="update">
<if test="title!=null">
title=#{title},
</if>
<if test="author!=null">
author=#{author}
</if>
</sql>
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<include refid="update"/>
</set>
where id=#{id}
</update>
- 最好基于单表定义SQL片段
- 不要存在where标签
foreach
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
- 动态sql就是在拼接sql语句
Mybatis缓存
-
查询:连接数据库,耗资源
-
一次查询的结果,给他暂存一个可以直接取到的地方!–>内存:缓存
-
我们再次查询相同数据的时候,直接走缓存,就不用走数据库了
-
什么是缓存?
- 存在内存中的临时数据
- 将用户经常查询的数据放在缓存(内存)中,哟用户去查询数据就不用从磁盘中就是数据库中查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
-
为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率
-
什么样的数据能使用缓存?
- 经常查询并且不经常修改的数据
-
mybatis定义了两级缓存:_一级缓存__和__二级缓存
一级缓存
sqlsession级别的缓存
在同一次会话期间查询的数据会放在本地缓存中,需要同样的数据,直接从缓存中拿,没必要再去查数据库
- 缓存失效:
- 手动清理缓存clearcache
- 查询不同的东西
- 增删改查操作之后,会改变原来的数据,所以必定会刷新缓存
- 查询不同的Mapper.xml
二级缓存
会话关闭一级缓存就没了,一级缓存中的数据被保存到二级缓存中
<!-- 开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
<!-- 开启二级缓存-->
<cache/>
自定义缓存
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
注意:实体类需要序列化
- 小结
- 只要开启了二级缓存,在同一个Mapper下就有效
- 所有的数据都会先放在一级缓存中
- 只有当会话提交,或者关闭的时候,才会提交到二级缓存
总结
-
在Mybatis中,有两种占位符
- #{}解析传递进来的参数数据
- ${}对传递进来的参数原样拼接在SQL中
-
如果我们在Hibernate中,当我们插入数据的时候,我们是可以选择是UUID策略的,那么在Mybatis是怎么做的呢??
<!-- mysql的uuid生成主键 -->
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey keyProperty="id" order="BEFORE" resultType="string">
select uuid()
</selectKey>
INSERT INTO USER(id,username,birthday,sex,address) VALUES(#{id},#{username},#{birthday},#{sex},#{address})
</insert>
- user对象插入到数据库后,新记录的主键要如何通过user对象返回
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User">
<selectKey keyProperty="id" order="AFTER" resultType="int">
select LAST_INSERT_ID()
</selectKey>
INSERT INTO USER(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address})
</insert>
- resultMap和resultType区别
- 指定输出结果的类型(pojo、简单类型、hashmap…),将sql查询结果映射为java对象,sql查询的__列名__要和resultType指定pojo的__属性名相同__,指定相同 属性方可映射成功
- resultMap:将sql查询结果映射为java对象,如果sql查询列名和最终要映射的pojo的属性名不一致**,使用resultMap将列名和pojo的属性名做一个对应关系**
- association和collection
- association:将关联查询信息映射到一个pojo类中。【多的一方】
- collection:将关联查询信息映射到一个list集合中。【一的一方】
- 延迟加载
- lazyLoadingEnabled:全局懒加载
- aggressiveLazyLoading:按需加载
- 在程序中调用的__SQL语句__是由映射文件的__命令空间(namespace)+sql片段的id__所组成的。它内部会生成一个Statement对象的。
- 缓存
- 一级缓存:一级缓存是一个__sqlsession__级别,sqlsession只能访问自己的一级缓存的数据
- 二级缓存:跨sqlSession,是__mapper级别__的缓存,对于mapper级别的缓存__不同的sqlsession__是可以__共享__的
- 缓存注意
- Mybatis默认就是支持一级缓存的,并不需要我们配置
- mybatis和spring整合后,不支持一级缓存,spring按照mapper的模板去生成mapper代理对象,模板中在最后__统一关闭sqlsession__
- 为什么增删改查的时候,缓存默认就会被清空?
- 缓存保存了__增删改后的数据__,那么再次读取时就会读到__脏数据__了!
LAST_INSERT_ID()
INSERT INTO USER(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address})
* resultMap和resultType区别
* 指定输出结果的类型(pojo、简单类型、hashmap..),将sql查询结果映射为java对象,sql查询的__列名__要和resultType指定pojo的__属性名相同__,指定相同 属性方可映射成功
* resultMap:将sql查询结果映射为java对象,如果sql查询列名和最终要映射的pojo的属性名不一致**,使用resultMap将列名和pojo的属性名做一个对应关系**
* association和collection
* association:**将关联查询信息映射到一个pojo类中。**【多的一方】
* collection:**将关联查询信息映射到一个list集合中。**【一的一方】
* 延迟加载
* lazyLoadingEnabled:全局懒加载
* aggressiveLazyLoading:按需加载
* 在程序中调用的__SQL语句__是由映射文件的__命令空间(namespace)+sql片段的id__所组成的。它内部会生成一个Statement对象的。
* 缓存
* 一级缓存:一级缓存是一个__sqlsession__级别,sqlsession只能访问自己的一级缓存的数据
* 二级缓存:跨sqlSession,是__mapper级别__的缓存,对于mapper级别的缓存__不同的sqlsession__是可以__共享__的
* 缓存注意
* Mybatis默认就是支持一级缓存的,并不需要我们配置
* mybatis和spring整合后,不支持一级缓存,spring按照mapper的模板去生成mapper代理对象,模板中在最后__统一关闭sqlsession__
* 为什么增删改查的时候,缓存默认就会被清空?
* 缓存保存了__增删改后的数据__,那么再次读取时就会读到__脏数据__了!