长沙教育建设信息网站,宿松网站建设公司,长春省妇幼网站做四维,wordpress开ORM框架为什么不香#xff1f; 对ORM框架的偏见 看了一些MyBaties与Hibernate进行对比的文章。可能是因为一些Hibernate历史原因#xff0c;国内对于Hibernate普遍存在偏见#xff0c;我摘抄了几点#xff1a; 1. hibernate是全自动#xff0c;而mybatis是半自动 hibernat… ORM框架为什么不香 对ORM框架的偏见 看了一些MyBaties与Hibernate进行对比的文章。可能是因为一些Hibernate历史原因国内对于Hibernate普遍存在偏见我摘抄了几点 1. hibernate是全自动而mybatis是半自动 hibernate完全可以通过对象关系模型实现对数据库的操作拥有完整的JavaBean对象与数据库的映射结构来自动生成sql。而mybatis仅有基本的字段映射对象数据以及对象实际关系仍然需要通过手写sql来实现和管理。 2. sql直接优化上mybatis要比hibernate方便很多 由于mybatis的sql都是写在xml里因此优化sql比hibernate方便很多。而hibernate的sql很多都是自动生成的无法直接维护sql 3. 应用场景 MyBatis 适合需求多变的互联网项目例如电商项目、金融类型、旅游类、售票类项目等。 Hibernate 适合需求明确、业务固定的项目例如 OA 项目、ERP 项目和 CRM 项目等。 也不知道是不是因为这些对Hibernate的偏见导致大家对ORM框架也普遍存在偏见。 现状是不论大小公司国内清一色地使用MyBaties。有时我都不敢说我喜欢使用ORM框架。 本文并不是一篇为Hibernate洗地的文章而是介绍另一款比较小众的ORM框架Ebean。 领域问题分析 介绍Ebean之前我们需要弄清楚一个问题为什么会有MyBaties和ORM这些框架对于这个问题我们无从下手那么我们将问题倒置如果没有这些框架会怎么样 问题倒置的好处是我们立马就有了可下手的方向。我们找到了不使用框架的情况下Java代码与数据库进行交互的代码 public static void viewTable(Connection con) throws SQLException {String query select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES;try (Statement stmt con.createStatement()) {ResultSet rs stmt.executeQuery(query);while (rs.next()) {String coffeeName rs.getString(COF_NAME);int supplierID rs.getInt(SUP_ID);float price rs.getFloat(PRICE);int sales rs.getInt(SALES);int total rs.getInt(TOTAL);}} catch (SQLException e) {JDBCTutorialUtilities.printSQLException(e);}} 这样的代码存在什么问题呢 1. 代码不易于维护你需要知道每个字段在数据库中的类型才知道该调用ResultSet的哪个方法2. 代码重复像COF_NAME这样的字段名在整个代码仓库可能会飘落得到处都是3. 不安全手工拼装SQL带来的安全问题不须多言。 以上三个问题我们称之为ORM领域核心问题。 从目前市面上的解决方案来看解决这些核心问题的方案至少需要包含以下三个能力 1. 自动映射在数据库与Java对象之间自动进行字段类型映射而不是手工进行映射2. 自动生成SQL根据Java API自动生成SQL而不是手写3. 自动执行自动执行而不是手工直接操作JDBC接口。 MyBaties如何解决核心问题 MyBaties通过Mapper实现自动映射、自动执行。但是并没有实现自动生成SQL也正是它称之为Mapper的原因。 public interface PersonMapper {Insert(Insert into person(name) values (#{name}))public Integer save(Person person);Select(Select personId, name from Person where personId#{personId})Results(value {Result(property personId, column personId),Result(propertyname, column name),Result(property addresses, javaType List.class,column personId, manyMany(select getAddresses))})public Person getPersonById(Integer personId);// ...
} 我个人很好奇为什么MyBatise没有使用JPA规范而是自己又创造一种注解。 MyBaties另一种通过XML的配置方式配置的本文就不介绍了。想想当年Spring也是使用XML进行配置Bean的现在好像已经没有人这么干了。 说到底MyBaties也是一个ORM框架。MyBatis-Plus插件的流行程度正好证明了这一点。所以大家没有必要对ORM框架抱有偏见。:P JPA小传 在介绍Ebean前我们回顾一下JPA的历史。 JPA全称Java Persistent APIJava持久化API。它只是规范并不是具体技术其中Hibernate应该是最出名的实现之一了。Ebean也是具体实现之一。 值得注意的是我们应该可以认定这个规范没有限制我们只能用它将数据持久化到数据库思路要打开。即使我们绝大多数时候只用它持久化数据到数据库中。 它的版本历史如下 • 2006.5.11: JPA1.0作为JSR220规范的一部分发布。Ebean同年11月发布Bate测试版本• 2009年JPA2.0发布• 2013年和2017年JPA2.1和JPA2.2分别发布• 2019年JPA更名为Jakarta Persistence。• 2020年和2022年Jakarta Persistence3.0和3.1版本分别发布。 此部分内容来自 https://handwiki.org/wiki/Java_Persistence_API https://en.wikipedia.org/wiki/Jakarta_Persistence Ebean一款被低估的ORM框架 Ebean最早于2006年11月13日发布了Bate测试版本。然后v1.0.0版本在2008年11月24日由它的作者Rob Bygrave发布到了SourceForge。 后来Ebean迁到了Github。目前最新版本是2023年11月22日。从Github组织来看Ebean的主要维护人只有Rob Bygrave。18年的坚持不得不佩服作者的毅力。 但这也成为我认为Ebean目前最大的问题如果作者突然有个什么三长两短社区应该如何应对。即使它目前有将近100个contributor。 个人在2011左右接触到Play Framework的时候了解到Ebean。Play框架使用Ebean作为它的JPA实现。当时就被它优秀的设计所吸引。 但是真正让我使用的是它的设计非常符合我的DDD口味同时鼓励充血模型的实体。 Ebean是如何实现自动映射的 在上文中我们已经介绍了ORM领域核心问题自动映射。这是JPA规范要解决的最重要的问题之一。Ebean实现了JPA规范定义的注解。 用户在字段上加上JPA的注解然后在真正需要映射的时候Ebean自动进行映射。以下是定义一个实体Person它对应的表名是people。实体字段与数据库字段也有相应的映射 Entity
Table(namepeople)
public class Person { IdGeneratedValueprivate int id; Column(namefirst_name, length10)private String firstName; Column(namelast_name, length10)private String lastName; 在实体类上中定义Java类与数据库之间的映射关系最大的好处就是DDL语句和数据库迁移SQL可以被工具自动生成。 假如你在Person类中增加一个email的字段Ebean的的DDL特性就可以为你生成相应的建表语句。而Ebean的Migration工具就为你生成相应的alert语句。当然这些语句的执行时机还是由用户控制。 JPA本身提供了大量注解Ebean还扩展了一些有用的注解 1. DbJson注解自动将对象转成JSON进行存储。如果你所使用的数据库支持JSON模式你会非常喜欢这个注解有了这个注解你可能就不需要值对象注解了2. WhenCreated注解自动设置对象的创建时间3. WhenModified注解自动设置对象的修改时间4. DbMap注解自动将Map结构构建数据库映射到不同的数据类型如果是Postgre就映射到HSTORE其它数据库则映射到VARCHAR。5. SoftDelete 软删除注解当调用实体的delete方法时只是软删除。只需要在实体中增加一个字段 SoftDeleteboolean deleted; 更多相关信息https://ebean.io/docs/mapping/ Ebean是如何自动生成SQL并执行的 以下我们通过一个实例来展示Ebean相关的能力。 // Database是Ebean与数据库进行交互的主要接口
Autowired
Database database;
Test
public void crud() {Person customer new Person();customer.setFirstName(Jack);customer.setLastName(J);// 这是为了让大家对Ebean的database类有一个感性认知database.save(customer);// 实际应用中我通常是在Person类中定义一个save方法并在内容调用database.save(this)。// 最终就是实现这样的调用效果customer.save()// 批量执行存储。你猜这里应该是生成一条语句还是多条语句database.saveAll(customerList);// 根据ID查询对象。Ebean生成相应的select-where语句并执行Customer customerA database.find(Customer.class, 1);// 当然你也可以只查询其中一个字段的值Ebean将生成并执行// select first_name from people where id1;database.find(Customer.class).select(first_name).where().idEq(1).findSingleAttribute();customerA.setFirstName(Jane);// Ebean会识别出customerA要做的是修改而不是创建新的记录。所以生成alert语句并执行。database.save(customerA);// 当然少不了大家关心的能否执行原生SQLString sql select id, first_name from customer where first_name like ?;Customer customer database.findNative(Customer.class, sql).setParameter(Jo%).findOne();// 另有时我们会想念DTO则可以这么写ListCustomerDto beans database.findDto(CustomerDto.class, select id, first_name from customer where first_name :name).setParameter(name, Rob).findList();// CustomerDto是需要提前定义好的。// 删除记录database.delete(customer);} 至此已经把Ebean的解决方案介绍完成由于篇幅有限还请感兴趣的同学到官网学习。 Ebean的实体类增强技术 在Ebean的官网或者一些网上的文章你会发现只要实体类继承了Ebean的BaseModel类都会自动多出save方法以及其它方法。这Lombok与类似只要加一个Setter注解类中就自动出多了相应的setter方法。 又或者你会看到 Person contact new QContact().firstName.equalTo(rob).findOne(); QContact类是由Ebean生成的在实体类前加一个Q字母代表查询类方便用户使用链式调用来查询自己想要数据。而不是需要像database.find(Customer.class).select(first_name)这样手工写字段名。 发生以上的魔法是因为Ebean使用了增强Enhancement技术。这项技术必须嵌入到我们的IDE和构建工具中否则相关代码的编译都不可能通过。 Enhancement技术虽然让我们少写代码但是我们也要认清这门技术所带来的成本它使我们的开发环境强依赖相应的插件。比如Maven必须要安装它的插件才能构建通过、IDE必须安装插件才能正常写代码。 幸运的是我们可以选择不使用它的编译时生成的代码。IDE也就不需要安装相应的插件了。 我个人宁愿自己在实体中手写save方法也不使用这项技术生成。另一个重要的考虑因素是我不希望领域实体类依赖于具体实现技术。 然而运行时还是必须加上agent即-javaagent:路径/ebean-agent.jar以便Ebean对实体进行脏检查和懒加载支持。 使用经验 以下是一些个人的使用经验仅供参考 1. JPA的所有API并不是每一个都必须用到。比如字段上的Basic(fetchFetchType.LAZY)懒式加载我就不建议使用。因为在实际工作中你不能确保每个人都理解懒式加载的应用场景比如它的JPQL我们完全没有必要又另学一种SQL再者Java API的调用方式才应该是推荐2. 使用Java配置类对Ebean进行配置而不是使用官网介绍properties配置。只有这样才足够灵活应对将来的多数据源需求3. 在实体类中不要直接使用Ebean的技术而是在实体类中调用repository接口再由repository的实现调到Ebean。 如果各位读者想看更多的Ebean如何实现DDD repository的文章请点赞并转发。 关于DDD的文章 我是如何将同事的代码改成DDD风格的这十年我所经历的领域驱动设计DDD领域驱动设计DDD下没有POJO