前言

在学习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框架-增加缓存&事务功能的更多相关文章

  1. 要想精通Mybatis?从手写Mybatis框架开始吧!

    1.Mybatis组成 动态SQL Config配置 Mapper配置 2.核心源码分析 Configuration源码解析 SqlSessionFactory源码解析 SqlSession源码解析 ...

  2. 手写mybatis框架笔记

    MyBatis 手写MyBatis流程 架构流程图 封装数据 封装到Configuration中 1.封装全局配置文件,包含数据库连接信息和mappers信息 2.封装*mapper.xml映射文件 ...

  3. 手写mybatis框架

    前言 很久没有更新mybatis的源码解析了,因为最近在将自己所理解的mybatis思想转为实践. 在学习mybatis的源码过程中,根据mybatis的思想自己构建了一个ORM框架 .整个代码都是自 ...

  4. 手写MyBatis ORM框架实践

    一.实现手写Mybatis三个难点 1.接口既然不能被实例化?那么我们是怎么实现能够调用的? 2.参数如何和sql绑定 3.返回结果 下面是Mybatis接口 二.Demo实现 1.创建Maven工程 ...

  5. 重学 Java 设计模式:实战中介者模式「按照Mybaits原理手写ORM框架,给JDBC方式操作数据库增加中介者场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 同龄人的差距是从什么时候拉开的 同样的幼儿园.同样的小学.一样 ...

  6. 带码农《手写Mybatis》进度3:实现映射器的注册和使用

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获!

  7. 纯手写Myatis框架

    1.接口层-和数据库交互的方式 MyBatis和数据库的交互有两种方式: 使用传统的MyBatis提供的API: 使用Mapper接口: 2.使用Mapper接口 MyBatis 将配置文件中的每一个 ...

  8. 手写DAO框架(一)-从“1”开始

    背景: 很久(4年)之前写了一个DAO框架-zxdata(https://github.com/shuimutong/zxdata),这是我写的第一个框架.因为没有使用文档,我现在如果要用的话,得从头 ...

  9. 手写MQ框架(二)-服务端实现

    一.起航 书接上文->手写MQ框架(一)-准备启程 本着从无到有,从有到优的原则,所以计划先通过web实现功能,然后再优化改写为socket的形式. 1.关于技术选型 web框架使用了之前写的g ...

随机推荐

  1. .NetCore(Avalonia) 项目dll混淆,Ubuntu 或者deepin操作系统 deb安装包解压,重新打包

    .NetCore(Avalonia) 项目dll混淆,deb安装包解压,重新打包 本文分为两部分,一部分是介绍使用 DotNetReactor6.0 及以上版本混淆.netcore项目的dll. 另一 ...

  2. for循环的插入元素

    Scanner input = new Scanner(System.in);  int[] num = new int[5];  for (int i = 0; i < num.length; ...

  3. 三张图理解JavaScript原型链

  4. python3中文输出乱码的问题

    最近使用you-get这个工具下载视频,发现命令行窗口里显示的媒体标题是乱码(但文件管理器里显示正常).我的命令行窗口的code page是936,sys.stdout.encoding是utf-8, ...

  5. 封装Vue Element的table表格组件

    上周分享了几篇关于React组件封装方面的博文,这周就来分享几篇关于Vue组件封装方面的博文,也好让大家能更好地了解React和Vue在组件封装方面的区别. 在封装Vue组件时,我依旧会交叉使用函数式 ...

  6. Scala中做简易wordCount

    使用foldLeft函数,实现简易的wordCount import scala.collection.mutable object Demo_019 { def main(args: Array[S ...

  7. 分布式数据库中间件 MyCat | 分库分表实践

    MyCat 简介 MyCat 是一个功能强大的分布式数据库中间件,是一个实现了 MySQL 协议的 Server,前端人员可以把它看做是一个数据库代理中间件,用 MySQL 客户端工具和命令行访问:而 ...

  8. Jmeter 常用函数(2)- 详解 __RandomDate

    如果你想查看更多 Jmeter 常用函数可以在这篇文章找找哦 https://www.cnblogs.com/poloyy/p/13291704.html 作用 产生一个随机日期 语法格式 ${__R ...

  9. 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 ...

  10. js 常用业务工具方法 (es5,es6)持续更新

    数组去重 数组去重最原始的方法就是使用双层循环. es5: // 使用indexOf function unique(array) { var res = []; for (var i = 0, le ...