当前位置:网站首页>很多小夥伴不太了解ORM框架的底層原理,這不,冰河帶你10分鐘手擼一個極簡版ORM框架(趕快收藏吧)

很多小夥伴不太了解ORM框架的底層原理,這不,冰河帶你10分鐘手擼一個極簡版ORM框架(趕快收藏吧)

2022-07-08 01:55:00 冰 河

大家好,我是冰河~~

最近很多小夥伴對ORM框架的實現很感興趣,不少讀者在冰河的微信上問:冰河,你知道ORM框架是如何實現的嗎?比如像MyBatis和Hibernate這種ORM框架,它們是如何實現的呢?

為了能够讓小夥伴們更加深刻並且清晰的理解ORM框架的實現原理,冰河决定自己手擼一個極簡版的ORM框架,讓小夥伴們一看就能够明白什麼是ORM框架?ORM框架到底是如何運行的?ORM框架是如何將程序對象與數據庫中的數據進行映射的?不過,在正式開始手擼ORM框架之前,我們要先來搞清楚什麼是ORM框架。

什麼是ORM框架?

ORM全稱為:Object Relational Mapping,翻譯成中文就是:對象關系映射。也就是說ORM框架就是對象關系映射框架,它通過元數據描述對象與關系映射的細節,ORM框架在運行的時候,可以根據對應與映射之間的關系將數據持久化到數據庫中。

其實,從本質上講,ORM框架主要實現的是程序對象到關系數據庫數據的映射。說的直白點:ORM框架就是將實體和實體與實體之間的關系,轉化為對應的SQL語句,通過SQL語句操作數據庫,將數據持久化到數據庫中,並且對數據進行相應的增删改查操作。

最常用的幾種ORM框架為:MyBatis、Hibernate和JFinal。

手擼ORM框架

這裏,我們模擬的是手擼Hibernate框架實現ORM,小夥伴們也可以模擬其他的ORM框架實現,核心原理都是相通的。如果大家在模擬其他框架手擼實現ORM時,遇到問題的話,都可以私聊我溝通,我看到的話,會第一時間回複大家。

好了,說幹就幹,我們開始吧。

@Table注解的實現

首先,我們創建一個io.mykit.annotation.jdk.db.provider Java包,在這個Java包創建一個@Table注解,@Table注解標注在Java類上,錶示當前類會被映射到數據庫中的哪張數據錶上,如下所示。

