开源市场上的Java的ORM框架一个都不好用,所以花了几天时间自己撸了一个 OrmKids,欢迎大家下载学习。遇到问题请关注公众号进群大家一起讨论。

OrmKids

支持分库分表的MySQL单表ORM框架,暂用于学习,后续会在生产环境进行检验

功能特性

  1. 代码简洁,没有任何依赖项,除了使用时需要用到MySQL driver
  2. 易于使用,无须复杂的配置
  3. 提供自动创建表功能
  4. 支持分库又分表,可以只分库,也可以只分表
  5. 支持groupby/having
  6. 支持原生SQL
  7. 支持事件回调,可用于服务跟踪调试和动态sql改写

不支持多表关联

  1. 多表比较复杂,实现成本高,学习成本也高,容易出错
  2. 常用的多表的操作一般都可以使用多条单表操作组合实现
  3. 在分库分表的场合,很少使用多表操作
  4. 不使用外键,专注于sql逻辑

db.withinTx

对于复杂的多表查询和批量数据处理,可以使用该方法。 用户可以获得原生的jdbc链接,通过编写jdbc代码来实现。

Q

用户可以使用Q对象构建复杂的SQL查询

其它数据库支持

暂时没有

实体接口

/*** 所有的实体类必须实现该接口*/
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);}/*** 定义表的物理结构属性如engine=innodb,用于动态创建表* @return*/TableOptions options();/*** 定义表的主键和索引信息,用于动态创建表* @return*/TableIndices indices();}
复制代码

单表单主键

@Table
public class User implements IEntity {@Column(name = "id", type = "int", autoincrement = true, nullable = false)private Integer id;@Column(name = "name", type = "varchar(255)", nullable = false)private String name;@Column(name = "nick", type = "varchar(255)", nullable = false)private String nick;@Column(name = "passwd", type = "varchar(255)")private String passwd;@Column(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;}@Overridepublic TableOptions options() {return new TableOptions().option("engine", "innodb");}@Overridepublic TableIndices indices() {return new TableIndices().primary("id").unique("name");}@Overridepublic String table() {return "user";}}复制代码

单表复合主键

@Table
public class Member implements IEntity {@Column(name = "user_id", type = "int", nullable = false)private Integer userId;@Column(name = "group_id", type = "int", nullable = false)private Integer groupId;@Column(name = "title", type = "varchar(255)")private String title;@Column(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;}@Overridepublic TableOptions options() {return new TableOptions().option("engine", "innodb");}@Overridepublic TableIndices indices() {return new TableIndices().primary("user_id", "group_id");}@Overridepublic String table() {return "member";}}
复制代码

分库接口

public interface IGridable<T extends IEntity> {/*** 根据实体对象选择分库索引*/int select(int dbs, T t);/*** 根据特定参数选择分库索引*/int select(int dbs, Object... params);}
复制代码

分库分表

@Table
public class BookShelf implements IEntity {public final static int PARTITIONS = 4;@Column(name = "user_id", type = "varchar(255)", nullable = false)private String userId;@Column(name = "book_id", type = "varchar(255)", nullable = false)private String bookId;@Column(name = "comment", type = "varchar(255)")private String comment;@Column(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;}@Overridepublic String table() {return "book_shelf";}@Overridepublic TableOptions options() {return new TableOptions().option("engine", "innodb");}@Overridepublic TableIndices indices() {return new TableIndices().primary("user_id", "book_id");}/* * 分表策略*/@Overridepublic String suffix() {var crc32 = new CRC32();crc32.update(userId.getBytes(Utils.UTF8));return String.valueOf(Math.abs(crc32.getValue()) % PARTITIONS);}/*** 分库策略*/public static class GridStrategy<D extends DB> implements IGridable<BookShelf> {@Overridepublic int select(int dbs, BookShelf t) {return Math.abs(t.getUserId().hashCode()) % dbs;}@Overridepublic int select(int dbs, Object... params) {String userId = (String) params[0];return Math.abs(userId.hashCode()) % dbs;}}}
复制代码

