MyBatis缓存及嵌套查询--3

本文详细介绍了MyBatis的一级和二级缓存机制,包括缓存的默认行为、如何配置缓存策略以及如何利用缓存提高数据库查询效率。同时,文章还深入探讨了语句级缓存配置和嵌套查询的应用。
摘要由CSDN通过智能技术生成

MyBatis缓存

## 概述
MyBatis的缓存是指将查询结果放到缓存中,从而避免多次与数据库交互,影响查询效率等。

1、一级缓存:默认开启

1、第一种情况
session级别的缓存,一级缓存默认存在。当在同一个session范围内执行查询的时候,如果执行相同的查询,那么第二次查询会从缓存中获取数据。
主函数

public static void main(String[] args) {
		SqlSession session =MyBatisUtil.getSqlSession();
		Test1Mapper testMapper = session.getMapper(Test1Mapper.class);
		//第一次查询
		List<Test1> testList = testMapper.selectTest();
		for(Test1 t : testList) {
			System.out.println(t.toString());
		}
		//第二次查询
		List<Test1> testList2 = testMapper.selectTest();
		for(Test1 t2 : testList2) {
			System.out.println(t2.toString());
		}
		session.close();
	}

测试结果
只进行一次查询
在这里插入图片描述

2、第二种情况
如果两次查询之间有增删改操作,sqlsession缓存区会被自动清空,说明下一次查询会重新执行sql语句。
主函数

public static void main(String[] args) {
		SqlSession session =MyBatisUtil.getSqlSession();
		Test1Mapper testMapper = session.getMapper(Test1Mapper.class);
		
		//第一次查询
		List<Test1> testList = testMapper.selectTest();
		for(Test1 t : testList) {
			System.out.println(t.toString());
		}
		
		//更新操作
		Test1 test = new Test1();
		test.setId("8");
		test.setName("dd");
		test.setAge(18);
		int i = testMapper.updateTest1(test);
		
		//第二次查询
		List<Test1> testList2 = testMapper.selectTest();
		for(Test1 t2 : testList2) {
			System.out.println(t2.toString());
		}
		session.close();
	}

测试结果
在这里插入图片描述

2、二级缓存:默认不开启

1、不开启的情况
如果在不同的session范围内,执行相同的数据查询,那么每次查询将会执行独立的数据库检索过程。
主函数

public static void main(String[] args) {
		//第一个session
		SqlSession session =MyBatisUtil.getSqlSession();
		Test1Mapper testMapper = session.getMapper(Test1Mapper.class);
		List<Test1> testList = testMapper.selectTest();
		for(Test1 t : testList) {
			System.out.println(t.toString());
		}
		session.close();
		//第二个session
		SqlSession session2 =MyBatisUtil.getSqlSession();
		Test1Mapper testMapper2 = session2.getMapper(Test1Mapper.class);
		List<Test1> testList2 = testMapper2.selectTest();
		for(Test1 t2 : testList2) {
			System.out.println(t2.toString());
		}
		session.close();
	}

测试结果
在这里插入图片描述

2、开启后的情况
1、在mapper映射文件中的标签中加入:
2、在实体类中实现序列化接口
主函数

public static void main(String[] args) {
		//第一个session
		SqlSession session =MyBatisUtil.getSqlSession();
		Test1Mapper testMapper = session.getMapper(Test1Mapper.class);
		List<Test1> testList = testMapper.selectTest();
		for(Test1 t : testList) {
			System.out.println(t.toString());
		}
		session.close();
		
		//第二个session
		SqlSession session2 =MyBatisUtil.getSqlSession();
		Test1Mapper testMapper2 = session2.getMapper(Test1Mapper.class);
		List<Test1> testList2 = testMapper2.selectTest();
		for(Test1 t2 : testList2) {
			System.out.println(t2.toString());
		}
		session.close();
	}

测试结果
在这里插入图片描述

3、加入增删改的情况
主函数

