zoukankan      html  css  js  c++  java
  • 手写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

  • 相关阅读:
    C#调试信息打印到输出窗口
    C#拼接SQL中in条件
    从图像到知识:深度神经网络实现图像理解的原理解析
    Cocoa Touch(六):App运行机制 NSRunLoop, KVC, KVO, Notification, ARC
    Cocoa Touch(五):网络请求 NSURLSession/AFNetworking, GCD, NSURLResquest
    JQuery:选择器、动画、AJAX请求
    Cocoa Touch(四): 多线程GCD, NSObject, NSThread, NSOperationQueue
    Socket、RPC通信实例,简单版本,仅供查阅
    Cocoa Touch(三):图形界面UIKit、Core Animation、Core Graphics
    Cocoa Touch(二):数据存储CoreData, NSKeyArchiver, NSOutputStream, NSUserDefaults
  • 原文地址:https://www.cnblogs.com/xiaobingblog/p/13647690.html
Copyright © 2011-2022 走看看