定义单个数据库

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;}@Overrideprotected Connection conn() {  // 获取链接try {return ds.getConnection();} catch (SQLException e) {throw new KidsException(e);}}}
复制代码

定义网格数据库——分库

public class GridDemoDB extends GridDB<DemoDB> {/*** 传进来多个DB对象*/public GridDemoDB(DemoDB[] dbs) {super(dbs);this.registerGridables();}/* * 注册实体类的分库策略*/@Overridepublic void registerGridables() {this.gridWith(BookShelf.class, new BookShelf.GridStrategy<DemoDB>());}}复制代码

单表单主键增删改查

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); // 创建表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); // 查询总行数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); // 统计所有行System.out.println(count);} finally {db.drop(User.class); // 删除表}}}
复制代码

单表复合主键增删改查

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);  // 获取总行数 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); // 再获取总行数System.out.println(count);} finally {db.drop(Member.class); // 删表}}}
复制代码

复杂查询

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)); // 查询总行数// 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); // 条件总行数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); // 条件总行数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); // 条件总行数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); // 条件总行数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); // 条件总行数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"); // 复杂sql构造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);}}}
复制代码

分表

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)); // 创建所有分表}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); // 插入,自动插入相应分表}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); // 更新,自动更新相应分表数据}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); // 挨个删除,自动删除相应分表数据}} finally {for (int i = 0; i < BookShelf.PARTITIONS; i++) {db.drop(BookShelf.class, String.valueOf(i)); // 删除所有分表}}}}
复制代码

分库

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); // 构造Grid实例try {for (int k = 0; k < BookShelf.PARTITIONS; k++) {grid.create(BookShelf.class, String.valueOf(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); // 插入,自动分发到相应的分库中的分表}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))); // 依次查询出所有分库的分表的行数}}Random random = new Random();for (var bs : bss) {bs.setComment("comment_update_" + random.nextInt(100));grid.update(bs); // 更新,自动分发到相应的分库中的分表}for (var bs : bss) {bs = grid.get(BookShelf.class, bs.getUserId(), bs.getBookId()); // 主键查询,自动分发到相应的分库中的分表System.out.println(bs.getComment());}for (var bs : bss) {grid.delete(bs); // 删除,自动分发到相应的分库中的分表}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))); // 依次查询出所有分库的分表的行数}}} finally {for (int k = 0; k < BookShelf.PARTITIONS; k++) {grid.drop(BookShelf.class, String.valueOf(k)); // 删除所有分库中的分表}}}}
复制代码

事件上下文对象

