手写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 ...
随机推荐
- .NetCore(Avalonia) 项目dll混淆,Ubuntu 或者deepin操作系统 deb安装包解压,重新打包
.NetCore(Avalonia) 项目dll混淆,deb安装包解压,重新打包 本文分为两部分,一部分是介绍使用 DotNetReactor6.0 及以上版本混淆.netcore项目的dll. 另一 ...
- for循环的插入元素
Scanner input = new Scanner(System.in); int[] num = new int[5]; for (int i = 0; i < num.length; ...
- 三张图理解JavaScript原型链
- python3中文输出乱码的问题
最近使用you-get这个工具下载视频,发现命令行窗口里显示的媒体标题是乱码(但文件管理器里显示正常).我的命令行窗口的code page是936,sys.stdout.encoding是utf-8, ...
- 封装Vue Element的table表格组件
上周分享了几篇关于React组件封装方面的博文,这周就来分享几篇关于Vue组件封装方面的博文,也好让大家能更好地了解React和Vue在组件封装方面的区别. 在封装Vue组件时,我依旧会交叉使用函数式 ...
- Scala中做简易wordCount
使用foldLeft函数,实现简易的wordCount import scala.collection.mutable object Demo_019 { def main(args: Array[S ...
- 分布式数据库中间件 MyCat | 分库分表实践
MyCat 简介 MyCat 是一个功能强大的分布式数据库中间件,是一个实现了 MySQL 协议的 Server,前端人员可以把它看做是一个数据库代理中间件,用 MySQL 客户端工具和命令行访问:而 ...
- Jmeter 常用函数(2)- 详解 __RandomDate
如果你想查看更多 Jmeter 常用函数可以在这篇文章找找哦 https://www.cnblogs.com/poloyy/p/13291704.html 作用 产生一个随机日期 语法格式 ${__R ...
- C++ Templates (2.1 类模板Stack的实现 Implementation of Class Template Stack)
返回完整目录 目录 2.1 类模板Stack的实现 Implementation of Class Template Stack 2.1.1 声明类模板 Declaration of Class Te ...
- js 常用业务工具方法 (es5,es6)持续更新
数组去重 数组去重最原始的方法就是使用双层循环. es5: // 使用indexOf function unique(array) { var res = []; for (var i = 0, le ...