10 分鐘手擼極簡版 ORM 框架!
大家好,我是冰河~~
最近很多小夥伴對 ORM 框架的實現很感興趣,不少讀者在冰河的微信上問:冰河,你知道 ORM 框架是如何實現的嗎?比如像 MyBatis 和 Hibernte 這種 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 + ", ]";
}
}
註解解析類的實現
在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 框架吧!!
好了,今天就到這兒吧,我是冰河,大家有啥問題可以在下方留言,也可以加我微信:sun_shine_lyz,我拉你進羣,一起交流技術,一起進階,一起進大廠~~
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/G4jjTvhzec_C4GFQXTPQlg