public class Context {private DB db; // 数据库实例private Connection conn;  // 当前的链接private Class<? extends IEntity> clazz; // 当前的实体类private Q q; // 查询sqlprivate Object[] values; // 查询的绑定参数private boolean before; // before or afterprivate Exception error; // 异常private long duration; // 耗时microsecond}
复制代码

事件回调

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 -> { // 全局事件回调System.out.printf("db=%s sql=%s cost=%dus\n", ctx.db().name(), ctx.q().sql(), ctx.duration());return true; // 返回false会导致事件链终止,后续的ORM操作也不会执行});try {db.create(User.class);db.scope(ctx -> { // 范围回调,execute方法内部的所有ORM操作都会回调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); // 删除表}}}
复制代码

核心技术靠化缘是要不来的——自己动手写ORM框架相关推荐

  1. 深度学习核心技术精讲100篇(四十二)-Seq2seq框架下的文本生成

    前言 文本生成,旨在利用NLP技术,根据给定信息产生特定目标的文本序列,应用场景众多,并可以通过调整语料让相似的模型框架适应不同应用场景.本文重点围绕Encoder-Decoder结构,列举一些以文本 ...

  2. 分布式系统技术难题--异地多活

    什么是异地多活? 为了保证系统能够对机房级别的故障进行容错,不会使系统不可用,这就需要在机房级别对系统进行冗余处理.而这就需要在架构上进行良好的设计.来面对多机房场景下的技术挑战.事实上,异地多活最大 ...

  3. 码洞原创深度技术文章大全 —— 高端面试必备

    <快学 Go 语言>第 11 课 -- 协程 <快学 Go 语言>第 10 课 -- 错误与异常 <快学 Go 语言>第 9 课 -- 接口 <快学 Go 语 ...

  4. 大志非才不就,大才非学不成—我的博文资源汇总

    零.苦逼码农的自我修养系列 PS:为什么此部分序号是零而不是一?因为这是作为一个码农所应该具有的基础之中的基础,要想做个好码农,此部分还得花大力气啃书啊,这决定了我们看待计算机程序的高度. 0.1 数 ...

  5. .NET全栈开发工程师

    .NET全栈开发工程师学习路径 PS:最近一直反复地看博客园以前发布的一条.NET全栈开发工程师的招聘启事,觉得这是我看过最有创意也最朴实的一个招聘启事,更为重要的是它更像是一个技术提纲,能够指引我们 ...

  6. Docker 核心技术与实现原理

    提到虚拟化技术,我们首先想到的一定是 Docker,经过四年的快速发展 Docker 已经成为了很多公司的生产环境中大规模使用,也不再是一个只能在开发阶段使用的玩具了.作为在生产环境中广泛应用的产品, ...

  7. SSM三大框架的运行流程、原理、核心技术详解!

    一.Spring部分 1.Spring的运行流程 第一步:加载配置文件ApplicationContext ac = new ClassPathXmlApplicationContext(" ...

  8. MaxCompute执行引擎核心技术DAG揭秘

    简介: 作为业界少有的EB级数据分布式平台,MaxCompute每天支撑上千万个分布式作业的运行.这些作业特点各异,既有包含数十万计算节点的超大型作业,也有中小规模的分布式作业.不同用户对于不同规模/ ...

  9. 打破国外垄断,我国拿下一项“制芯”关键技术

    来源:科技日报 "PM2.5,是大家很熟悉的微小颗粒物,直径小于或等于2.5微米.但我们研制这种制造芯片的关键材料,在过程中如果进入了哪怕PM1.0的粉尘,这个材料就是废品,就不能被应用到芯 ...

最新文章

  1. 我用Python爬取英雄联盟的皮肤,隔壁家的小弟弟都馋哭了
  2. 009_InputNumber计数器
  3. linux为什么创建不了分区,linux下扩容磁盘扩展分区解决因无法创建新分区不能扩容lvm问题...
  4. SAP Leonardo机器学习Restful API如何获得Access Token
  5. Thinkphp 关联模型和试图模型区别
  6. html网页之间怎么切换效果,HTML教程:网页页面切换的各种效果-♚付涛纪实阁♚...
  7. FCPX插件:Hyper Zoom Transitions Mac(33种扭曲变形缩放转场效果)
  8. php狼和兔子算法,PHP基于递归算法解决兔子生兔子问题php技巧
  9. 慕课版软件质量保证与测试(第一章.课后作业)
  10. 肌电信号 聚类 Matlab
  11. 5.VM虚拟机网络设置---桥接模式
  12. 南方cass简码识别大全_南方CASS简码成图的方法
  13. 谷歌Chrome紧急更新补丁0day漏洞
  14. Linux学习_系统文件IO
  15. 解决报错:%d format: a number is required, not str
  16. 新辰:台北90后创业炸鸡配啤酒 来自星星的你 不成功也难
  17. 国内如何打开 Coursera?(Mac系统)
  18. REVERSE-COMPETITION-HGAME2022-Week3
  19. 【离散】如何利用顶点数求树叶或知树叶求顶点
  20. 线性代数学习之线性系统

热门文章

  1. xmm1是什么器件_数字电路实验指导书
  2. 随机抽样java_java生成抽样随机数的多种算法
  3. java简述垃圾回收原理及算法_Java垃圾回收原理和算法
  4. yjv是电缆还是电线_YJV与VV电缆的区别你知道吗?推荐
  5. jacoco统计server端功能测试覆盖率
  6. 记录某对比软件注册 版本4
  7. dealloc时取weakself引起崩溃
  8. getSlotFromBufferLocked: unknown buffer: 0xf3d94ca0
  9. C#-常用对象-思维导图
  10. Sql添加Oracle数据库的表空间和用户