Skip to main content

Reason

Unlike other ORMs, Jimmer uses immutable objects as entity objects.

Motivation

Forbid Circular References

ORMs are often accompanied by a very headache problem: circular references.

  • Data structures containing circular references cannot be simply JSON serialized, which is a fatal flaw for HTTP API design.

  • In fact, this problem can be solved, for example, advanced features of JSON serialization libraries (such as Jackson) can serialize data structures containing circular references. But this is meaningless, because the serialized result contains special extended content, which cannot be understood by client programming languages.

As an ORM, Jimmer of course allows users to define bidirectional associations between entity types, which is a powerful capability of ORM.

However, when instantiating objects, once the developer has determined the type of the aggregate root, the data structure created can only be a unidirectional tree with the aggregate root object as the starting point.

That is, bidirectional associations can be defined between entity types, keeping the possibilities of both directions. When instantiating objects, choose to use one of the association directions according to business characteristics.

tip

Jimmer entity-related data structures never contain circular references, so they can be directly involved in JSON serialization and then transmitted via HTTP protocols, while ensuring the simplicity of HTTP transmitted data to ensure that any programming language can understand.

This brings great simplification to application development.

Incorrect Demo

TreeNode aggregateRoot = Immutables.createTreeNode(aggregateRootDraft ->
aggregateRootDraft
.setName("Aggregate root")
.addIntoChildNodes(childDraft ->
childDraft
.setName("Child")
// Throw CircularReferenceException
.setParent(aggregateRootDraft)
)
);
info

The above code uses an interface called TreeNode, which is the interface type automatically generated by Jimmer based on the user-defined type TreeNode.

Readers can ignore this auto-generated interface for now, later documentation Draft will introduce it.

tip

This code will cause an exception: org.babyfish.jimmer.CircularReferenceException.

Jimmer objects are immutable objects. Users can neither build circular references when creating objects, nor form circular references through subsequent modifications. This quality will be preserved forever.

Other benefits

In addition to ensuring absolutely no circular references between objects, immutable objects have many other benefits, including:

  • Multi-thread safety, which is easy to understand and needs no explanation.

  • For collection containers that rely on hashCode, such as Set and Map, object immutability is desperately needed.

    Once mutable objects are held by hashCode sensitive collection containers like Set or Map (as Key), developers must be very careful to ensure that the data shared by these containers is stable. If a mistake is accidentally made, it usually takes debug tracking to find the problem, which often wastes time and affects efficiency. (In fact, hashCode sensitive collection containers holding mutable objects is a common behavior, and it can also be said that most Java code is not strict, developers just avoid this problem.)

  • In actual development, there are some other situations where objects are held for a long time. Although not dependent on hashCode, problems can also arise from holding objects for a long time, such as:

    • Using data persisted in WebSession for a long time

    • Using first-level cache, that is, using process-local cache in JVM memory to persist some data for a long time

    Careful developers certainly do not want references in WebSession or Cache that persist data for a long time to share data structures with references leaked to user code, which would lead to uncontrollable mutual interference.

    Therefore, when performing read/write operations on data structures persisted in WebSession or JVM internal Cache, careful developers will copy mutable data structures once before saving or returning them. Among them, copying when writing is still acceptable, but copying every time when reading is expensive. It can be seen that

    • Using mutable objects, whether it is necessary to copy objects to ensure necessary security depends on the developer's ability to foresee risks. This requires developers to have some experience and be cautious by nature. However, even if the risks are foreseen, there is no objective standard for the solution. Being too strict will lead to too many unnecessary copies and waste, and being too loose will lead to insufficient copying and bugs (the more team members, the easier to make mistakes). Moreover, for data of a certain volume, there are often disputes within the team about the strictness of this protection mechanism, which is highly subjective.

    • Using immutable objects, the data structure is only copied in part when it is "modified" (Here the "modified" is pseudo-modification, not real modification of the current data, which will be discussed in detail in subsequent documents. Jimmer/Immer internally optimizes this: the modified object will be copied, and from its parent object to the root node, all ancestor nodes will also be copied, while all other unchanged branches still share and reuse the original) to get a new aggregate root reference, and simply share the original reference in all other cases. It has a very strict, indisputable objectivity.

    tip

    Undoubtedly, development based on objective laws is bound to be superior to development based on subjective feelings, which is very important.