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

线性回归从原理到落地:手撕公式、诊断假设、业务翻译 线性回归是机器学习中最基础且应用最广的统计建模方法,其核心在于通过最小二乘法寻找变量间最优的线性解释关系。它并非黑箱预测工具,而是建立在四大关键假设(线性、独立性、同方差性、误差正态性)之上的可解释性框架。掌握其数学直觉——如用协方差与方差手动推导斜率、用正规方程理解多变量求解——是避免误用的前提;而残差图诊断、标准化处理、p值与置信区间解读,则直接决定模型能否通过业务验证。本文聚焦真实二手房价格分析场景,贯穿从NumPy手算、sklearn工程实现到statsmodels统计推断的完整链路,并重点解决‘系 阅读详情

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


OrmKids

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

功能特性

  1. 代码简洁,没有任何依赖项,除了使用时需要用到MySQL driver

  2. 易于使用,无须复杂的配置

  3. 提供自动创建表功能

  4. 支持分库又分表,可以只分库,也可以只分表

  5. 支持groupby/having

  6. 支持原生SQL

  7. 支持SubQuery

  8. 支持事件回调,可用于服务跟踪调试和动态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;
   }

   @Override
   public TableOptions options() {
       return new TableOptions().option("engine", "innodb");
   }

   @Override
   public TableIndices indices() {
       return new TableIndices().primary("id").unique("name");
   }

   @Override
   public 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;
   }

   @Override
   public TableOptions options() {
       return new TableOptions().option("engine", "innodb");
   }

   @Override
   public TableIndices indices() {
       return new TableIndices().primary("user_id", "group_id");
   }

   @Override
   public 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;
   }

   @Override
   public String table() {
       return "book_shelf";
   }

   @Override
   public TableOptions options() {
       return new TableOptions().option("engine", "innodb");
   }

   @Override
   public TableIndices indices() {
       return new TableIndices().primary("user_id", "book_id");
   }

   /*
    * 分表策略
    */

   @Override
   public String suffix() {
       var crc32 = new CRC32();
       crc32.update(userId.getBytes(Utils.UTF8));
       return String.valueOf(Math.abs(crc32.getValue()) % PARTITIONS);
   }

   /**
    * 分库策略
    */

   public static class GridStrategy implements IGridable<BookShelf> {

       @Override
       public int select(int dbs, BookShelf t) {
           return Math.abs(t.getUserId().hashCode()) % dbs;
       }

       @Override
       public 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;
   }

   @Override
   protected 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();
   }

   /*
    * 注册实体类的分库策略
    */

   @Override
   public void registerGridables() {
       this.gridWith(BookShelf.class, new BookShelf.GridStrategy());
   }

}

单表单主键增删改查

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 >= 50
           var 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 >= 50
           exams = 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 >= 50
           exams = 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 < 60
           exams = 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 / 10
           var 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; // 查询sql
   private Object[] values; // 查询的绑定参数
   private boolean before; // before or after
   private 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); // 删除表
       }
   }

}
项目开源地址:https://github.com/pyloque/ormkids

自己实现orm框架 自己实现简单的orm框架。。 可以参照着自己写。。。。 立即下载

相关推荐

手写简易版ORM框架(仿mybatis)

自己手写的一个半自动框架,里面有运行例子,部分代码从mybatis中抽出

基于Spring JDBC 手写定制自己的ORM框架

