詳解Java注解實(shí)現(xiàn)自己的ORM
搞過(guò)Java的碼農(nóng)都知道,在J2EE開(kāi)發(fā)中一個(gè)(確切地說(shuō),應(yīng)該是一類(lèi))很重要的框架,那就是ORM(Object Relational Mapping,對(duì)象關(guān)系映射)。它把Java中的類(lèi)和數(shù)據(jù)庫(kù)中的表關(guān)聯(lián)起來(lái),可以像操作對(duì)象那樣操作數(shù)據(jù)表,十分方便。給碼農(nóng)們節(jié)約了大量的時(shí)間去摸魚(yú)。其實(shí)它的本質(zhì)一點(diǎn)都不復(fù)雜,而最核心的就是怎么實(shí)現(xiàn)對(duì)象和表之間的轉(zhuǎn)換。之前對(duì)反射和注解有了一點(diǎn)了解,所以就試著來(lái)實(shí)現(xiàn)咱們自己的縫合怪。
首先,需要建立一個(gè)「表格」:
/**
* 類(lèi)注解,將類(lèi)注解成數(shù)據(jù)庫(kù)表
*
* @author xiangwang
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
String name() default "";
}然后,定義需要的數(shù)據(jù)庫(kù)數(shù)據(jù)類(lèi)型:
/**
* 字段類(lèi)型枚舉
*
* @author xiangwang
*/
public enum Type {
CHAR,
STRING,
BOOLEAN,
INTEGER,
LONG,
FLOAT,
DOUBLE,
DATETIME
}
/**
* 數(shù)據(jù)庫(kù)字段類(lèi)型
*
* @author xiangwang
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnType {
Type value() default Type.INTEGER;
}再來(lái)完善字段相關(guān)信息:
/**
* 字段信息
*
* @author xiangwang
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtraInfo {
String name() default "";
int length() default 0;
}
/**
* 明確字段約束
*
* @author xiangwang
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
boolean primaryKey() default false;
boolean allowNull() default true;
boolean unique() default false;
// 還可以增加默認(rèn)值
}把他們拼起來(lái),成為完整的字段描述:
/**
* 拼裝注解,形成完整的字段嵌套注解
*
* @author xiangwang
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableColumn {
ColumnType columntype() default @ColumnType;
ExtraInfo extrainfo() default @ExtraInfo;
Constraints constraints() default @Constraints;
}最后,創(chuàng)建實(shí)體類(lèi),應(yīng)用剛才寫(xiě)好的這些注解:
/**
* 用戶(hù)實(shí)體類(lèi)
*
* @author xiangwang
*/
@DBTable(name = "User")
public class User {
@TableColumn(
columntype = @ColumnType(Type.INTEGER),
extrainfo = @ExtraInfo(name = "id", length = 4),
constraints = @Constraints(primaryKey = true))
private String id;
@TableColumn(
columntype = @ColumnType(Type.STRING),
extrainfo = @ExtraInfo(name = "name", length = 32),
constraints = @Constraints(primaryKey = false, allowNull = false, unique = true))
private String name;
@TableColumn(
columntype = @ColumnType(Type.INTEGER),
extrainfo = @ExtraInfo(name = "age", length = 4),
constraints = @Constraints(primaryKey = false))
private Integer age;
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; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}來(lái)看看ORM是怎么工作的吧:
/**
* 解析類(lèi)型注解
*/
private static String getColumnType(ColumnType columntype) {
String type = "";
switch (columntype.value()) {
case CHAR:
type += "CHAR";
break;
case STRING:
type += "VARCHAR";
break;
case BOOLEAN:
type += "BIT";
break;
case INTEGER:
type += "INT";
break;
case LONG:
type += "BIGINT";
break;
case FLOAT:
type += "FLOAT";
break;
case DOUBLE:
type += "DOUBLE";
break;
case DATETIME:
type += "DATETIME";
break;
default:
type += "VARCHAR";
break;
}
return type;
}
/**
* 解析信息注解
*/
private static String getExtraInfo(ExtraInfo extrainfo) {
String info = "";
if (null != extrainfo.name()) {
info = extrainfo.name();
} else {
return null;
}
if (0 < extrainfo.length()) {
info += " (" + extrainfo.length() + ")";
} else {
return null;
}
return info;
}
/**
* 解析約束注解
*/
private static String getConstraints(Constraints con) {
String constraints = "";
if (con.primaryKey()) {
constraints += " PRIMARY KEY";
}
if (!con.allowNull()) {
constraints += " NOT NULL";
}
if (con.unique()) {
constraints += " UNIQUE";
}
return constraints;
}做了那么多的鋪墊,終于到了臨門(mén)一腳了,實(shí)現(xiàn)一個(gè)縫合怪了:
/**
* 臨門(mén)一腳:實(shí)現(xiàn)一個(gè)縫合怪
*/
private static void createTable(List<String> list) {
for (String className : list) {
Class<?> clazz;
try {
clazz = Class.forName(className);
DBTable dbTable = clazz.getAnnotation(DBTable.class);
if (dbTable == null) {// 無(wú)DBTable注解
continue;
}
// 轉(zhuǎn)大寫(xiě)
String tableName = clazz.getSimpleName().toUpperCase();
StringBuilder sql = new StringBuilder("CREATE TABLE " + tableName + "(");
for (Field field : clazz.getDeclaredFields()) {
// 反射得到注解
Annotation[] anns = field.getDeclaredAnnotations();
if (anns.length < 1) {
continue;
}
String columnInfo = "";
// 類(lèi)型判斷
if (anns[0] instanceof TableColumn) {
TableColumn column = (TableColumn) anns[0];
String type = getColumnType(column.columntype());
columnInfo = getExtraInfo(column.extrainfo());
// 代替(
columnInfo = columnInfo.replace("(", type + "(");
columnInfo += getConstraints(column.constraints());
}
sql.append("\n " + columnInfo + ",");
}
// 刪除尾部的逗號(hào)
String tableCreate = sql.substring(0, sql.length() - 1) + "\n);";
System.out.println(tableCreate);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}驗(yàn)證效果的時(shí)候到了:
public static void main(String[] args) {
Class<?> clazz = User.class;
List<String> list = new ArrayList<>();
list.add(clazz.getName());
createTable(list);
}當(dāng)然,實(shí)際的運(yùn)營(yíng)于生產(chǎn)環(huán)境中的ORM框架可要比這個(gè)小玩意復(fù)雜多了。但千變?nèi)f變,原理不變,ORM的核心——反射+ 注解——就是這么玩的。
到此這篇關(guān)于Java注解實(shí)現(xiàn)自己的ORM的文章就介紹到這了,更多相關(guān)Java注解ORM內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java中ThreadLocal的應(yīng)用場(chǎng)景實(shí)例分析
在本篇文章里小編給大家整理的是一篇關(guān)于java中ThreadLocal的應(yīng)用場(chǎng)景實(shí)例分析,對(duì)此有興趣的朋友們可以學(xué)習(xí)參考下。2021-02-02
詳解如何使用MyBatis簡(jiǎn)化JDBC開(kāi)發(fā)
JavaEE?企業(yè)級(jí)?Java?項(xiàng)目中的經(jīng)典三層架構(gòu)為表現(xiàn)層,業(yè)務(wù)層和持久層.MyBatis?對(duì)?JDBC?代碼進(jìn)行了封裝,作為一款優(yōu)秀的持久層框架,專(zhuān)門(mén)用于簡(jiǎn)化JDBC開(kāi)發(fā).本文主要介紹一下如何使用MyBatis簡(jiǎn)化JDBC開(kāi)發(fā),需要的可以參考一下2023-01-01
Java將網(wǎng)絡(luò)圖片轉(zhuǎn)成輸入流以及將url轉(zhuǎn)成InputStream問(wèn)題
這篇文章主要介紹了Java將網(wǎng)絡(luò)圖片轉(zhuǎn)成輸入流以及將url轉(zhuǎn)成InputStream問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01
IDEA生成patch和使用patch的方法實(shí)現(xiàn)
比如你本地修復(fù)的 bug,需要把增量文件發(fā)給客戶(hù),很多場(chǎng)景下大家都需要手工整理修改的文件,并整理好目錄,這個(gè)很麻煩,那有沒(méi)有簡(jiǎn)單的技巧呢?本文主要介紹了IDEA生成patch和使用patch的方法實(shí)現(xiàn),感興趣的可以了解一下2023-08-08
java中Timer定時(shí)器的使用和啟動(dòng)方式
這篇文章主要介紹了java中Timer定時(shí)器的使用和啟動(dòng)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
java操作ElasticSearch聚合查詢(xún)的示例代碼
這篇文章主要介紹了java操作ElasticSearch聚合查詢(xún)的示例代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2024-08-08
使用Java編寫(xiě)一個(gè)好用的解析配置工具類(lèi)
這篇文章主要為大家詳細(xì)介紹了如何使用Java編寫(xiě)一個(gè)好用的解析配置工具類(lèi),支持解析格式有properties,yaml和yml,感興趣的可以了解下2024-11-11
深入探究Java?@MapperScan實(shí)現(xiàn)原理
之前是直接在Mapper類(lèi)上面添加注解@Mapper,這種方式要求每一個(gè)mapper類(lèi)都需要添加此注解,麻煩。通過(guò)使用@MapperScan可以指定要掃描的Mapper類(lèi)的包的路徑,這篇文章深入探究Java?@MapperScan的實(shí)現(xiàn)原理2023-01-01

