手写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-ibatis