public static void main(String[] args) {
		//第一次查询
		SqlSession session =MyBatisUtil.getSqlSession();
		Test1Mapper testMapper = session.getMapper(Test1Mapper.class);
		List<Test1> testList = testMapper.selectTest();
		for(Test1 t : testList) {
			System.out.println(t.toString());
		}
		
		//更新操作
		Test1 test = new Test1();
		test.setId("8");
		test.setName("dd");
		test.setAge(18);
		int i = testMapper.updateTest1(test);
		
		//第二次查询
		List<Test1> testList2 = testMapper.selectTest();
		for(Test1 t2 : testList2) {
			System.out.println(t2.toString());
		}
	}

测试结果
在这里插入图片描述

3、基于语句级别的配置

1、对于映射文件中的select、insert、update、delete:
flushCache属性
将其设置为true,无论什么时候被调用,都会导致缓存被清空。默认值是false。可以清空一级缓存和二级缓存
2、对于映射文件的select:
useCache属性:
如果设置为true,将会导致本条语句的结果被缓存。默认值是true。
称如果设置成false,可以阻止二级缓存的产生。不会阻止一级缓存的产生
3、全局配置:
cacheEnabled默认开启,设置为false会关闭全局范围内的二级缓存,优先级高于mapper中的


4、基于mapper级别的配置

cache的其他配置:
所有的这些属性都可以通过缓存元素的属性来修改。比如:

<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>

这个更高级的配置创建了一个FIFO缓存,并每隔60秒刷新,存数结果对象或列表的512个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。
eviction—可回收策略
可用的收回策略有:
. LRU – 最近最少使用的:移除最长时间不被使用的对象。
. FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
. SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
. WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是LRU。
flushInterval(刷新间隔) 可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式 的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目) 可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。
readOnly(只读) 属性可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。

嵌套查询

1、一对一和多对一

实质就是在一个表的实体类中加入另一个表实体类类型的属性,并在mapper映射中通过实现关联,当一个表查询数据时,另一个表通过所关联表的外键来进行查询自己的信息并将其置入主表的实体类中,从而实现嵌套查询。
示例如下:
java代码—添加对应的实体类及接口

//主实体类
class Test1 implements Serializable{
	
	private static final long serialVersionUID = 1L;
	
	private String id;
	private String name;
	private int age;
	private Test2 test2;
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public Test2 getTest2() {
		return test2;
	}
	public void setTest2(Test2 test2) {
		this.test2 = test2;
	}
	public static long getSerialversionuid() {
		return serialVersionUID;
	}
	@Override
	public String toString() {
		return "Test1 [id=" + id + ", name=" + name + ", age=" + age + ", test2=" + test2 + "]";
	}
}

//从类
class Test2 implements Serializable{
	
	private static final long serialVersionUID = 1L;
	
	private String id;
	private String username;
	private String address;
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	@Override
	public String toString() {
		return "Test2 [id=" + id + ", username=" + username + ", address=" + address + "]";
	}
	
}

interface Test1Mapper{
	/* 一对一查询 */
	Test1 selectTest1(String id);

	/* 多对一查询 */
	List<Test1> selectAllData();
}

interface Test2Mapper{
	Test2 selectTest2(String id);
}

//工具类
class MyBatisUtil {
	private static SqlSessionFactory sqlSessionFactory = null;
	static {
		 try {
			InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	private MyBatisUtil(){}
	public static SqlSession getSqlSession() {
		return sqlSessionFactory.openSession();
	}
}

修改主配置文件mybatis-config.xml–第二个对象加入相应别名及mapper扫描

<?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>

	<!-- 引入数据库链接配置 -->
	<properties resource="db.properties"></properties>
	
	<!-- 定义返回值类型别名BlogMapper.xml中的resultType 类型是pojo类-->
	<typeAliases>
		<typeAlias type="com.my.test.Test1" alias="Test1"/>
		<typeAlias type="com.my.test.Test2" alias="Test2"/>
		<!-- 也可以使用
				<package name="包" />
				会自动扫描此包下的所有类并使其类名为别名
		 -->
	</typeAliases>
	
	<!-- 引入数据源 -->
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC"></transactionManager>
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}"></property>
				<property name="url" value="${jdbc.url}"></property>
				<property name="username" value="${jdbc.username}"></property>
				<property name="password" value="${jdbc.password}"></property>
			</dataSource>
		</environment>
	</environments>
	
