詳解Java注解實(shí)現(xiàn)自己的ORM
搞過(guò)Java的碼農(nóng)都知道,在J2EE開(kāi)發(fā)中一個(gè)(確切地說(shuō),應(yīng)該是一類)很重要的框架,那就是ORM(Object Relational Mapping,對(duì)象關(guān)系映射)。它把Java中的類和數(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è)「表格」:
/**
* 類注解,將類注解成數(shù)據(jù)庫(kù)表
*
* @author xiangwang
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
String name() default "";
}然后,定義需要的數(shù)據(jù)庫(kù)數(shù)據(jù)類型:
/**
* 字段類型枚舉
*
* @author xiangwang
*/
public enum Type {
CHAR,
STRING,
BOOLEAN,
INTEGER,
LONG,
FLOAT,
DOUBLE,
DATETIME
}
/**
* 數(shù)據(jù)庫(kù)字段類型
*
* @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í)體類,應(yīng)用剛才寫(xiě)好的這些注解:
/**
* 用戶實(shí)體類
*
* @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是怎么工作的吧:
/**
* 解析類型注解
*/
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 = "";
// 類型判斷
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版簡(jiǎn)單的猜數(shù)字游戲?qū)嵗a
猜數(shù)字游戲是一款經(jīng)典的游戲,該游戲說(shuō)簡(jiǎn)單也很簡(jiǎn)單,說(shuō)不簡(jiǎn)單確實(shí)也很難,那么下面這篇文章主要給大家介紹了java版簡(jiǎn)單的猜數(shù)字游戲的相關(guān)資料,文中給出了詳細(xì)的實(shí)現(xiàn)分析和示例代碼供大家參考學(xué)習(xí),需要的朋友們下面來(lái)一起看看吧。2017-05-05
SpringBoot加載應(yīng)用事件監(jiān)聽(tīng)器代碼實(shí)例
這篇文章主要介紹了SpringBoot加載應(yīng)用事件監(jiān)聽(tīng)器代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
IDEA 2019.2.2配置Maven3.6.2打開(kāi)Maven項(xiàng)目出現(xiàn) Unable to import Maven
這篇文章主要介紹了IDEA 2019.2.2配置Maven3.6.2打開(kāi)Maven項(xiàng)目出現(xiàn) Unable to import Maven project的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12
Java封裝的實(shí)現(xiàn)訪問(wèn)限定符、包
封裝就是將數(shù)據(jù)和操作數(shù)據(jù)的方法進(jìn)行有機(jī)結(jié)合,隱藏對(duì)象的屬性(成員變量)和實(shí)現(xiàn)細(xì)節(jié),僅對(duì)外公開(kāi)接口來(lái)和對(duì)象進(jìn)行交互,下面這篇文章主要給大家介紹了關(guān)于Java封裝實(shí)現(xiàn)訪問(wèn)限定符、包的相關(guān)資料2022-08-08
淺談JSP與Servlet傳值及對(duì)比(總結(jié))
下面小編就為大家?guī)?lái)一篇淺談JSP與Servlet傳值及對(duì)比(總結(jié))。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05
java實(shí)現(xiàn)抽獎(jiǎng)功能解析
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)抽獎(jiǎng)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03
MyBatis-Plus 使用枚舉自動(dòng)關(guān)聯(lián)注入
本文主要介紹了MyBatis-Plus 使用枚舉自動(dòng)關(guān)聯(lián)注入,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-06-06

