核心技术靠化缘是要不来的——自己动手写ORM框架
生活随笔
收集整理的這篇文章主要介紹了
核心技术靠化缘是要不来的——自己动手写ORM框架
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
開源市場(chǎng)上的Java的ORM框架一個(gè)都不好用,所以花了幾天時(shí)間自己擼了一個(gè) OrmKids,歡迎大家下載學(xué)習(xí)。遇到問(wèn)題請(qǐng)關(guān)注公眾號(hào)進(jìn)群大家一起討論。
OrmKids
支持分庫(kù)分表的MySQL單表ORM框架,暫用于學(xué)習(xí),后續(xù)會(huì)在生產(chǎn)環(huán)境進(jìn)行檢驗(yàn)
功能特性
不支持多表關(guān)聯(lián)
db.withinTx
對(duì)于復(fù)雜的多表查詢和批量數(shù)據(jù)處理,可以使用該方法。 用戶可以獲得原生的jdbc鏈接,通過(guò)編寫jdbc代碼來(lái)實(shí)現(xiàn)。
Q
用戶可以使用Q對(duì)象構(gòu)建復(fù)雜的SQL查詢
其它數(shù)據(jù)庫(kù)支持
暫時(shí)沒有
實(shí)體接口
/*** 所有的實(shí)體類必須實(shí)現(xiàn)該接口*/ public interface IEntity {/*** 表名* @return*/String table();/*** 分表必須覆蓋此方法* @return*/default String suffix() {return null;}default String tableWithSuffix() {return tableWith(suffix());}default String tableWith(String suffix) {return Utils.tableWithSuffix(table(), suffix);}/*** 定義表的物理結(jié)構(gòu)屬性如engine=innodb,用于動(dòng)態(tài)創(chuàng)建表* @return*/TableOptions options();/*** 定義表的主鍵和索引信息,用于動(dòng)態(tài)創(chuàng)建表* @return*/TableIndices indices();} 復(fù)制代碼單表單主鍵
public class User implements IEntity {(name = "id", type = "int", autoincrement = true, nullable = false)private Integer id;(name = "name", type = "varchar(255)", nullable = false)private String name;(name = "nick", type = "varchar(255)", nullable = false)private String nick;(name = "passwd", type = "varchar(255)")private String passwd;(name = "created_at", type = "datetime", nullable = false, defaultValue = "now()")private Date createdAt;public User() {}public User(String name, String nick, String passwd) {this.name = name;this.nick = nick;this.passwd = passwd;}public Integer getId() {return id;}public String getName() {return name;}public String getNick() {return nick;}public String getPasswd() {return passwd;}public Date getCreatedAt() {return createdAt;}public TableOptions options() {return new TableOptions().option("engine", "innodb");}public TableIndices indices() {return new TableIndices().primary("id").unique("name");}public String table() {return "user";}}復(fù)制代碼單表復(fù)合主鍵
public class Member implements IEntity {(name = "user_id", type = "int", nullable = false)private Integer userId;(name = "group_id", type = "int", nullable = false)private Integer groupId;(name = "title", type = "varchar(255)")private String title;(name = "created_at", type = "datetime", nullable = false, defaultValue = "now()")private Date createdAt;public Member() {}public Member(Integer userId, Integer groupId, String title, Date createdAt) {this.userId = userId;this.groupId = groupId;this.title = title;this.createdAt = createdAt;}public Integer getUserId() {return userId;}public Integer getGroupId() {return groupId;}public String getTitle() {return title;}public Date getCreatedAt() {return createdAt;}public TableOptions options() {return new TableOptions().option("engine", "innodb");}public TableIndices indices() {return new TableIndices().primary("user_id", "group_id");}public String table() {return "member";}} 復(fù)制代碼分庫(kù)接口
public interface IGridable<T extends IEntity> {/*** 根據(jù)實(shí)體對(duì)象選擇分庫(kù)索引*/int select(int dbs, T t);/*** 根據(jù)特定參數(shù)選擇分庫(kù)索引*/int select(int dbs, Object... params);} 復(fù)制代碼分庫(kù)分表
public class BookShelf implements IEntity {public final static int PARTITIONS = 4;(name = "user_id", type = "varchar(255)", nullable = false)private String userId;(name = "book_id", type = "varchar(255)", nullable = false)private String bookId;(name = "comment", type = "varchar(255)")private String comment;(name = "created_at", type = "datetime", nullable = false, defaultValue = "now()")private Date createdAt;public BookShelf() {}public BookShelf(String userId, String bookId, String comment, Date createdAt) {this.userId = userId;this.bookId = bookId;this.comment = comment;this.createdAt = createdAt;}public String getUserId() {return userId;}public String getBookId() {return bookId;}public void setComment(String comment) {this.comment = comment;}public String getComment() {return comment;}public Date getCreatedAt() {return createdAt;}public String table() {return "book_shelf";}public TableOptions options() {return new TableOptions().option("engine", "innodb");}public TableIndices indices() {return new TableIndices().primary("user_id", "book_id");}/* * 分表策略*/public String suffix() {var crc32 = new CRC32();crc32.update(userId.getBytes(Utils.UTF8));return String.valueOf(Math.abs(crc32.getValue()) % PARTITIONS);}/*** 分庫(kù)策略*/public static class GridStrategy<D extends DB> implements IGridable<BookShelf> {public int select(int dbs, BookShelf t) {return Math.abs(t.getUserId().hashCode()) % dbs;}public int select(int dbs, Object... params) {String userId = (String) params[0];return Math.abs(userId.hashCode()) % dbs;}}} 復(fù)制代碼定義單個(gè)數(shù)據(jù)庫(kù)
public class DemoDB extends DB {private DataSource ds;public DemoDB(String name, String uri) {this(name, new HashMap<>(), uri);}public DemoDB(String name, Map<Class<? extends IEntity>, Meta> metas, String uri) {super(name, metas);var ds = new MysqlConnectionPoolDataSource(); // 連接池ds.setUrl(uri);this.ds = ds;}protected Connection conn() { // 獲取鏈接try {return ds.getConnection();} catch (SQLException e) {throw new KidsException(e);}}} 復(fù)制代碼定義網(wǎng)格數(shù)據(jù)庫(kù)——分庫(kù)
public class GridDemoDB extends GridDB<DemoDB> {/*** 傳進(jìn)來(lái)多個(gè)DB對(duì)象*/public GridDemoDB(DemoDB[] dbs) {super(dbs);this.registerGridables();}/* * 注冊(cè)實(shí)體類的分庫(kù)策略*/public void registerGridables() {this.gridWith(BookShelf.class, new BookShelf.GridStrategy<DemoDB>());}}復(fù)制代碼單表單主鍵增刪改查
public class DemoSimplePk {private final static String URI = "jdbc:mysql://localhost:3306/mydrc?user=mydrc&password=mydrc&useUnicode=true&characterEncoding=UTF8";public static void main(String[] args) {var db = new DemoDB("demo", URI);try {db.create(User.class); // 創(chuàng)建表var user = new User("test1", "nick1", "passwd1");db.insert(user); // 插入System.out.println(user.getId());user = db.get(User.class, user.getId()); // 主鍵查詢System.out.printf("%s %s %s %s %s\n", user.getId(), user.getName(), user.getNick(), user.getPasswd(),user.getCreatedAt());user = new User("test2", "nick2", "passwd2");db.insert(user); // 再插入var count = db.count(User.class); // 查詢總行數(shù)System.out.println(count);var users = db.find(User.class); // 列出所有行System.out.println(users.size());for (var u : users) {System.out.printf("%s %s %s %s %s\n", u.getId(), u.getName(), u.getNick(), u.getPasswd(),u.getCreatedAt());}users = db.find(User.class, Q.eq_("nick"), "nick2"); // 條件查詢System.out.println(users.size());var setters = new HashMap<String, Object>();setters.put("passwd", "whatever");db.update(User.class, setters, 2); // 修改users = db.find(User.class); // 再列出所有行System.out.println(users.size());for (var u : users) {System.out.printf("%s %s %s %s %s\n", u.getId(), u.getName(), u.getNick(), u.getPasswd(),u.getCreatedAt());}db.delete(User.class, 1); // 刪除db.delete(User.class, 2); // 再刪除count = db.count(User.class); // 統(tǒng)計(jì)所有行System.out.println(count);} finally {db.drop(User.class); // 刪除表}}} 復(fù)制代碼單表復(fù)合主鍵增刪改查
public class DemoCompoundPk {private final static String URI = "jdbc:mysql://localhost:3306/mydrc?user=mydrc&password=mydrc&useUnicode=true&characterEncoding=UTF8";public static void main(String[] args) {var db = new DemoDB("demo", URI);try {db.create(Member.class); // 建表var member = new Member(1, 2, "boss", null);db.insert(member); // 插入member = db.get(Member.class, 1, 2); // 主鍵查詢System.out.println(member.getTitle());member = new Member(2, 2, "manager", new Date());db.insert(member); // 再插入var count = db.count(Member.class); // 獲取總行數(shù) System.out.println(count);var members = db.find(Member.class); // 獲取全部行for (var m : members) {System.out.printf("%d %d %s %s\n", m.getUserId(), m.getGroupId(), m.getTitle(), m.getCreatedAt());}member = new Member(2, 3, "manager", new Date());db.insert(member); // 再插入members = db.find(Member.class, Q.eq_("group_id"), 2); // 條件查詢for (var m : members) {System.out.printf("%d %d %s %s\n", m.getUserId(), m.getGroupId(), m.getTitle(), m.getCreatedAt());}var setters = new HashMap<String, Object>();setters.put("title", "employee");db.update(Member.class, setters, 2, 3); // 修改member = db.get(Member.class, 2, 3); // 主鍵查詢System.out.println(member.getTitle());db.delete(Member.class, 1, 2); // 刪除db.delete(Member.class, 2, 2); // 刪除db.delete(Member.class, 2, 3); // 刪除count = db.count(Member.class); // 再獲取總行數(shù)System.out.println(count);} finally {db.drop(Member.class); // 刪表}}} 復(fù)制代碼復(fù)雜查詢
public class DemoComplexQuery {private final static String URI = "jdbc:mysql://localhost:3306/mydrc?user=mydrc&password=mydrc&useUnicode=true&characterEncoding=UTF8";public static void main(String[] args) {var db = new DemoDB("demo", URI);try {db.create(Exam.class); // 建表var random = new Random();for (var i = 0; i < 100; i++) {var userId = Math.abs(random.nextLong());var exam = new Exam(userId, random.nextInt(100), random.nextInt(100), random.nextInt(100),random.nextInt(100), random.nextInt(100), random.nextInt(100));db.insert(exam); // 插入}System.out.println(db.count(Exam.class)); // 查詢總行數(shù)// math >= 50var exams = db.find(Exam.class, Q.ge_("math"), 50); // 條件查詢System.out.println(exams.size());var count = db.count(Exam.class, Q.ge_("math"), 50); // 條件總行數(shù)System.out.println(count);// math > 50 & english >= 50exams = db.find(Exam.class, Q.and(Q.gt_("math"), Q.ge_("english")), 50, 50); // 條件查詢System.out.println(exams.size());count = db.count(Exam.class, Q.and(Q.gt_("math"), Q.ge_("english")), 50, 50); // 條件總行數(shù)System.out.println(count);// math > 50 || english >= 50exams = db.find(Exam.class, Q.or(Q.gt_("math"), Q.ge_("english")), 50, 50); // 條件查詢System.out.println(exams.size());count = db.count(Exam.class, Q.or(Q.gt_("math"), Q.ge_("english")), 50, 50); // 條件總行數(shù)System.out.println(count);// math > 50 && (english >= 50 || chinese > 60)exams = db.find(Exam.class, Q.and(Q.gt_("math"), Q.or(Q.ge_("english"), Q.gt_("chinese"))), 50, 50, 60); // 條件查詢System.out.println(exams.size());count = db.count(Exam.class, Q.and(Q.gt_("math"), Q.or(Q.ge_("english"), Q.gt_("chinese"))), 50, 50, 60); // 條件總行數(shù)System.out.println(count);// math > 50 || physics between 60 and 80 || chemistry < 60exams = db.find(Exam.class, Q.or(Q.gt_("math"), Q.between_("physics"), Q.lt_("chemistry")), 50, 60, 80, 60); // 條件查詢System.out.println(exams.size());count = db.count(Exam.class, Q.or(Q.gt_("math"), Q.between_("physics"), Q.lt_("chemistry")), 50, 60, 80,60); // 條件總行數(shù)System.out.println(count);// group by math / 10var q = Q.select().field("(math div 10) * 10 as mathx", "count(1)").table("exam").groupBy("mathx").having(Q.gt_("count(1)")).orderBy("count(1)", "desc"); // 復(fù)雜sql構(gòu)造var rank = new LinkedHashMap<Integer, Integer>();db.any(Exam.class, q, stmt -> { // 原生sql查詢stmt.setInt(1, 0);ResultSet rs = stmt.executeQuery();while (rs.next()) {rank.put(rs.getInt(1), rs.getInt(2));}return rs;});rank.forEach((mathx, c) -> {System.out.printf("[%d-%d) = %d\n", mathx, mathx + 10, c);});} finally {db.drop(Exam.class);}}} 復(fù)制代碼分表
public class DemoPartitioning {private final static String URI = "jdbc:mysql://localhost:3306/mydrc?user=mydrc&password=mydrc&useUnicode=true&characterEncoding=UTF8";public static void main(String[] args) {var db = new DemoDB("demo", URI);try {for (int i = 0; i < BookShelf.PARTITIONS; i++) {db.create(BookShelf.class, String.valueOf(i)); // 創(chuàng)建所有分表}var bss = new ArrayList<BookShelf>();for (int i = 0; i < 100; i++) {var bs = new BookShelf("user" + i, "book" + i, "comment" + i, new Date());bss.add(bs);db.insert(bs); // 插入,自動(dòng)插入相應(yīng)分表}for (int i = 0; i < BookShelf.PARTITIONS; i++) {System.out.printf("partition %d count %d\n", i, db.count(BookShelf.class, String.valueOf(i)));}Random random = new Random();for (var bs : bss) {bs.setComment("comment_update_" + random.nextInt(100));db.update(bs); // 更新,自動(dòng)更新相應(yīng)分表數(shù)據(jù)}bss = new ArrayList<BookShelf>();for (int i = 0; i < BookShelf.PARTITIONS; i++) {bss.addAll(db.find(BookShelf.class, String.valueOf(i))); // 指定分表列出所有行}for (var bs : bss) {System.out.println(bs.getComment());}for (var bs : bss) {db.delete(bs); // 挨個(gè)刪除,自動(dòng)刪除相應(yīng)分表數(shù)據(jù)}} finally {for (int i = 0; i < BookShelf.PARTITIONS; i++) {db.drop(BookShelf.class, String.valueOf(i)); // 刪除所有分表}}}} 復(fù)制代碼分庫(kù)
public class DemoSharding {private static DemoDB[] dbs = new DemoDB[3];static {Map<Class<? extends IEntity>, Meta> metas = new HashMap<>();dbs[0] = new DemoDB("demo-0", metas,"jdbc:mysql://localhost:3306/mydrc?user=mydrc&password=mydrc&useUnicode=true&characterEncoding=UTF8");dbs[1] = new DemoDB("demo-1", metas,"jdbc:mysql://localhost:3307/mydrc?user=mydrc&password=mydrc&useUnicode=true&characterEncoding=UTF8");dbs[2] = new DemoDB("demo-2", metas,"jdbc:mysql://localhost:3308/mydrc?user=mydrc&password=mydrc&useUnicode=true&characterEncoding=UTF8");}public static void main(String[] args) {var grid = new GridDemoDB(dbs); // 構(gòu)造Grid實(shí)例try {for (int k = 0; k < BookShelf.PARTITIONS; k++) {grid.create(BookShelf.class, String.valueOf(k)); // 創(chuàng)建所有分庫(kù)中的分表}var bss = new ArrayList<BookShelf>();for (int i = 0; i < 100; i++) {var bs = new BookShelf("user" + i, "book" + i, "comment" + i, new Date());bss.add(bs);grid.insert(bs); // 插入,自動(dòng)分發(fā)到相應(yīng)的分庫(kù)中的分表}for (int k = 0; k < grid.size(); k++) {for (int i = 0; i < BookShelf.PARTITIONS; i++) {System.out.printf("db %d partition %d count %d\n", k, i,grid.count(BookShelf.class, k, String.valueOf(i))); // 依次查詢出所有分庫(kù)的分表的行數(shù)}}Random random = new Random();for (var bs : bss) {bs.setComment("comment_update_" + random.nextInt(100));grid.update(bs); // 更新,自動(dòng)分發(fā)到相應(yīng)的分庫(kù)中的分表}for (var bs : bss) {bs = grid.get(BookShelf.class, bs.getUserId(), bs.getBookId()); // 主鍵查詢,自動(dòng)分發(fā)到相應(yīng)的分庫(kù)中的分表System.out.println(bs.getComment());}for (var bs : bss) {grid.delete(bs); // 刪除,自動(dòng)分發(fā)到相應(yīng)的分庫(kù)中的分表}for (int k = 0; k < grid.size(); k++) {for (int i = 0; i < BookShelf.PARTITIONS; i++) {System.out.printf("db %d partition %d count %d\n", k, i,grid.count(BookShelf.class, k, String.valueOf(i))); // 依次查詢出所有分庫(kù)的分表的行數(shù)}}} finally {for (int k = 0; k < BookShelf.PARTITIONS; k++) {grid.drop(BookShelf.class, String.valueOf(k)); // 刪除所有分庫(kù)中的分表}}}} 復(fù)制代碼事件上下文對(duì)象
public class Context {private DB db; // 數(shù)據(jù)庫(kù)實(shí)例private Connection conn; // 當(dāng)前的鏈接private Class<? extends IEntity> clazz; // 當(dāng)前的實(shí)體類private Q q; // 查詢sqlprivate Object[] values; // 查詢的綁定參數(shù)private boolean before; // before or afterprivate Exception error; // 異常private long duration; // 耗時(shí)microsecond} 復(fù)制代碼事件回調(diào)
public class DemoEvent {private final static String URI = "jdbc:mysql://localhost:3306/mydrc?user=mydrc&password=mydrc&useUnicode=true&characterEncoding=UTF8";public static void main(String[] args) {var db = new DemoDB("demo", URI);db.on(ctx -> { // 全局事件回調(diào)System.out.printf("db=%s sql=%s cost=%dus\n", ctx.db().name(), ctx.q().sql(), ctx.duration());return true; // 返回false會(huì)導(dǎo)致事件鏈終止,后續(xù)的ORM操作也不會(huì)執(zhí)行});try {db.create(User.class);db.scope(ctx -> { // 范圍回調(diào),execute方法內(nèi)部的所有ORM操作都會(huì)回調(diào)System.out.printf("db=%s sql=%s cost=%dus\n", ctx.db().name(), ctx.q().sql(), ctx.duration());return true;}).execute(() -> {db.count(User.class);db.find(User.class);});} finally {db.drop(User.class); // 刪除表}}} 復(fù)制代碼總結(jié)
以上是生活随笔為你收集整理的核心技术靠化缘是要不来的——自己动手写ORM框架的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 饭店开除拾金不昧保洁员 收大量差评:领导
- 下一篇: 什么是WebService(WebSer