手寫mybatis框架

超人小冰發表於2020-09-05

前言

很久沒有更新mybatis的原始碼解析了,因為最近在將自己所理解的mybatis思想轉為實踐。 在學習mybatis的原始碼過程中,根據mybatis的思想自己構建了一個ORM框架 。整個程式碼都是自己手動構造,沒有一句程式碼是Copy,肯定不如谷歌大神那樣的程式碼,但已基本實現了SQL語句的實現和物件關係對映功能。若對原始碼感興趣,可加入我一起寫這個專案。後文會附帶地址,若覺得不錯,希望手動star下哦!

專案地址:https://github.com/xbcrh/simple-ibatis

框架介紹

simple-batis是自己編寫的一個簡單ORM框架。在學習mybatis原始碼時,有感而發。耗時3周左右,基本滿足了一些常用的SQL操作本專案所涉及的程式碼都是個人所寫,沒有一句copy,肯定不是很完善,大家理解下,後續有時間會一直更新。如果你對原始碼感興趣,也可以加入一起,將自己的理解轉為程式碼真的會加深印象。

框架程式碼簡單梳理

註釋 com.simple.ibatis.annotation

@Dao
標註在mapper類上。標誌著該類是一個mapper類,在解析時會進行解析

@Dao
public interface App1 {
}

@Select

標註在mapper類中的方法上。標誌著該方法是一個Select方法,並在Select方法內部寫具體的sql語句。對於有引數注入的情況,引數使用{}進行代替

@Select("SELECT name from sys_user where name = {user.name} and id = {id}")
List<String> test1(User user, int id);

@Param

標註在mapper類中的方法引數上。對引數名進行一次重新命名。若不使用此註釋,會預設按照引數名當做注入的元素

@Select("SELECT name from sys_user where id = {userId}")
List<String> test2(@Param("userId") int id);

@Update

標註在mapper類中的方法上。標誌著該方法是一個Update方法

@Update("update sys_user set name = {user.name} where id = {user.id}")
void update3(User user);

@Insert

標註在mapper類中的方法上。標註著該方法是一個Insert方法

@Insert("insert into sys_user(id,name) values ({user.id},{user.name})")
int insert4(@Param("user") User user);

@Delete

標註在mapper類中的方法上。標註著該方法是一個Delete方法

@Delete("delete from sys_user where id = {user.id}")
int delete5(@Param("user") User user);

資料庫註冊 com.simple.ibatis.driver

DriverRegister 提供資料庫註冊功能。未避免重複註冊,內部使用了一個快取

資料來源 com.simple.ibatis.datasource

NormalDataSource 普通資料來源,沒有池化的功能,提供獲取資料庫連線的功能
PoolDataSource 池化資料來源,存放著活躍連線列表和空閒連線列表。並對獲取連線和釋放連線做了一系列操作
PoolConnection 連線的包裝類,除了存放真實連線外,還存放此連線被獲取時間,用於判斷連線是否超時

核心類 com.simple.ibatis.core

Config 全域性核心類,存放資料來源,mapper包地址,mapper類解析檔案
MapperCore mapper類解析檔案
SqlSource 具體的sql語句封裝

代理類 com.simple.ibatis.mapping

MapperProxy mapper介面代理類。使用動態代理技術

執行器類 com.simple.ibatis.execute

Executor 執行器介面
SimpleExecutor 具體執行器,執行具體的sql方法。生成結果
ExecutorFactory 生成Executor的工廠類

反射類 com.simple.ibatis.reflect

ClassWrapper 類加強器,封裝了Object的get和set方法。
ObjectWrapper 物件包裝類。呼叫ObjectWrapper.setVal和getVal就可以設定和獲得屬性。不需要顯示的呼叫物件的getxxx和setxxx方法。
ObjectWrapperFactory 物件包裝類生成器

處理器類 com.simple.ibatis.statement

PreparedStatementHandle PreparedStatement生成器。將java屬性轉為jdbc屬性並注入。
ResultSetHandle 對查詢結構ResultSet進行解析,轉換為Java型別

工具類 com.simple.ibatis.util

PackageUti 解析包的工具類
TypeUtil 型別判斷的工具類

具體程式碼實踐

1.  程式碼執行預設在java8上,因為用到了引數反射,所以在idea中記得開啟parameters;

File->Settings->Build,Execution,Deployment->Compiler->Java Compiler
在 Additional command line parameters: 後面填上 -parameters

 

2. 構建pojo檔案(記得在資料庫也建立一張sys_user表)

public class User {
private int id;

private String name;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

2. 構建mapper檔案

在mapper檔案上加@Dao註解,併為每個方法編寫Sql語句,如下所示。目前僅支援註解方式配置SQL,後續會繼續完善使其支援.xml檔案。

@Dao
public interface App1 {

@Select("SELECT * from sys_user")
List<User> select2();

@Select("SELECT name from sys_user where name = {user.name} and id = {id}")
List<String> select3(User user, @Param("id") int id);

@Update("update sys_user set name = {user.name} where id = {user.id}")
void update4(User user);

@Insert("insert into sys_user(id,name) values ({user.id},{user.name}) ")
int insert5(@Param("user") User user);
}

3. 編寫測試類

public class ExecutorTest {

    @Test
    public void shouldConnect(){
        /**構建資料來源,使用時下面程式碼可設定為全域性變數,只載入一次*/
        PoolDataSource poolDataSource = new PoolDataSource("com.mysql.jdbc.Driver","jdbc:mysql://101.132.150.75:3306/our-auth","root","root");
        ExecutorFactory executorFactory = new ExecutorFactory("com/simple/ibatis/mapper",poolDataSource);
       
        /**拿到具體的mapper代理類*/
        SimpleExecutor executor = executorFactory.getExecutor();
        App1 app1 = executor.getMapper(App1.class);

        /**構建查詢條件*/
        User user = new User();
        user.setName("xiabing");
        user.setId(1);
        
        /**呼叫插入方法*/
        int count = app1.insert5(user);
        
        /**呼叫更新方法*/
        user.setName("root");
        app1.update4(user);
        
        /**查詢使用者名稱,返回字元*/
        List<String> users = app1. select3(user,3);
        System.out.println(users.get(0));
       
       /**查詢使用者,返回物件*/
       List<User> userLists = app1.select2();
       System.out.println(userLists.get(0).getName());
    
    }
}                                    

最終結果

 

 框架待完善

1.  目前該框架僅支援註解注入SQL語句,不支援XML注入SQL語句

2. 目前物件屬性不支援集合類,物件中也不支援巢狀非基本資料型別

3. 框架中沒有加入快取

4. 解析mapper檔案僅支援一個包下的mapper檔案

結語

正如自己所說,將自己思路變為實踐。該框架程式碼不完善地方很多,但在寫的過程中,對mybatis的原理也熟悉了。此框架是我學習的路上的產物。希望小夥伴可以支援下,對原始碼感興趣的或想學習mybatis原始碼的夥伴可以評論下,和我一起寫好這個框架,一起開源(學習路上一起加油)

相關文章