天天看点

一个让开发人员仅通过声明式代码的方式实现智能数据结构的Java框架BabyFish是什么 框架架构 Java环境要求 离线文档 框架功能 许可:LPGL3.0 鸣谢 历史 联系我,提出意见和期望

一个朋友的框架发布1.1版本,和1.0在运行时生成额外字节码不同,1.1在编译时通过maven插件生成额外字节码,因此避免了暴露微量的设计模式给用户,让用法更简单粗暴,性能也更高。很大一部分不太像框架了,而更像是java语言的扩展,是个重大升级。此外,充分吸取1.0的教训,给出架构图并,让所有文档中英文双版,避免别人不明全局。另外,不再兼容java7。

babyfish是一款让开发人员仅通过声明式代码的方式实现智能数据结构的java框架; 其功能非常丰富,请阅读下面这张图片以对其大体架构有个粗略了解。

一个让开发人员仅通过声明式代码的方式实现智能数据结构的Java框架BabyFish是什么 框架架构 Java环境要求 离线文档 框架功能 许可:LPGL3.0 鸣谢 历史 联系我,提出意见和期望

在这张图中,有5个功能点被红色星号标注:

<a href="https://github.com/babyfish-ct/babyfish/blob/master/readme_zh_cn.md#unstablecollectionelements">unstable collection elements</a>

<a href="https://github.com/babyfish-ct/babyfish/blob/master/readme_zh_cn.md#bubbleevent">bubble event</a>

<a href="https://github.com/babyfish-ct/babyfish/blob/master/readme_zh_cn.md#objectmodel4java">objectmodel4java</a>

<a href="https://github.com/babyfish-ct/babyfish/blob/master/readme_zh_cn.md#objectmodel4jpa">objectmodel4jpa</a>

<a href="https://github.com/babyfish-ct/babyfish/blob/master/readme_zh_cn.md#querypath">query path</a>

,它们是本框架最重要的5个功能点。

babyfish1.1需要java8

文档

描述

图文并茂地详细讲述部分重要功能(请使用高版本ie或其它浏览器打开)。

逐个介绍每个demo,并给出建议的阅读顺序(请使用高版本ie或其它浏览器打开)。

提供一种更好的实现国际化的方法。和传统的使用在运行时刻报错的java.util.resourcebundle进行开发不同, 请类型国际化的所有错误均在编译时刻报告给开发人员(此功能需要编译时字节码增强)。

更好的事件通知模型, 如同.net的委托一样,相比于java bean规范的建议更简单、更节约、更强大 (此功能需要编译时字节码增强)。

开发人员往往需要覆盖“public boolean equals(object o)”方法, 通常他们有两种检查参数对象的类型是否和当前对象(this)的类型相同的办法。

过于严格的办法: if (this.getclass() != o.getclass()) return false;

过于宽松的办法:if (!(o instanceof ${thisclass})) return false;

不幸的是,两种都是错的,本功能提供一种实现“equals”方法的完美方法, 其相应的demo指出它们各自的问题并给出正确的解决方案。

此功能用于深度优先或广度优先的对象图遍历,遍历过程中提供丰富的遍历上下文变量。

“x”代表“extensible”, 为了实现一些不可思议的功能,x集合框架扩展了java集合框架的接口并给出一套全新的实现。

和“org.apache.commons.collections4.bidimap”类似, 为了让“java.util.map” 支持值(非键)的唯一性; x集合框架支持bidmap;同理,也支持bidilist。

java集合框架支持哈希结构和红黑树结构,例如hashmap, treeset and treemap; 它们的好处是高性能,但是有一个缺陷,当某个对象被作为元素添加到set或被作为键添加到map,它的数据就不能被修改了。

x集合框架支持一个“不稳定集合元素的功能“,即便某个对象被作为元素添加到set或被作为键添加到map, 它也可以被修改,因为与之相关的所有set和map集合均会在其被修改时自动修改。

集合类型

不稳定集合元素能力体现

set

不稳定元素

map

不稳定键

bidilist

bidimap

不稳定值

“ma”代表“modification aware”,ma集合框架扩展至x集合框架,用户支持数据变更事件通知。 对于每一个被修改的元素或键值对,两个事件将会被触发,其中一个在修改之前触发,而另外一个在修改后触发。 这个功能和关系型数据库的行级触发器极为类似。

在babyfish集合框架中,集合对象的数据不仅会被显式的api调用修改,有时也会被系统自动修改。其中,最典型的代表就是“不稳定集合元素”导致的集合被自动修改的案例了。 这种不是由开发人员发起的、自动的、隐式的集合修改照样会导致事件的触发。

java集合框架是支持集合视图的,例如

“java.util.navigablemap”支持headmap、tailmap、submap和descendingmap方法。

“java.util.navigableset”支持headset、tailset、subset、descendingset和descendingiterator方法。

“java.util.list”支持sublist和listiterator方法。

“java.util.map”支持keyset、values和entryset方法。

“java.util.collection”支持iterator方法。

这表示开发人员可以基于原始集合或视图集合创建新的视图集合,所有视图集合均和原始集合共享同一份数据,其中任何一个被修改时,原始集合均会受到影响。

ma集合框架支持一个叫“冒泡事件“的功能,当视图集合被修改的时候,事件将会在此视图对象上被触发;然后,事件向上冒泡,让上一层视图集合触发事件;以此类推,最终,冒泡到根部的原始集合,事件将在原始集合上被触发。

我们知道,orm框架常常支持一种叫延迟加载的技术,延迟集合被允许是虚假的,并不包含任何数据,直到第一次被开发人员使用时,才会通过io操作从外界获取数据从而转变成真实集合。 这是一个很强大的功能,如此好的功能如果只能在orm领域使用,就是太浪费了。

