1. 程式人生 > >mybatis 使用緩存策略

mybatis 使用緩存策略

jdbc 文件 這樣的 cto 設置 NPU ace 內存 cache

mybatis中默認開啟緩存

  1、mybatis中,默認是開啟緩存的,緩存的是一個statement對象。

不同情況下是否會使用緩存

  同一個SqlSession對象,重復調用同一個id的<select>(id必須相同)的時候,緩存才會生效,兩者缺一不可,而是會執行兩次sql,並不會使用緩存。

  下面舉一個例子,首先看一個PersonMapper.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">

<mapper namespace="lixin.gan.mapper.PersonMapper">

	<select id="selectAll1"  resultType="Person">
		select * from person
	</select>
	
	<select id="selectAll2"  resultType="Person">
		select * from person
	</select>

</mapper>

  針對上面這一個xml來進行測試

  不同SqlSession調用同一個id的方法

public class Test {
	public static void main(String[] args) throws IOException {
				
		InputStream config = Resources.getResourceAsStream("mybatis.xml");
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
		
		SqlSession session1 = factory.openSession();
		PersonMapper personMapper1 = session1.getMapper(PersonMapper.class);
		personMapper1.selectAll1();

		SqlSession session2 = factory.openSession();
		PersonMapper personMapper2 = session2.getMapper(PersonMapper.class);
		personMapper2.selectAll1();
	
	}

}

  上面這個代碼中,創建了兩個不同的sqlsession,雖然調用的是同一個id對應的方法,但是其實sql語句是執行了兩次,利用log4j打印的日誌如下:

org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==>  Preparing: select * from person 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==> Parameters: 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - <==      Total: 4
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==>  Preparing: select * from person 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==> Parameters: 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - <==      Total: 4

  

  同一個SqlSession調用不同id,但是sql相同的方法

public class Test {
	public static void main(String[] args) throws IOException {
				
		InputStream config = Resources.getResourceAsStream("mybatis.xml");
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
		
		SqlSession session1 = factory.openSession();
		PersonMapper personMapper1 = session1.getMapper(PersonMapper.class);
		personMapper1.selectAll1();
		personMapper1.selectAll2();
	}
}

  上面這個代碼,雖然創建的一個SqlSession,並且呢,使用同一個SqlSession去調用相同sql的方法,但是由於方法對應的id不同,一個是selectAll1,另一個是selectAll2,所以sql語句仍然會執行了兩次,利用log4j打印的日誌依舊依舊是:

org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==>  Preparing: select * from person 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==> Parameters: 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - <==      Total: 4
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==>  Preparing: select * from person 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==> Parameters: 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - <==      Total: 4

  

  同一個SqlSession,調用同一個id的方法。

public class Test {
	public static void main(String[] args) throws IOException {
				
		InputStream config = Resources.getResourceAsStream("mybatis.xml");
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
		
		SqlSession session1 = factory.openSession();
		PersonMapper personMapper1 = session1.getMapper(PersonMapper.class);
		personMapper1.selectAll1();
		personMapper1.selectAll1();
	}
}

  雖然上面這段代碼,進了兩次selectAll1()操作,但是因為是一個SqlSession,並且方法id相同,所以,會使用緩存,使用log4j打印的日誌顯示,只進行了一次查詢:

org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==>  Preparing: select * from person 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==> Parameters: 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - <==      Total: 4

  

