手寫mybatis框架-增長緩存&事務功能

前言

在學習mybatis源碼之餘,本身完成了一個簡單的ORM框架。已完成基本SQL的執行和對象關係映射。本週在此基礎上,又加入了緩存和事務功能。全部代碼都沒有copy,若是也對此感興趣,請賞個Star。html

項目地址:simple-ibatisjava

第一版博文地址:博客園博文mysql

增長代碼詳解

緩存 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);
}

自定義框架緩存接口,提供基本的增刪改查功能。git

緩存基本實現類-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的封裝,實現了基本的緩存獲取,刪除,清除等功能。github

具有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緩存介紹sql

項目代碼測試

@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一系列操做的事務控制。mybatis

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測試。app

總結:

該項目屬於我抱着學習的心態去作的項目,將Mybatis源碼一步步拆解,在實踐中去領悟其強大的地方。這次在已有的基礎上增長了緩存和事務的功能。又是一次學習之旅。由於代碼全手寫,沒有COPY任何一句代碼,不是很完善,請見諒。若是覺的感興趣,請給我個star支持下。由於本身想一直去維護這個項目,若是你也感興趣,能夠私聊我和我一塊兒作下去,一塊兒寫好這個開源項目。最後,真心求Star了 --------

項目地址:simple-ibatis

相關文章
相關標籤/搜索