	<!-- 定义映射文件(将sql语句放入映射文件中) -->
	<mappers>
         <mapper resource="mapper/TestMapper.xml"/>
         <mapper resource="mapper/Test2Mapper.xml"/>
	</mappers>
</configuration>

Test1Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"  
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<mapper namespace="com.my.test.Test1Mapper">
	<cache />
	<resultMap type="Test1" id="TestResultMap">
		<id column="id" property="id" jdbcType="VARCHAR"></id>
		 <!-- 
           property是Test1实体类中加入的属性
           column指向相关的外键
           select指向mytest的查询方法
           -->
		<association property="test2" column="id" select="com.my.test.Test2Mapper.selectTest2">
		</association>
	</resultMap>
	
	<!-- 一对一查询 -->
	<select id="selectTest1" parameterType="string"  resultMap="TestResultMap">
		select*from test1 where id = #{id};
	</select>
	
	<!-- 多对一查询 -->
	<select id="selectAllData" resultMap="TestResultMap">
		select*from test1;
	</select>
	
</mapper>

Test2Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"  
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<mapper namespace="com.my.test.Test2Mapper">
	<cache />
	<resultMap type="Test2" id="TestResultMap">
		 <id column="id" property="id"/>
	</resultMap>
	
	<select id="selectTest2" parameterType="string" resultMap="TestResultMap">
          select*from test2 where id = #{id};
	</select> 
</mapper>

2、一对多

一个Test1对应多个Test2
实体类及接口

//主实体类
class Test1 implements Serializable{
	
	private static final long serialVersionUID = 1L;
	
	private String id;
	private String name;
	private int age;
	private List<Test2> test2List;
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public List<Test2> getTest2List() {
		return test2List;
	}
	public void setTest2List(List<Test2> test2List) {
		this.test2List = test2List;
	}
	@Override
	public String toString() {
		return "Test1 [id=" + id + ", name=" + name + ", age=" + age + ", test2List=" + test2List + "]";
	}
	
}

//从类
class Test2 implements Serializable{
	
	private static final long serialVersionUID = 1L;
	
	private String id;
	private String username;
	private String address;
	private String tid;
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public String getTid() {
		return tid;
	}
	public void setTid(String tid) {
		this.tid = tid;
	}
	@Override
	public String toString() {
		return "Test2 [id=" + id + ", username=" + username + ", address=" + address + ", tid=" + tid + "]";
	}
	
}

interface Test1Mapper{
	Test1 selectTest1(String id);
}

interface Test2Mapper{
	List<Test2> selectTest2(String tid);
}

Test1Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"  
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<mapper namespace="com.my.test.Test1Mapper">
	<cache />
	<resultMap type="Test1" id="TestResultMap">
		<!-- <id column="id" property="id" jdbcType="VARCHAR"></id> -->
		 <!-- 
           property是Test1实体类中加入的属性
           column指向相关的外键
           select指向mytest的查询方法
           -->
		<collection property="test2List" ofType="com.my.test.Test2" column="id"
		select="com.my.test.Test2Mapper.selectTest2"></collection>
	</resultMap>
	
	<select id="selectTest1" parameterType="string"  resultMap="TestResultMap">
		select*from test1 where id = #{id};
	</select>
	
</mapper>

Test2Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"  
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<mapper namespace="com.my.test.Test2Mapper">
	<cache />
	<resultMap type="Test2" id="TestResultMap">
		 <id column="tid" property="tid"/>
	</resultMap>
	
	<select id="selectTest2" parameterType="string" resultMap="TestResultMap">
          select*from test2 where tid = #{tid};
	</select> 
</mapper>

3、嵌套结果

执行流程:
1、先执行关联查询,一次性将所有数据都查询出来
2、再将所有查询出来的列组织成嵌套的结果对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值