詳解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版簡(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-05SpringBoot加載應(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-06IDEA 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-12Java封裝的實(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-05java實(shí)現(xiàn)抽獎(jiǎng)功能解析
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)抽獎(jiǎng)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03MyBatis-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