mybatis 使用緩存策略
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 使用緩存策略