文章目录
Mybatis-补充
1 连接池
在JDBC中,数据库的连接是一种有限的昂贵资源,所以为了避免频繁的创建导致资源浪费,JDBC有了连接池的概念,而mybatis中的连接池就是基于JDBC连接池做的扩展
1.1 实现
在配置文件中,通过来实现mybatis中的连接池配置
1.2 分类
在mybatis中,数据库连接池主要分为三类
UNPOOLED 不使用连接池的数据源
POOLED 使用连接池的数据源
JNDI 使用JNDI实现的数据库连接池
1.3 UNPOOLED连接过程分析
1 配置dateSource 的type属性为UNPOOLED
2 MyBatis 实例化一个UnpooledDataSourceFactory工厂实例
3 通过.getDataSource() 方法返回一个UnpooledDataSource 实例对象引用
UnpooledDataSource会做以下事情
1.1 初始化驱动: 判断driver驱动是否已经加载到内存中,如果还没有加载,则会动态地加载driver类,并实例化一个Driver对象,使用DriverManager.registerDriver()方法将其注册到内存中,以供后续使用。
1.2 创建Connection对象: 使用DriverManager.getConnection()方法创建连接。
1.3 配置Connection对象: 设置是否自动提交autoCommit和隔离级别isolationLevel。
1.4 返回Connection对象
我们每调用一次getConnection()方法,都会通过DriverManager.getConnection()返回新的java.sql.Connection实例,所以没有连接池
详细过程可查看相关源码
1.4 POOLED过程分析
详细过程可查看相关源码
2 事务和隔离级别
2.1 mybatis中的事务
Mybatis 中事务的提交方式,本质上就是调用 JDBC 的 setAutoCommit()来实现事务控制
// 在打开session时传入true设置为自动提交
sqlSession = MybatisUtils.openSession(true);
2.2 事务回滚
一般与try…catch配合使用,在出现报错的时候可以通过sqlSession.rollback()进行事务回滚,一般用于多个写操作的时候
2.3 事务的隔离级别
脏读 | 用户1读取用户2未提交的数据 |
---|---|
不可重复读 | 用户1进行事务操作未提交时用户2提交了修改事务操作,用户1发现同样的条件, 读取过的数据, 再次读取出来发现值不一样了 |
幻读 | 用户1进行事务操作未提交时用户2提交了新增或删除事务操作,用户1发现同样的条件, 第1次和第2次读出来的记录数不一样 |
当出现这些问题时我们就需要设置事务的隔离级别来解决这个问题
脏读 | 不可重复读 | 幻读 | 说明 | |
---|---|---|---|---|
Read uncommitted | √ | √ | √ | 直译就是"读未提交",意思就是即使一个更新语句没有提交,但是别 的事务可以读到这个改变.这是很不安全的。允许任务读取数据库中未提交的数据更改,也称为脏读。 |
Read committed | × | √ | √ | 直译就是"读提交",可防止脏读,意思就是语句提交以后即执行了COMMIT以后,别的事务才能读到这个改变. 只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 |
Repeatable read | × | × | √ | 直译就是"可以重复读",这是说在同一个事务里面先后执行同一个查询语句的时候,得到的结果是一样的.在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读。 |
Serializable | × | × | × | 直译就是"序列化",意思是说这个事务执行的时候不允许别的事务并发执行. 完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞 |
mybatis中也可以设置隔离级别,只不过增加了一个没有事务的属性
package org.apache.ibatis.session;
public enum TransactionIsolationLevel {
NONE(0),
READ_COMMITTED(2),
READ_UNCOMMITTED(1),
REPEATABLE_READ(4),
SERIALIZABLE(8);
private final int level;
private TransactionIsolationLevel(int level) {
this.level = level;
}
public int getLevel() {
return this.level;
}
}
3 延迟加载
3.1 概念
需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载
3.2 实现
通过lazyLoadingEnabled、aggressiveLazyLoading可以对延迟加载进行配置
<settings>
<setting name="logImpl" value="LOG4J"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
在association配置fetchType属性
<association property="user" column="user_id" javaType="User" select="com.tledu.Mybatis_hw.mapper.UserMapper.selectUserById" fetchType="lazy"/>
添加了懒加载配置之后,我们在进行列表查询的过程中就不会再做大量的关联查询了,可以提升列表查询的效率,在我们用到具体字段之后才会进行关联查询
collection的懒加载实现和association基本类似
4 注解开发
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@ResultMap:实现引用@Results 定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装
可参考之前实现Mapper的三种方式的第二种
4.1 使用Result进行映射
@Select("select * from t_address where id = #{id}")
@Results(id = "addressRes", value = {
//id = true 标志这个字段是主键
@Result(id = true, column = "id", property = "id"),
@Result(column = "addr", property = "addr"),
@Result(column = "phone", property = "mobile"),
})
Address selectById(int id);
4.2 注解进行映射
1对多,1对1类似,使用@one
@Select("select * from t_user where id = #{id}")
@Results(id = "addressRes", value = {
//id = true 标志这个字段是主键
@Result(id = true, column = "id", property = "id"),
@Result(column = "id", property = "addressList",
many = @Many(select = "com.tledu.erp.mapper.IAddressMapper.listByUserId", fetchType = FetchType.EAGER))
})
User selectById(int id);
5 缓存
5.1 一级缓存
一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就默认存在
一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存
运用场景:连续进行同一个查询时,第二个查询会读缓存,只执行一次sql语句
5.2 二级缓存
二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的
5.2.1 实现
配置映射xml文件
<mapper namespace="com.tledu.erp.mapper.IAddressMapper">
<!-- 开启缓存支持-->
<cache />
</mapper>
查询语句中需要指定useCache=“true”
<select id="selectOne" resultMap="addressResultMap" useCache="true">
select *
from t_address
where id = #{id}
</select>
注意: 当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化方式来保存对象
5.3 总结
只有当sqlSession close之后,二级缓存才能生效
当执行增删改操作的时候,必须执行commit()才能持久化到数据库中,同时二级缓存清空
session.clearCache()无法清除二级缓存,如果需要清除二级缓存,可以通过sqlSessionFactory.getConfiguration().getCache(“缓存id”).clear();
但是当我们查询语句中,执行commit() 或者是close()关闭session,都不会清空二级缓存
6 分页查询
6.1 插件实现
6.1.1 引入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
6.1.2 配置拦截器
在mybatis的配置文件中增加插件
<!--
plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
properties?, settings?,
typeAliases?, typeHandlers?,
objectFactory?,objectWrapperFactory?,
plugins?,
environments?, databaseIdProvider?, mappers?
-->
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->
<property name="param1" value="value1"/>
</plugin>
</plugins>
6.1.3 配置插件
参考pagehelper官网
6.1.4 使用插件
@Test
public void testList() throws IOException {
PageInfo pageInfo = new PageInfo();
// 插件里提供的分页工具,在要查询之前,执行一下PageHelper.startPage(当前页数,每页的容量), 当使用工具时候,会导致懒加载失败
// 加了这个操作,插件就会在sql语句中拼接limit限制,并且还会统计总个数
PageHelper.startPage(1, 2);
List<User> users = pageMapper.list1(pageInfo);
// 拿到结果之后通过PageInfo.of() 的方法,获得pageInfo
com.github.pagehelper.PageInfo<User> list = com.github.pagehelper.PageInfo.of(users);
System.out.println(list);
}
6.2 基本代码实现
创建一个page实体类
@Data
public class PageInfo<T> {
private Integer pageNum;
private Integer pageSize;
private Integer total;
private List<T> list;
private Integer start;
public void setStart() {
this.start = (this.pageNum - 1) * this.pageSize;
}
}
编写xml
<mapper namespace="com.tledu.Mybatis_hw.mapper.PageMapper">
<select id="list" resultType="User">
select *
from user
limit #{start},#{pageSize}
</select>
<select id="list1" resultType="User">
select *
from user
</select>
<select id="count" resultType="int">
select count(1)
from user;
</select>
</mapper>
编写Mapper接口方法
public interface PageMapper {
List<User> list(PageInfo pageInfo);
List<User> list1(PageInfo pageInfo);
int count();
}
测试
@Test
public void testPage() {
PageInfo pageInfo = new PageInfo();
pageInfo.setPageNum(1);
pageInfo.setPageSize(2);
pageInfo.setStart();
pageInfo.setTotal(pageMapper.count());
pageInfo.setList(pageMapper.list(pageInfo));
}
@Test
public void testPage2() {
PageInfo pageInfo = new PageInfo();
PageHelper.startPage(1, 2);
List<User> users = pageMapper.list1(pageInfo);
com.github.pagehelper.PageInfo<User> list = com.github.pagehelper.PageInfo.of(users);
System.out.println(list);
}