手写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
手写mybatis框架-增加缓存&事务功能的更多相关文章
- 要想精通Mybatis?从手写Mybatis框架开始吧!
1.Mybatis组成 动态SQL Config配置 Mapper配置 2.核心源码分析 Configuration源码解析 SqlSessionFactory源码解析 SqlSession源码解析 ...
- 手写mybatis框架笔记
MyBatis 手写MyBatis流程 架构流程图 封装数据 封装到Configuration中 1.封装全局配置文件,包含数据库连接信息和mappers信息 2.封装*mapper.xml映射文件 ...
- 手写mybatis框架
前言 很久没有更新mybatis的源码解析了,因为最近在将自己所理解的mybatis思想转为实践. 在学习mybatis的源码过程中,根据mybatis的思想自己构建了一个ORM框架 .整个代码都是自 ...
- 手写MyBatis ORM框架实践
一.实现手写Mybatis三个难点 1.接口既然不能被实例化?那么我们是怎么实现能够调用的? 2.参数如何和sql绑定 3.返回结果 下面是Mybatis接口 二.Demo实现 1.创建Maven工程 ...
- 重学 Java 设计模式:实战中介者模式「按照Mybaits原理手写ORM框架,给JDBC方式操作数据库增加中介者场景」
作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 同龄人的差距是从什么时候拉开的 同样的幼儿园.同样的小学.一样 ...
- 带码农《手写Mybatis》进度3:实现映射器的注册和使用
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获!
- 纯手写Myatis框架
1.接口层-和数据库交互的方式 MyBatis和数据库的交互有两种方式: 使用传统的MyBatis提供的API: 使用Mapper接口: 2.使用Mapper接口 MyBatis 将配置文件中的每一个 ...
- 手写DAO框架(一)-从“1”开始
背景: 很久(4年)之前写了一个DAO框架-zxdata(https://github.com/shuimutong/zxdata),这是我写的第一个框架.因为没有使用文档,我现在如果要用的话,得从头 ...
- 手写MQ框架(二)-服务端实现
一.起航 书接上文->手写MQ框架(一)-准备启程 本着从无到有,从有到优的原则,所以计划先通过web实现功能,然后再优化改写为socket的形式. 1.关于技术选型 web框架使用了之前写的g ...
随机推荐
- Python学习笔记之 Python设计思想&设计原则
Python设计思想&设计原则 设计思想 1.封装 数据角度 多种数据合为一种数据 优势:代码可读性高 将数据与行为相关联 例如:电脑(内存,储存空间,...) 行为角度 ...
- HTML基础-05
字体 文本颜色:color:red;字体分类: 衬线字体serif --字体宽度各异,有衬线 --Times.Georgia.宋体 无衬线字体sans-serif --字体宽度各异,无衬线 --Hel ...
- 精讲RestTemplate第8篇-请求失败自动重试机制
本文是精讲RestTemplate第8篇,前篇的blog访问地址如下: 精讲RestTemplate第1篇-在Spring或非Spring环境下如何使用 精讲RestTemplate第2篇-多种底层H ...
- 图论算法(一)存图与STL第六弹——vector容器
图论算法(一)存图 我发现我的博客阅读量贼低,问小伙伴们,ta们都说这些博客太长了QAQ! 今天来个短亿点的(也短不了多少……) 进入正题,图论究竟是什么? 图论就是给你一张图,让你在这张图上进行各种 ...
- mysql无法远程连接问题(ERROR 1045 (28000): Access denied for user 'root')
mysql版本 : 8.0.21 使用mysql 作为nextcloud的数据库.之前使用挺正常的,因为被黑客勒索过一次,重新启动了一个mysql的docker镜像. 结果数据库配置老是失败,next ...
- Go 切片的一种有趣内存泄漏方式
今天我在看 Prashant Varanasi 的 Go 发布会演讲:使用火焰图进行生产分析(Analyzing production using Flamegraphs),在演讲开始的第 28 分钟 ...
- 要有一颗理财的心 - 读<富爸爸.穷爸爸>
记得工作没多久后的一次加薪的例行谈话.部门经理和我说,不能靠工资过日子,要多想想怎么投资,我的主要财富就是靠投资赚来的.当时第一反应,老板,你不给我加薪找这借口也太牵强了吧.我的收入只有工资,我的工资 ...
- YouCompleteMe unavailable: requires Vim compiled with Python 2.x support
问题:YouCompleteMe unavailable: requires Vim compiled with Python 2.x support 解决:重新编译,加入--enable-pytho ...
- 《p5.js创意游戏编程》第一课:跳动的小球
准备:Hbuilder/vscode等可以编写网页的编辑器 如果想立刻上手也可以使用在线编译器p5.js官方在线编辑器,如果打不开也可以使用国内的一款在线编辑器jsrun编辑器,(第一课先使用jsru ...
- 个人项目 源程序特征统计程序(C++)
零.GitHub地址 https://github.com/King-Authur/Word-count 一.项目的相关要求 wc.exe 是一个常见的工具,它能统计文本文件的字符数.单词数和行数.这 ...