Skip to main content

Dynamicity

info

The example code in this article uses a type called BookDraft, which is an interface type automatically generated by Jimmer based on the user-defined type Book.

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

Basic Concepts

1. A few properties

Book book = Immutables.createBook(draft -> {
draft.setName("Learning GraphQL");
});

In this case, the JSON for the books object would be like follows

{"name": "Learning GraphQL"}

2. More Properties

Book book = Immutables.createBook(draft -> {
draft.setName("Learning GraphQL");
draft.setEdition(1);
draft.setPrice(new BigDecimal("49.99"));
});

In this case, the JSON for the books object would be like follows

{
"name": "Learning GraphQL",
"edition": 1,
"price": 49.99
}

3. Include Association

Book book = Immutables.createBook(draft -> {
draft.setName("Learning GraphQL");
draft.setEdition(1);
draft.setPrice(new BigDecimal("49.99"));
draft.applyStore(store -> {
store.setName("O'REILLY");
store.setWebsite("https://www.oreilly.com/");
});
});

In this case, the JSON for the books object would be like follows

{
"name": "Learning GraphQL",
"edition": 1,
"price": 49.99,
"store": {
"name": "O'REILLY",
"website": "https://www.oreilly.com/"
}
}

4. More Associations (Larger Breadth)

Book book = Immutables.createBook(draft -> {
draft.setName("Learning GraphQL");
draft.setEdition(1);
draft.setPrice(new BigDecimal("49.99"));
draft.applyStore(store -> {
store.setName("O'REILLY");
store.setWebsite("https://www.oreilly.com/");
});
draft.addIntoAuthors(author -> {
author.setFirstName("Eve");
author.setLastName("Procello");
author.setGender(Gender.FEMALE);
});
draft.addIntoAuthors(author -> {
author.setFirstName("Alex");
author.setLastName("Banks");
author.setGender(Gender.MALE);
});
});

In this case, the JSON for the books object would be like follows

{
"name": "Learning GraphQL",
"edition": 1,
"price": 49.99,
"store": {
"name": "O'REILLY",
"website": "https://www.oreilly.com/"
},
"authors": [
{
"firstName": "Eve",
"lastName": "Procello",
"gender:" "FEMALE"
},
{
"firstName": "Alex",
"lastName": "Banks",
"gender:" "MALE"
}
]
}

5. Deeper Associations (Larger Depth)

Here is the translation to English:

Unlike the previous examples, here we choose BookStore as the aggregate root for the data structure, rather than Book.

BookStore store = Immutables.createBookStore(draft -> {
draft.setName("O'REILLY");
draft.setWebsite("https://www.oreilly.com/");
draft.addIntoBooks(book -> {
book.setName("Learning GraphQL");
book.setEdition(1);
book.setPrice(new BigDecimal("49.99"));
book.addIntoAuthors(author -> {
author.setFirstName("Eve");
author.setLastName("Procello");
author.setGender(Gender.FEMALE);
});
book.addIntoAuthors(author -> {
author.setFirstName("Alex");
author.setLastName("Banks");
author.setGender(Gender.MALE);
});
});
});

In this case, the JSON for the store object would be like follows

{
"name": "O'REILLY",
"website": "https://www.oreilly.com/",
"books": [
{
"name": "Learning GraphQL",
"edition": 1,
"price": 49.99,
"authors": [
{
"firstName": "Eve",
"lastName": "Procello",
"gender:" "FEMALE"
},
{
"firstName": "Alex",
"lastName": "Banks",
"gender:" "MALE"
}
]
}
]
}

Characteristics of Dynamic Objects

Dynamic object may lack any property, or in other words, dynamic object are not required to have all properties set.

caution

In dynamic object, missing properties and properties set to null are completely different things.

  • Missing property: The value of the property of the object is unknown, the current business is not interested in it

  • Property set to null: The value of the property of the object is known, it really is nothing

In static POJOs, the two are actually indistinguishable. What's worse, developers often intentionally or unintentionally confuse the two by taking advantage of Java's lack of null safety.

The concept of dynamic object is very important and key to understanding Jimmer!

For missing properties:

  • Using code to access them directly will result in an exception org.babyfish.jimmer.UnloadedException

    note

    If the reader has Hibernate experience, think of this as org.hibernate.LazyInitializationException.

  • In Jackson serialization, they will be automatically ignored.

    info

    This requires some configuration for Jackson. Since this detail is very important, it has been made into a separate document. Please refer to Work with Jackson.

Interaction with ORM

Dynamicity is an intrinsic characteristic of Jimmer objects and is universally applicable. Both the ORM framework itself and developers can easily build dynamic objects for each other to use.

  • Jimmer creates dynamic objects and returns them to developers

    That is, query data structures of any shape. This feature is called object fetchers.

  • Developers create dynamic objects and pass them to Jimmer

    That is, save data structures of any shape. This feature is called save commands.

Object Fetchers

Jimmer creates dynamic objects and returns them to developers

info
Compare to GraphQL
  • GraphQL is based on HTTP services, which can only be experienced if it crosses the boundaries of HTTP services. In Jimmer, this is the underlying API for ORM, and you can use this capability in any code logic.
  • Until now, the GraphQL protocol does not support recursive queries on self-associated properties with infinite depth; And Jimmer supports

Save Commands

Developers create dynamic objects and pass them to Jimmer

  • Upper right corner: The user passes in a data structure of any shape, and asks Jimmer to save it.

    There is an essential difference between this and the save method of other ORM frameworks. Taking JPA/Hibernate as an example, whether the scalar properties of the entity need to be saved is controlled byColumn.insertable andColumn.updatable, and whether association properties need to be saved is controlled byOneToOne.cascade,ManyToOne.cascade,OenToMany.cascade andManyToOne.cascade. However, no matter how the developer configures it, the shape of the data structure that JPA/Hibernate can save for you is fixed.

    Jimmer adopts a completely different approach. Although the saved jimmer object is strongly typed, it is dynamic (that is, not setting the object property and setting the object property to null are completely different things), Properties that are set are saved and properties that are not set are ignored, so that data structures of any shapes can be saved.

  • Upper left corner: Query the existing data structure from the database for comparison with the new data structure specified by the user.

    The shape of the data structure queried from database is same with the shape of new data structure give by user. Therefore, the query cost and comparison cost are determined by the complexity of the data structure specified by the user.

  • Below: Compare the old and new data structures, find DIFF and execute the corresponding SQL operations:

    • Orange part: For entity objects that exist in both old and new data structures, if some scalar properties change, modify the data
    • Blue part: For entity objects that exist in both old and new data structures, if some associations change, modify the association
    • Green part: For entity objects that exist in the new data structure but do not exist in the old data structure, insert data and create the association
    • Red part: For entity objects that exist in the old data structure but not in the new data structure, dissociate this object, clear the association and possibly delete the data
tip

The purpose of this function: take the data structure of any shape as a whole, and use one line of code to write it into the database, no matter how complicated the intermediate details are, you don't have to care.

If you know React or Vue in the web field, it is not difficult to see that this function is very similar to `Virtual DOM diff`.