原因
和其他ORM不同,Jimmer采用不可变对象作为实体对象
初衷
禁止循环引用
ORM常常伴随一个非常头疼的问题:循环引用。
-
包含循环引用的数据结构无法被简单地JSON序列化,这对HTTP API的设计是致命缺陷。
-
其实,这个问题并非无法解决,比如,可以利用JSON序列化库 (如Jackson) 的高级功能序列化包含循环引用的数据结构。但这毫无意义,因为序列化后的结果包含了特殊的扩展内容,并不能为客户端的所有编程语言所理解。
作为一个ORM,Jimmer当然允许用户在实体类型之间定双向关联,这是ORM一个强大的能力。
然而,在实例化对象时,一旦开发人员决定了聚合根的类型,那么被创建的数据结构就只能是以聚合根对象作为起点的单向树。
即,实体类型之间可以定义双向关联,保留两个方向的可能性。待实例化对象时,再按照业务特性选择使用其中一个关联方向。
Jimmer中任何实体相关数据结构绝不包含循环引用,因此,无需做任何转化,就可以直接参与JSON序列化进而被HTTP协议传输,并保证HTTP传递的数据的简单性以保证任何编程语言都可以理解。
这为应用开发带来了极大的简化。
演示
- Java
- Kotlin
TreeNode aggregateRoot = Immutables.createTreeNode(aggregateRootDraft ->
aggregateRootDraft
.setName("Aggregate root")
.addIntoChildNodes(childDraft ->
childDraft
.setName("Child")
// 抛出CircularReferenceException
.setParent(aggregateRootDraft)
)
);
val aggregateRoot = TreeNode {
val that = this
name = "Aggregate root"
childNodes().addBy {
name = "Child"
// 抛出CircularReferenceException
parent = that
}
}
上面代码使用了一个名为TreeNode
的类型,该接口是Jimmer根据用户定义的类型TreeNode
自动生成的接口类型。
读者可先行忽略这个自动生成的接口,后续文档Draft会对其做出介绍
这段代码会导致异常:org.babyfish.jimmer.CircularReferenceException。
Jimmer对象是不变对象,用户既无法在创建对象时构建循环引用,也无法通过后期修改来形成循环引用,这个品质将会被永远保留。
其他好处
除了保证对象之间绝对没有循环引用外,不可变对象还有很多其他好处。
-
多线程安全性,这点很好理解,不用解释。
-
对于Set、Map这类依赖hashCode的集合容器而言,迫切需要对象的不可变性。
一旦Set或Map (作为Key) 这样的hashCode敏感集合容器持有了可变对象,开发人员就必须小心翼翼地确保不去修改被这些容器共享的数据。如果不慎犯了错误,通常需要debug跟踪来发现问题,这往往会浪费时间影响效率。(其实,hashCode敏感集合容器持有可变对象是一种常见的行为,也可以说大部分Java代码都是不严密的,只是开发人员避开了此问题而已)
-
实际开发中还有一些长期持有对象的场合,虽然不依赖hashCode,但也会因长期持有对象而导致问题,比如:
-
利用WebSession中长期持有某些数据
-
利用一级缓存,即,使用JVM内存的进程内缓存,长期持有某些数据
细心的开发人员肯定不希望WebSession或Cache中被长期持有数据的引用和泄漏到用户代码中的引用共享数据结构,进而导致不可控的彼此干扰。
因此,对WebSession或JVM内部Cache这类长期持有数据的结构进行读写操作时,细心的开发人员会将可变数据结构复制一次再进行保存或返回。其中,写入时复制尚可接受,但每次读取都复制一次显得昂贵。可见
-
使用可变对象,是否需要复制对象以保证必要安全性,依赖于开发人员的风险预知能力,这需要开发人员有一定经验并生性谨慎。然而,即便预知了风险,解决之道也没有客观标准,过于严格会导致过多不必要复制,形成浪费,过于宽松会导致复制不足,出现BUG (团队人数越多,越容易出错)。而且,对于有一定体量的数据而言,团队内部对这种保护机制的严格程度也常有争论,具备很强的主观性。
-
使用不可变对象,只 有数据结构被“修改” (这里的修改是伪修改,并非真正的修改当前数据,后续文档会详细讨论之) 时才复制部分数据 (Jimmer/Immer内部对此有优化,被修改的对象会被复制,从其上级对象开始到根节点为止的所有祖先节点也会被复制,然而,其余所有未变的分支仍然共享重用) 得到新的聚合根引用,其余情况一律简单共享原始引用即可。具备非常严格的无可争议的客观性。
提示毫无疑问,基于客观规律进行开发,必然优于基于主观感觉进行开发,这非常重要。
-