手寫mybatis框架-增加快取&事務功能
前言
在學習mybatis原始碼之餘,自己完成了一個簡單的ORM框架。已完成基本SQL的執行和物件關係對映。本週在此基礎上,又加入了快取和事務功能。所有程式碼都沒有copy,如果也對此感興趣,請賞個Star。
專案地址:simple-ibatis
初版博文地址:部落格園博文
增加程式碼詳解
快取 com.simple.ibatis.cache
快取介面-Cache
public interface Cache { /**放入快取*/ void putCache(String key,Object val); /**獲取快取*/ Object getCache(String key); /**清空快取*/ void cleanCache(); /**獲取快取健數量*/ int getSize(); /**移除key的快取*/ void removeCache(String key); }
自定義框架快取介面,提供基本的增刪改查功能。
快取基本實現類-SimpleCache
public class SimpleCache implements Cache{ // 內部使用HashMap作為快取實現 private static Map<String,Object> map = new HashMap<>(); // 呼叫map.put()方法實現存快取功能 @Override public void putCache(String key, Object val) { map.put(key,val); } // 呼叫map.get()方法實現取快取功能 @Override public Object getCache(String key) { return map.get(key); } // 呼叫map.clear()方法實現清空快取功能 @Override public void cleanCache() { map.clear(); } // 呼叫map.size()方法獲取快取數量 @Override public int getSize() { return map.size(); } // 呼叫map.remove()方法移除快取 @Override public void removeCache(String key) { map.remove(key); } }
simple-ibatis完成對HasaMap的封裝,實現了基本的快取獲取,刪除,清除等功能。
具備LRU淘汰策略-LruCache
/** * @author xiabing * @description: 快取包裝類,具備Lru淘汰策略功能 */ public class LruCache implements Cache{ // 預設快取數 private static Integer cacheSize = 100; // 負載因子 private static Float loadFactory = 0.75F; // 真實快取 private Cache trueCache; // 重寫LinkedHashMap方法實現Lru功能 private Map<String,Object> linkedCache; // 待移除的快取元素 private static Map.Entry removeEntry; public LruCache(Cache trueCache){ this(cacheSize,loadFactory,trueCache); } public LruCache(Integer cacheSize, Float loadFactory, Cache trueCache) { this.cacheSize = cacheSize; this.loadFactory = loadFactory; this.trueCache = trueCache; this.linkedCache = new LinkedHashMap<String, Object>(cacheSize,loadFactory,true){ @Override // 當快取數大於設定的預設快取數時,linkedHashMap會淘汰最近最少使用的元素,獲取此元素,在真實快取中淘汰即可 protected boolean removeEldestEntry(Map.Entry eldest) { if(getSize() > cacheSize){ removeEntry = eldest; return true; } return false; } }; } @Override public void putCache(String key, Object val) { this.trueCache.putCache(key,val); this.linkedCache.put(key,val); if(removeEntry != null){ // 若找到了最近最少元素,則進行移除 removeCache((String)removeEntry.getKey()); removeEntry = null; } } @Override public Object getCache(String key) { // linkedCache獲取的意義是觸發linkedHashMap元素排序 linkedCache.get(key); return trueCache.getCache(key); } @Override public void cleanCache() { trueCache.cleanCache(); linkedCache.clear(); } @Override public int getSize() { return trueCache.getSize(); } @Override public void removeCache(String key) { trueCache.removeCache(key); } }
LruCache是根據LinkedHashMap的特性來實現,若對此有疑問,可參考mybatis關於LruCache功能的實現 - mybatis快取介紹
專案程式碼測試
@Test // 測試快取獲取 public void shouldGetCache() throws SQLException { // 初始化連線池 PoolDataSource poolDataSource = new PoolDataSource("com.mysql.jdbc.Driver","jdbc:mysql://101.132.150.75:3306/our-auth","root","root"); Config config = new Config("com/simple/ibatis/mapper",poolDataSource); // 設定全域性配置,開啟快取 config.setOpenCache(true); // 獲取執行器 Executor simpleExecutor = config.getExecutor(); UserMapper userMapper = simpleExecutor.getMapper(UserMapper.class); User user = new User(); user.setId(1); user.setName("root"); // 第一次呼叫 List<User> userList = userMapper.getUsers(user); // 第二次呼叫,我在原始碼中有列印輸出,若使用了快取,則列印語句 List<User> userList1 = userMapper.getUsers(user); simpleExecutor.close(); }
結果列印如下 this is cache .感興趣的可以自己試下
cache我設定了全域性可配置,預設生成的是LruCache。並在更新,修改,刪除的SQL操作前強制重新整理快取。詳細程式碼邏輯見專案中SimpleExecutor類。
事務功能com.simple.ibatis.transaction
事務介面-Transaction
/** * @Author xiabing * @Desc 增加事務功能 **/ public interface Transaction { /**獲取連結*/ Connection getConnection() throws SQLException; /**提交*/ void commit() throws SQLException; /**回滾*/ void rollback() throws SQLException; /**關閉*/ void close() throws SQLException; }
JDBC事務-SimpleTransaction
package com.simple.ibatis.transaction; import com.simple.ibatis.datasource.PoolDataSource; import java.sql.Connection; import java.sql.SQLException; /** * @Author xiabing * @Desc 事務的簡單實現 **/ public class SimpleTransaction implements Transaction{ private Connection connection; // 資料庫連線 private PoolDataSource dataSource; // 資料來源 private Integer level = Connection.TRANSACTION_REPEATABLE_READ;; // 事務隔離級別 private Boolean autoCommmit = true; // 是否自動提交 public SimpleTransaction(PoolDataSource dataSource){ this(dataSource,null,null); } public SimpleTransaction(PoolDataSource dataSource, Integer level, Boolean autoCommmit) { this.dataSource = dataSource; if(level != null){ this.level = level; } if(autoCommmit != null){ this.autoCommmit = autoCommmit; } } @Override public Connection getConnection() throws SQLException{ this.connection = dataSource.getConnection(); this.connection.setAutoCommit(autoCommmit); this.connection.setTransactionIsolation(level); return this.connection; } @Override public void commit() throws SQLException{ if(this.connection != null){ this.connection.commit(); } } @Override public void rollback() throws SQLException{ if(this.connection != null){ this.connection.rollback(); } } /**關閉連結前,若設定了自動提交為false,則必須進行回滾操作*/ @Override public void close() throws SQLException{ if(!autoCommmit && connection != null){ connection.rollback(); } /**放回連線池*/ if(connection != null){ dataSource.removeConnection(connection); } /**連結設為null*/ this.connection = null; } }
simpleTransaction主要將事務管理功能交給了資料庫本身(即connection),事務隔離級別默然是mysql的事務隔離級別。通過對Connection的管理,進而實現對connection一系列操作的事務控制。
Test public void shouldOpenTransaction() { /**基本配置*/ PoolDataSource poolDataSource = new PoolDataSource("com.mysql.jdbc.Driver","jdbc:mysql://101.132.150.75:3306/our-auth","root","root"); Config config = new Config("com/simple/ibatis/mapper",poolDataSource); /**設定為啟用事務,關閉自動提交*/ config.setOpenTransaction(true); /**獲取執行器*/ Executor simpleExecutor = config.getExecutor(); UserMapper userMapper = simpleExecutor.getMapper(UserMapper.class); User user = new User(); user.setId(1); user.setName("xiabing"); /**更新名字為xiabing,但未提交*/ userMapper.update(user); User user1 = userMapper.getUserById(1); /**獲取ID為1的名字,為root,說明上文的語句還沒有提交*/ System.out.println(user1.getName()); /**事務提交語句*/ //simpleExecutor.commit(); }
若不提交事物,即執行 simpleExecutor.commit()語句,更新語句將不會自動提交到資料庫。上述程式碼在github專案中Test類中shouldOpenTransaction()方法上,可自行debug測試。
總結:
該專案屬於我抱著學習的心態去做的專案,將Mybatis原始碼一步步拆解,在實踐中去領悟其強大的地方。此次在已有的基礎上增加了快取和事務的功能。又是一次學習之旅。因為程式碼全手寫,沒有COPY任何一句程式碼,不是很完善,請見諒。如果覺的感興趣,請給我個star支援下。因為自己想一直去維護這個專案,如果你也感興趣,可以私聊我和我一起做下去,一起寫好這個開源專案。最後,真心求Star了 --------
專案地址:simple-ib