为了让这种强大的功能可以在任何场合被使用(而非仅仅在orm实现中实现),babyfish支持抽象而普适的延迟集合框架。

没有相应的demo,因为这是框架的spi,并非api。

java集合框架附带了一个强大的工具类java.util.collections, 此类提供很多静态方法以创建神奇的集合代理对象。

babyfish集合框架扩展了java集合框架的接口,类似的,它需要提供自己的工具类:org.babyfish.collection.macollections"。

objectmodel为babyfish框架的核心功能,也是我开发此框架的原因。

objectmodel用于构建智能数据结构,又可分为两个部分

<a></a>

objectmodel4java:在java语言层面支持智能数据结构,这是本章节将重点介绍的功能。

java开发人员往往将数据模型类声明成除了getter和setter访问器外没有任何逻辑的简单类 (说直白点,就是c语言结构体),这种简单的类所描述的数据结构的智能性和方便性非常有限。 举一简单的例子,对于两个对象之间的双向关联,开发人员其中一端时,另外一端没有对应地修改, 就会导致数据不一致,通过某个对象的关系能导航到另外一个对象,但反过来不成立。

objectmodel4java在为数据结构引入智能性的同时,并没有增加开发的复杂程度。毕竟,除了getter和setter访问器 之外无任何逻辑的数据类写起来是非常简单的,这种简单的代码书写方式早已深入人心。objectmodel4java 只需要开发人员在这些简单的数据类上添加一点注解,借助于编译时的字节码增强maven插件,开发人员便可以得到功能强大的数据模型。

下面我们来看一个实际的例子,创建部门和员工之间的双向一对多关系。

一个部门可以包含多个员工,且这些员工是有先后顺序的。

假设现有6个对象,其变量名分别为department1, department2, employee1, employe2, employee3和employee4,它们的数据如下:

部门对象

员工对象

变量名

employees

department

index

department1

[ employee1, employee2 ]

employee1

employee2

1

department2

[ employee3, employee4 ]

employee3

employee4

此处,仅举一例,用户使用如下代码修改department1对象的employees集合

此语句视图将employee3插入到department1.employees集合的最前面, 为了维持数据结构一致性,objectmodel4java将会执行如下调整。

自动将employee3从department2.employees集合中删除。

自动将employee3.department设置为department1。

自动将employee1的index从0变成1。

自动将employee2的index从1变成2。

自动将employee4的index从1变成0。

最终,数据结构变成这样

[ employee3, employee1, employee2 ]

2

[ employee4 ]

在上个例子中,关联字段使用了java.util.list集合,除此之外,集合关联属性还可以使用java.util.set或java.util.map。 接下来的例子中,我们使用java.util.set来描述公司和投资人之间的双向多对多关系。

一个公司可以由多个投资人投资。

假设现有3个对象,其中的一个company类型的对象,变量名为apple,还有两个investor对象,变量名为steve和sculley。 它们的数据如下:

公司对象

投资人对象

shortname

investors

name

companies

apple

[ steve, sculley ]

steve

[ apple ]

sculley

现在开发人员执行如下一句代码:

[ sculley ]

[]

为了让实体对象的集合字段能具备以上所有能力,babyfish扩展lazy集合框架,给出了 自己的hibernate集合实现。

为了在查询业务支持支持动态的贪懒加载(关联对象的抓取方式由业务层和表示层的通过传递参数动态决定,而非在数据层内部硬编码实现), babyfish支持一个叫“query path”的功能,它看起来非常类似于

<a href="http://api.rubyonrails.org/classes/activerecord/querymethods.html#method-i-includes">active record的贪婪加载能力</a>

<a href="https://msdn.microsoft.com/en-us/library/bb738708(v=vs.110).aspx">ado.net entity framework的贪婪加载能力</a>

active record和ado.net entity framework的包含路径是类型不安全的字符串, 错误路径的错误要等到运行或测试程序才会被报告。 而query path则通过maven插件在编译时生成query path元模型源码的方式让路径强类型化,错误的路径会在编译时报错。

querypath不仅仅可以以当前被查询对象为核心对任意深度和广度的的关联属性进行贪婪加载,还可以对延迟的标量属性(不一定但往往是大字段)进行贪婪加载。

querypath还可以对被查询对象本身以及对象之间的集合关联进行排序。

hibernate有一个性能缺陷。当某个查询既包含对集合关联的抓取(join fetch)又具备分页条件时, hibernate无法通过sql语句实现分页查询,而是先不分页地查询出所有满足条件的对象,然后在内存中完成分页筛选, 同时在日志文件中打印如下警告:

"firstresult/maxresults specified with collection fetch; applying in memory!"

针对oracle数据库,babyfish能解决此问题,只需要开发使用babyfish扩展过的oracle方言即可,如下:

<a href="https://github.com/babyfish-ct/babyfish/blob/master/src/babyfish-hibernate-extension/src/main/java/org/babyfish/hibernate/dialect/oracle8idialect.java">org.babyfish.hibernate.dialect.oracle8idialect</a>

<a href="https://github.com/babyfish-ct/babyfish/blob/master/src/babyfish-hibernate-extension/src/main/java/org/babyfish/hibernate/dialect/oracle9idialect.java">org.babyfish.hibernate.dialect.oracle9idialect</a>

<a href="https://github.com/babyfish-ct/babyfish/blob/master/src/babyfish-hibernate-extension/src/main/java/org/babyfish/hibernate/dialect/oracle10gdialect.java">org.babyfish.hibernate.dialect.oracle10gdialect</a>

2008年8月: 我有了某些点子呢, 开始利用工作外的业余时间开发此框架。 2015年10月: 完成第一个版本1.0.0并上传至github 2016年6月: 完成1.1.0版本。

2016年6月25日于成都。