实现思路概述 ResultSet 说到ResultSet,对于有java开发经验的小伙伴自然是熟悉不过了。从ResultSet的取值大家都会,比如: private static List<Member> select(String sql) { List<Member> result = new ArrayList<>(); Connection con = null; //连接对象 PreparedState

Java是世界上最好的语言 655

基于SpringJDBC的轻量级ORM框架sborm.zip

一、SBORM 介绍 1、目前只考虑支持 mysql; 2、基于spring jdbc的上层封装,底层jdbc操作基于JdbcTemplate,对于使用spring jdbc的人会有一点价值,比较简洁的封装可以节省很多重复劳动,具体节省多少可以看看example; 3、实现一套简单的ORM(直接使用spring rowmapper,insert自己实现),可以基于对象进行crud和相对复杂(感觉比hibernate强大一点)的sql操作; 4、基于对象指定查询的字段,大部分时候可以忘掉表结构进行业务开发; 5、支持简单的数据库路由,读写分离(半自动,需要指定取writer还是reader,默认规则reader采用随机的方式,当然也可以手动指定); 6、支持简单的分表,主要是针对一定规则的分表,比如百分表、千分表,也可以自己指定分表后缀; 7、简单的单表查询(比如所有条件是and或者or结构),基本实现0sql代码编写(类似HibernateTemplate selectByExample、findByCriteria、find等方法); 8、简单的单表排序支持,支持多个排序条件组合; 9、对于复杂的sql查询,提供获取jdbctemplate实例进行操作,类似spring jdbc的常规用法; 10、提供Entity代码生成接口,Entity并非简单的pojo(尽可能不要去修改此类),引入字段常量类,方便查询的时候指定选择字段,从而更好实现查询条件的封装; 二、为什么写SBORM? 1、hibernate:过于臃肿,使用不够灵活,优化难(其实主要是因为很少用),HQL感觉就是个渣,在 mysql几乎一统天下的背景下,跨数据库级别的兼容吃力不讨好。Hibernate的对象化关联处理确实挺强大,但是使用起来坑太多,有多少人敢在项目 中大范围使用真不知道,屠龙刀不是人人都提的起啊。 2、mybatis:轻量级,基于xml的模式感觉不利于封装,代码量不小,基于xml维护也麻烦(个人观点, 现在注解模式貌似也挺不错),感觉mybatis更适合存在dba角色的年代,可以远离代码进行sql调优,复杂的查询拼装起来也更加优雅(java基本 就是if else ...),但是对于查询业务简单但是数据库集群环境的场景有点憋屈(其实对mybatis使用也不多,瞎评论^_^)。 3、spring jdbc:小巧,灵活,足够优秀,个人比较喜欢使用,但是代码量偏大,原生的接口重复劳动量大,比如insert、mapper之类的; SBORM只是针对spring jdbc的一些不方便的地方,做了一些封装,更加简化日常的开发工作,基于spring jdbc的RowMapper自动实现对象映射,也勉强算的上叫ORM,只是大部分功能已经由spring jdbc实现了。 平时不太喜欢使用hibernate和mybatis,主要是使用spring jdbc,写这个东西的出发点主要是平时使用spring jdbc觉 得比较麻烦,重复性的代码偏多,一方面通过自动mapper降低返回结果处理工作量,另一方面参考hibernate对象化查询条件的模式,写了一个 QueryBudiler,使得更多简单的单表查询可以通过对象组织查询、更改逻辑,避免过多去写相似性的SQL语句,减少DAO接口量。 三、一些亮点 1、Entity的设计:很多人看了也许会说,这个不是POJO,不是纯粹的Java Bean,显得很另类。但是有多人在开发过程中(特别是在写sql的时候),经常要去看看表结构设计?还有多少次因为改了表某个字段,还得遍历去查找哪些 sql使用了这个字段?多少次看到在代码中直接传入字段名作为查询参数感到别扭?如果将表结构字段都用java对象去描述,能够解决这些问题,就不必要在 乎是不是POJO了,后面看example的时候应该能体会这么做的一些好处,至少我觉得是挺方便的,将大部分查询脱离表结构设计。 2、简单的数据库路由:如果分库结构不是太复杂(比如简单的读写分离、或者多个库集成),BaseDao可以自 动进行路由(比如读写分离,根据业务模式指定读、写库),如果非默认的路由规则,也可以通过手动设置的模式,进行数据库路由。数据库路由直接由 Entity指定,所有的路由都是根据Entity识别,也就是说查询也是围绕Entity展开的,避免类似使用spring jdbc的时候,各种 template实例跳来跳去,硬编码引入,写一个业务还得看看到底该用哪个template,尤其是多个数据库共用一个template实例的时候。 3、QueryBuilder:单表查询基本上都可以实现零Sql(除非查询条件特别复杂的),更新、删除等操作也可以通过QueryBuilder进行批量处理,不局限于根据主键来处理。 4、分表操作的支持:对于分表操作和常规的使用没有区别,只是指定分表规则,mybatis好像也可以通过制定参数实现分表处理,没搞清楚hibernate对这个是怎么处理的(hibernate好像是bean和表一对一绑定的)? 标签:sborm

【ORM】怎样自己写一个ORM框架-1

最近一直在看一些Spring JDBC 的一些东西,觉得吧手动的方式真的不太习惯,而MyBatis需要配置Mapping,虽然更加灵活了,也不简单,hibernate 基本上可以实现单表Nosql但是比较重量级。纠结了这么长时间,还是现在用的的ORM框架比较好用。 我们一般的默认都是 约定优于配置 也就是说我们的规范更加重要,比如说为什么我们的JAVA类命名规则是驼峰,接口或者实现类加上I或者I...

Old Wang的博客 2227

一个灵活轻量级的基于SpringjdbcTemplate的ORM

这是一个基于 Spring JdbcTemplate 的小工具,帮助开发者简单地完成 Mysql 的增删改查。 使用 nimble-orm 的优势 为互联网频繁的表变动而生。 表名、字段名

自己写的orm辅助类

自己写的orm辅助类 .定义实体的特性(如对应的表名 主键名 属性对应的列名 实体和数据库对象的相互转换 简单实体的 CRUD操作 QQ交流群60168829 欢迎C# ASP.NET 和SQL新手 老手 高手加入 谢谢

自己开发一个Java ORM框架(5)-CRUD操作源码

本篇介绍Panda ORM的核心功能(即添加简单注解即可对实体进行增删改查操作功能)的设计与实现。一,整体思路介绍1,先了解项目结构如下,注意所有的包都在src目录的panda.orm下,配置文件config.properties处于src目录下。 2, annotation下是自定义注解,主要用于为实体类的列添加主键、外键相关的附加信息。这样Panda ORM运行的时候就知道实体类哪些列是主键、

编程副队长 3191

手写ORM框架.java

博文资源

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

《快学 Go 语言》第 11 课 —— 协程《快学 Go 语言》第 10 课 —— 错误与异常《快学 Go 语言》第 9 课 —— 接口《快学 Go 语言》第 8 课 —...

codehole_的博客 293

手写SpringBoot框架

阅读SpringBoot底层源码,手写类SpringBoot框架,并附控制层,视图层路径的测试。

Unity——Scroll View 滑动居中、居中的放大、其他的缩小(支持横竖Scroll View)

一、效果 二、上代码,两种方式。 (第一种是在Update中用插值完成的,如果不想再项目中看到Update,下面的另一种改了一段用DoTween实现的,需要导入一下DoTween) using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using Unit...

SiReSo 7662

深入Java日记——自己写一个ORM框架(1)

众所周知,ORM框架有很多,例如Hibernate,MyBatis,还有BeetlSQL等等,里面获取有很多我们不需要的功能,本系列博客主要教大家如何写一个简单的ORM框架这个ORM框架主要有以下功能:1. 生成JavaBean代码 2. 通过JavaBean来实现增删查改我们这次先讲如何生成JavaBean代码主要有以下几个步骤:1. 获取数据库连接 2. 获取表的信息 3. 将数据库的类型

饥渴计科极客杰铿的博客 4625

generate for与always内for:Verilog循环的静态生成与动态执行之辨

本文深入解析Verilog中generate for与always内部for循环的核心差异。generate for使用genvar变量在编译时静态生成硬件结构,适用于模块实例化;always内部for使用整型变量在运行时动态执行,综合为并行逻辑。理解这两种循环的适用场景和综合结果对提升数字电路设计质量至关重要。

yog99的博客 1088

仿mybatis,手写自己的ORM框架

orm框架,说白了就是帮助用户对jdbc进行封装,底层还是操作jdbc,前几天了解了一下mybatis的原理,于是想手写一个自己的半自动orm框架,加深对orm的使用和理解,顺便记录下自己的学习历程。 本篇博客的orm框架开发环境为Spring4.3.0.RELEASE+jdk1.8,基于注解的形式开发,目前提供的功能较为简单,暂未开发出参数转换器,首先介绍对开发中涉及到的注解,做一个简要的介绍...

qq_32459653的博客 1615

储能BMS三级架构解析:从模组到堆的管理与控制

本文深入解析了储能电池管理系统(BMS)的核心三级架构,即电池模组管理层(BMU)、电池簇管理层(BCMU)和电池堆管理层(BAMS)。文章详细阐述了各级的职能分工与协同机制,从底层数据采集、均衡管理,到中层信息汇总与安全控制,再到顶层的系统总控与对外通信,揭示了该架构如何实现大规模储能电池的精细化、安全与高效管理。

ik678901234的博客 472

手把手教你用TI MOSFET搭建0-100mA恒流源电路(附避坑指南)

本文详细介绍了如何使用TI MOSFET SI1204D搭建0-100mA恒流源电路,包括基础原理、电路设计、PCB布局和热管理技巧。通过分步指导和避坑指南,帮助工程师实现高精度、低成本的电流控制方案,适用于LED驱动、传感器激励等多种应用场景。

weixin_29310631的博客 486

利用SpringBoot手写属于自己的ORM框架

用springboot手写自己的ORM框架 手写之前让我们简单回顾一下ORM框架,就是java对象和数据库字段之间的转化,当我们封装好了ORM之后,我们就可以直接操作对象,而不用管后台和数据库打交道的一些细节。 但是如果我们要操作对象,那么对象和数据库表,字段如何映射的呢,想想hibernate和mybatis是不是在实体中加入一些注解呢,也有xml配置的,本文中主要讲注解。废话有点多,直接开撸 ...

xiaming08240316的专栏 856

从信号到振镜:STM32F103 + XY2-100协议 + AM26LS31芯片的激光打标/雕刻系统信号链搭建指南

本文详细介绍了基于STM32F103微控制器和XY2-100协议的激光打标/雕刻系统信号链搭建方法。从数字振镜控制协议解析、AM26LS31差分驱动电路设计到信号完整性优化,提供了一套完整的解决方案,帮助工程师实现高精度激光控制系统的开发与调试。

weixin_42536058的博客 237

手把手教你写一个java的orm(完)

生成sql:select 上一篇讲了怎样生成一个sql中where的一部分,之后我们要做事情就简单很多了,就只要像最开始一样的生成各种sql语句就好了,之后只要再加上我们需要的条件,一个完整的sql就顺利的做好了。 现在我们开始写生成查询语句的sql。一个查询语句大致上是这样的: SELECT name, id, create_date, age, mark, status FROM user 这...

weixin_30802273的博客 172

分享6款文字语音生成驱动虚拟数字人说话的开源项目

对定性和定量结果的大量实验表明,我们的算法实现了高质量的照片真实感对话头部视频,包括根据语音节奏进行的各种面部表情和头部运动,并超过了最先进的水平。我们的系统包含三个阶段。当然,FACEGOOD Audio2face 部分也可根据自己的喜好进行重新训练,比如你想用自己的声音或其它类型的声音,或者不同于 FACEGOOD 使用的模型绑定作为驱动数据,都可以根据下面提到的流程完成自己专属的动画驱动算法模型训练。兼顾生成的韵律和外表的相似,除了面部,考虑到了头部的动作,虑了背景区域的artifact。

南七小僧的学海无涯 3051

WordPress插件depicter存在SQL注入漏洞(CVE-2025-2011)

Depicter 是一款功能强大的 WordPress 幻灯片插件,允许用户通过直观的拖放编辑器轻松创建响应式滑块、轮播和动态内容展示。该插件提供丰富的模板库、动画效果和图层控制,支持多媒体内容嵌入(如图片、视频、HTML),并优化了移动端显示和SEO性能。其无代码操作界面适合非技术用户,同时提供高级自定义选项,适用于产品展示、营销推广和视觉叙事等多种场景,是提升网站视觉吸引力的高效工具。

m0_50125527的博客 556

InnoDB 内存结构总览:Change Buffer、AHI 与 Log Buffer

本文系统梳理InnoDB内存结构中的ChangeBuffer、Adaptive Hash Index(AHI)与LogBuffer。ChangeBuffer缓存二级索引页不在内存时的变更,减少随机IO,仅在页加载时合并;聚簇索引修改立即执行,因数据本身不可延后。AHI基于高频等值查询自动构建哈希索引,加速定位,仅支持等值操作。LogBuffer为redo log内存缓冲,配合磁盘日志实现崩溃恢复。三者均服务于性能优化,其中ChangeBuffer与AHI共享BufferPool空间,而LogBuffer独立

bksczm的博客 303

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

上一篇: 程序员的心理疾病
下一篇: 200行Java代码实现依赖注入框架
码洞
博客等级 码龄10年 41粉丝 137原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值