package io.mykit.annotation.jdk.db.provider;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** * 自定義Table注解 * @author binghe * */
@Inherited
@Target({
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Table {
    
 String value() default "";
}

@Column注解的實現

同樣的,在io.mykit.annotation.jdk.db.provider包下創建一個@Column注解,@Column注解標注在類中的字段上,錶示當前類中的字段映射到數據錶中的哪個字段上,如下所示。

package io.mykit.annotation.jdk.db.provider;
 
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
/** * 自定義Column注解 * @author binghe * */
@Inherited
@Target({
    ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
    
 String value() default "";
}

看到這裏,不管是使用過MyBatis的小夥伴還是使用過Hibernate的小夥伴,應該都會有所體會吧?沒錯,@Table注解和@Column注解,不管是在MyBatis框架還是Hibernate框架中,都會被使用到。這裏,我們在收錄極簡版ORM框架時,也使用了這兩個經典的注解。

創建實體類

io.mykit.annotation.jdk.db.provider.entity包下創建實體類User,並且@Table注解和@Column注解會被分別標注在User類上和User類中的字段上,將其映射到數據庫中的數據錶和數據錶中的字段上,如下所示。

package io.mykit.annotation.jdk.db.provider.entity;
 
import io.mykit.annotation.jdk.db.provider.Column;
import io.mykit.annotation.jdk.db.provider.Table;
 
/** * 自定義使用注解的實體 * @author binghe * */
@Table("t_user")
public class User implements Serializable{
    
 
 @Column("id")
 private String id;
 
 @Column("name")
 private String name;
 
 public User() {
    
  super();
 }
 
 public User(String id, String name) {
    
  super();
  this.id = id;
  this.name = name;
 }
 
 public String getId() {
    
  return id;
 }
 
 public void setId(String id) {
    
  this.id = id;
 }
 
 public String getName() {
    
  return name;
 }
 
 public void setName(String name) {
    
  this.name = name;
 }
 
 @Override
 public String toString() {
    
  return "User [id=" + id + ", name=" + name + "]";
 }
 
}

注解解析類的實現

io.mykit.annotation.jdk.db.provider.parser包中創建一個AnnotationParser類,AnnotationParser 類是整個框架的核心,它負責解析標注在實體類上的注解,並且將對應的實體類及其字段信息映射到對應的數據錶和字段上,如下所示。

package io.mykit.annotation.jdk.db.provider.parser;
 
import java.lang.reflect.Field;
import java.lang.reflect.Method;
 
import io.mykit.annotation.jdk.db.provider.Column;
import io.mykit.annotation.jdk.db.provider.Table;
 
/** * 解析自定義注解 * @author binghe * */
public class AnnotationParser {
    
 /** * 通過注解來組裝查詢條件,生成查詢語句 * @param obj * @return */  
    public static String assembleSqlFromObj(Object obj) {
      
        Table table = obj.getClass().getAnnotation(Table.class);  
        StringBuffer sbSql = new StringBuffer();  
        String tableName = table.value();  
        sbSql.append("select * from " + tableName + " where 1=1 ");  
        Field[] fileds = obj.getClass().getDeclaredFields();  
        for (Field f : fileds) {
      
            String fieldName = f.getName();  
            String methodName = "get" + fieldName.substring(0, 1).toUpperCase()  
                    + fieldName.substring(1);  
            try {
      
                Column column = f.getAnnotation(Column.class);  
                if (column != null) {
      
                    Method method = obj.getClass().getMethod(methodName);  
                    Object v = method.invoke(obj);  
                    if (v != null) {
      
                        if (v instanceof String) {
      
                         String value = v.toString().trim();
                            // 判斷參數是不是 in 類型參數 1,2,3 
                            if (value.contains(",")) {
      
                             //去掉value中的,
                             String sqlParams = value.replace(",", "").trim();
                             //value中都是純數字
                             if(isNum(sqlParams)){
    
                              sbSql.append(" and " + column.value() + " in (" + value + ") ");  
                             }else{
    
                              String[] split = value.split(",");
                              //將value重置為空
                              value = "";
                              for(int i = 0; i < split.length - 1; i++){
    
                               value += "'"+split[i]+"',";
                              }
                              value += "'"+split[split.length - 1]+"'";
                              sbSql.append(" and " + column.value() + " in (" + value + ") ");  
                             }
                            } else {
      
                             if(value != null && value.length() > 0){
    
                              sbSql.append(" and " + column.value() + " like '%" + value + "%' ");  
                             }
                            }  
                        } else {
    
                            sbSql.append(" and " + column.value() + "=" + v.toString() + " ");  
                        }  
                    }  
                }  
            } catch (Exception e) {
      
                e.printStackTrace();  
            }  
        }  
        return sbSql.toString();  
    }  
  
    /** * 檢查給定的值是不是 id 類型 1.檢查字段名稱 2.檢查字段值 * * @param target * @return */  
    public static boolean isNum(String target) {
      
        boolean isNum = false;  
        if (target.toLowerCase().contains("id")) {
      
            isNum = true;  
        }  
        if (target.matches("\\d+")) {
      
            isNum = true;  
        }  
        return isNum;  
    }  
}

至此,我們的極簡版ORM框架就實現好了,不過實現完還不行,我們還要對其進行測試驗證。

測試類的實現

io.mykit.annotation.jdk.provider包下創建AnnotationTest 類,用以測試我們實現的極簡ORM框架的效果,具體如下所示。

package io.mykit.annotation.jdk.provider;
 
import org.junit.Test;
 
import io.mykit.annotation.jdk.db.provider.entity.User;
import io.mykit.annotation.jdk.db.provider.parser.AnnotationParser;
import io.mykit.annotation.jdk.provider.parser.AnnotationProcessor;
 
/** * 測試自定義注解 * @author binghe * */
public class AnnotationTest {
    
 
 @Test
 public void testDBAnnotation(){
    
  User testDto = new User("123", "34");  
  User testDto1 = new User("123", "test1");  
  User testDto2 = new User("", "test1,test2,test3,test4");  
        String sql = AnnotationParser.assembleSqlFromObj(testDto);  
        String sql1 = AnnotationParser.assembleSqlFromObj(testDto1);  
        String sql2 = AnnotationParser.assembleSqlFromObj(testDto2);  
        System.out.println(sql);  
        System.out.println(sql1);  
        System.out.println(sql2);  
 }
}

運行測試

我們運行AnnotationTest#testDBAnnotation()方法,命令行會輸出如下信息。

select * from t_user where 1=1  and id like '%123%'  and name like '%34%' 
select * from t_user where 1=1  and id like '%123%'  and name like '%test1%' 
select * from t_user where 1=1  and name in ('test1','test2','test3','test4') 

可以看到,我們在測試程序中,並沒有在測試類中傳入或者執行任何SQL語句,而是直接創建User類的對象,並調用AnnotationParser#assembleSqlFromObj()進行解析,並且將對應的實體類對象轉換為SQL語句返回。

其實,MyBatis和Hibernate的底層核心原理都是這樣的,大家學會了嗎?有不懂的地方歡迎私聊我溝通。趕緊打開你的開發環境,手擼個極簡版ORM框架吧!!

大家有啥問題可以在下方留言,如果文章對你有點幫助,歡迎轉發給更多的小夥伴~~

好了,今天就到這兒吧,我是冰河,我們下期見~~

寫在最後

如果你想進大廠,想昇職加薪,或者對自己現有的工作比較迷茫,都可以私信我交流,希望我的一些經曆能够幫助到大家~~

推薦閱讀:

好了,今天就到這兒吧,小夥伴們點贊、收藏、評論,一鍵三連走起呀,我是冰河,我們下期見~~

原网站

版权声明
本文为[冰 河]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/189/202207080035189820.html