手写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 ...
随机推荐
- 2020 重新出发,JAVA 学习计划
------ @[toc]# 前言 我呢已经工作七年了,一直没有换工作,因为我这个人没什么太大的野心,安安稳稳的生活就挺好,目前的公司虽然福利一般,但是工作稳定,环境也都很熟悉了. 但是今年,到目前为 ...
- 伸展树(Splay)学习笔记
二叉排序树能够支持多种动态集合操作,它可以被用来表示有序集合,建立索引或优先队列等.因此,在信息学竞赛中,二叉排序树应用非常广泛. 作用于二叉排序树上的基本操作,其时间复杂度均与树的高度成正比,对于一 ...
- Vue 大量data及rules的data选项结构组织
如果Vue文件需要很多的data成员及表单验证,建议使用类似结构 export default{ data(){ const model = { username: 'suzhen', passwor ...
- [持续更新]——关于C++的一些可能会常用的函数
写在前面 这些函数都是我和朋友一点一点写出来的,可能部分代码会有点雷同,但大部分代码都是自我总结出来的.目前包含的函数功能分别是: 1.设置控制台颜色 2.设置控制台光标位置 3.隐藏控制台光标 4. ...
- css中关于:nth-child()和:nth-of-type()的深入理解
css中关于:nth-child()和:nth-of-type()的深入理解 在css中有这样一类属性,是以:nth-开头的,其中最常见的就是:nth-child() 和 :nth-of-type() ...
- 【支付宝SDK】沙箱调试,以及遇到的坑
from rest_framework.views import APIView from alipay import AliPay, DCAliPay, ISVAliPay from django. ...
- 使用CrashHandler获取应用crash信息
Android应用不可避免会发生crash,也称之为崩溃.发生原因可能是由于Android系统底层的bug,也可能是由于不充分的机型适配或者是糟糕的网络情况.当crash发生时,系统会kill掉正 ...
- ES6中的变量的解构赋值, 解放我们的双手,实现变量的批量赋值
ES6--变量的解构赋值 引言 正文 一.数组的解构赋值 解构失败 不完全解构 默认值 二.对象的解构赋值 三.字符串的解构赋值 结束语 引言 变量的解构赋值, 听起来很复杂, 简单点说可以理解成批量 ...
- Go 中的内联优化
文讨论 Go 编译器是如何实现内联的以及这种优化方法如何影响你的 Go 代码. 请注意:本文重点讨论 gc,实际上是 golang.org 的 Go 编译器.讨论到的概念可以广泛用于其他 Go 编译器 ...
- 开发过程中遇到的-npm加载进node_modules里的文件怎么修改
来源:https://juejin.im/post/5ec381215188256d776342cd https://mp.weixin.qq.com/s?__biz=MzUzNjk5MTE1OQ== ...