如何做到不同SqlSession對調用同一個方法id,仍使用緩存

  前面已經看到了,使用緩存的兩個必要條件:1、同一個SqlSession;2、調用同一個id的方法。

  但是有時候,情況是這樣的,多個用戶請求某個數據,其實執行的sql都是一樣的,但是因為是不同用戶,所以,就會產生多個SqlSession,多個SqlSession調用同一個id的方法,仍然不會使用緩存。

  這個時候,其實是存在資源浪費的問題,既然他們需要的數據是一樣的,那麽何必再重復查詢一次呢,如果數據量很大,重復查詢一次,豈不是耗費更多的資源?

  mybatis提供了解決辦法---二級緩存。方法就是:修改mapper文件,增加cache標簽。

  前面說到的緩存其實一級緩存,另外還有二級緩存:

  1、一級緩存就是每一個SqlSession自己在內存中的那一段存儲空間。

  2、二級緩存是其實factory緩存,數據存在SqlSessionFactory中的。

  當一個SqlSession要取數據,會先查看一級緩存中是不是存在緩存,如果存在緩存,則直接從當前SqlSession的一級緩存中讀取數據。如果不存在緩存,那麽就會執行sql命令,去數據庫中查詢數據,並且將查詢出的數據,保存到當前SqlSession對應的一級緩存中。可以調用clearCache()來清除當前SqlSession的一級緩存數據。

  各個SqlSession的一級緩存是不能共享的。但是二級緩存是可以共享的,於是乎,可以將一級緩存中的數據,放到二級緩存中,然後其他SqlSession在查詢之前,首先到一級緩存中查看是否有數據,如果一級緩存有數據,就從一級緩存中取數據;如果一級緩存中沒有數據,如果在mapper.xml中設置了cache,那麽可以繼續查詢二級緩存,如果二級緩存中存在要查詢到數據,那麽就從二級緩存中讀取數據,否則就從數據庫中讀取數據。

  二級緩存共享數據的一個前提是:一個SqlSession調用close()之後,才會將一級緩存中的數據放到二級緩存中,如果不關閉SqlSession,數據只會存在於一級緩存中,數據不會發生共享。

  修改後的PersonMapper.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">

<mapper namespace="lixin.gan.mapper.PersonMapper">

	<cache readOnly="true"></cache>

	<select id="selectAll1"  resultType="Person">
		select * from person
	</select>
	
	<select id="selectAll2"  resultType="Person">
		select * from person
	</select>

</mapper>

  正確、正常的測試代碼

public class Test {
	public static void main(String[] args) throws IOException {
				
		InputStream config = Resources.getResourceAsStream("mybatis.xml");
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
		
		SqlSession session1 = factory.openSession();
		PersonMapper personMapper1 = session1.getMapper(PersonMapper.class);
		personMapper1.selectAll1();
		
		// 可以調用clearCache來清空當前SqlSession的緩存
		//session1.clearCache();
		
		// 需要將session關閉之後,才會將數據放進二級緩存,否則數據不會放進二級緩存
		session1.close();
		
		SqlSession session2 = factory.openSession();
		PersonMapper personMapper2 = session2.getMapper(PersonMapper.class);
		personMapper2.selectAll1();
		
	}
}

  log4j記錄的日誌如下:

org.apache.ibatis.cache.decorators.LoggingCache - line:62 - Cache Hit Ratio [lixin.gan.mapper.PersonMapper]: 0.0
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==>  Preparing: select * from person 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==> Parameters: 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - <==      Total: 4
org.apache.ibatis.cache.decorators.LoggingCache - line:62 - Cache Hit Ratio [lixin.gan.mapper.PersonMapper]: 0.5

  

  不關閉SqlSession,沒有發生共享的測試

public class Test {
	public static void main(String[] args) throws IOException {
				
		InputStream config = Resources.getResourceAsStream("mybatis.xml");
		SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
		
		SqlSession session1 = factory.openSession();
		PersonMapper personMapper1 = session1.getMapper(PersonMapper.class);
		personMapper1.selectAll1();
		
		// 可以調用clearCache來清空當前SqlSession的緩存
		//session1.clearCache();
		
		// 需要將session關閉之後,才會將數據放進二級緩存,否則數據不會放進二級緩存
		//session1.close();
		// 此處沒有關閉session1,所以數據不會放進二級緩存中
		
		SqlSession session2 = factory.openSession();
		PersonMapper personMapper2 = session2.getMapper(PersonMapper.class);
		personMapper2.selectAll1();
		
	}
}

  log4j打印的日誌很清楚的記錄了上面的代碼,執行了兩次sql,因為數據沒有放進二級緩存,在session2在一級緩存和二級緩存中都沒有發現緩存,所以也要執行一次sql。

org.apache.ibatis.cache.decorators.LoggingCache - line:62 - Cache Hit Ratio [lixin.gan.mapper.PersonMapper]: 0.0
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==>  Preparing: select * from person 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==> Parameters: 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - <==      Total: 4
org.apache.ibatis.cache.decorators.LoggingCache - line:62 - Cache Hit Ratio [lixin.gan.mapper.PersonMapper]: 0.0
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==>  Preparing: select * from person 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - ==> Parameters: 
org.apache.ibatis.logging.jdbc.BaseJdbcLogger - line:139 - <==      Total: 4

  

mybatis 使用緩存策略