Mybatis-补充

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);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值