Skip to main content

Feature Introduction

Concept

Save instructions allow developers to save data structures of any shape, rather than save simple objects.

By default, when AssociatedSaveMode is set to REPLACE, Jimmer will completely replace the existing data structure in the database with the structure being saved, as shown in the figure:

  • Top right: Users pass in a data structure of any shape for Jimmer to write to the database.
  • Top left: Query the existing data structure from the database to compare with the new data structure passed in by users.

    Whatever shape of data structure the user passes in, the same shape will be queried from the database, ensuring the shapes of old and new data structures are identical. Therefore, the querying and comparison costs are determined by the complexity of the user-provided data structure.

  • Below:Compare the new and old data structures, find the DIFF and execute corresponding SQL operations to make them consistent:
    • Orange parts: For entity objects that exist in both new and old data structures, modify data if scalar properties have changed
    • Blue parts: For entity objects that exist in both new and old data structures, modify associations if they have changed
    • Green parts: For entity objects that exist in the old data structure but not in the new one, decouple this object, clear associations and possibly delete data
    • Red parts: For entity objects that exist in the new data structure but not in the old one, insert data and establish associations
tip

Unlike other ORMs, Jimmer doesn't require describing how data should be saved in the entity model

Jimmer adopts a completely different strategy - its entity objects are not POJOs and can flexibly control the shape of data structures.

That is, entity objects have dynamic properties - not specifying a property for an entity object and setting an entity's property to null are completely different things.

For any entity object, Jimmer will only save the specified properties while ignoring unspecified ones.

Therefore, Jimmer doesn't need to consider data saving behavior during entity modeling, but rather describes the expected behavior at runtime through the data structure being saved itself, providing absolute flexibility.

Scenarios

The UI design for modifying data in an application can be divided into two styles:

  • Fully Commit

    This type of UI often has complex forms and provides a final button. After editing, the user submits all the information in the form at once.

  • Incremental Commit

    This type of UI does not have a submit button. Each time the user completes a local operation, the page automatically submits the changed part, which is a fragmented commit mode.

The greatest value of the Save Command lies in simplifying the development of fully commit mode functionality. For the two different modes, the usage is different.

Fully CommitIncremental Commit

Jimmer automatically handles the internal details, comparing the new and old data to find all differences and executing the relevant modification operations (Jimmer's unique perspective)

Use the Save Command (parameters are often complex data structures)

Business code uses a combination of multiple simple operations to implement complex operations, and the user handles the internal details (the same as traditional methods).

Comprehensively use multiple methods:

Developers need to analyze their business scenarios to determine whether the current modification operation is a fully commit or an incremental commit, and make the right choice accordingly, without abuse.

Demo

In actual development, the data to be saved is always submitted by the client and can be passively accepted by the server (for example, @RequestBody in Spring).

However, to simplify the discussion here, we directly hard code the object to be saved, so the code for the saved parameters is relatively more.

  • Save simple object

    sqlClient.save(  
    Immutables.createBook(draft -> {
    draft.setName("GraphQL in Action");
    draft.setEdition(4);
    draft.setPrice(new BigDecimal("59.9"));
    })
    );
    note

    Here, the id property of the object to be saved is not specified. Jimmer will determine whether related data exists in the database according to the name and edition properties,
    so as to decide whether to INSERT or UPDATE.

    This is because in the entity definition, Book.name and Book.edition are annotated with @org.babyfish.jimmer.sql.Key.
    This article is just a quick preview and does not go deep into it. Interested parties can view Mapping Part/Advanced Mapping/Key and Mutation Part/Save Command.

  • Save data structures formed by multiple objects

    sqlClient.save(  
    Immutables.createBook(draft -> {
    draft.setName("GraphQL in Action");
    draft.setEdition(4);
    draft.setPrice(new BigDecimal("59.9"));
    draft.applyStore(store -> {
    store.setName("MANNING");
    store.setWebsite("https://www.manning.com");
    });
    draft.addIntoAuthors(author -> {
    author.setFirstName("Bob");
    author.setLastName("Rockefeller");
    author.setGender(Gender.MALE);
    });
    draft.addIntoAuthors(author -> {
    author.setFirstName("Eve");
    author.setLastName("Procello");
    author.setGender(Gender.FEMALE);
    });
    })
    );

Essential Difference from Other ORM

In the previous text, we demonstrated two examples, one describing how to save a simple object, and the other describing how to save an aggregate root object and cascade save more associated objects.

It is obvious that Jimmer's save directive can appear both simple and complex, depending on whether the data structure expressed by the dynamic entity passed by the user is simple or complex.

Jimmer does not provide configuration cascade options for association properties like traditional ORM, because it is not necessary at all. The dynamic entity gives Jimmer's save capability unlimited possibilities, so there is no need to limit it to some fixed configuration.

This absolute flexibility has many wonderful uses. For example, changing the price of book with id 100 to 60, the traditional ORM and Jimmer approaches are different:

  • Traditional ORM (take JPA as an example) adopts find first and then modify, which is intuitive but wastes performance

    Book book = entityManager.find(Book.class, 100L);
    if (book != null) {
    book.setPrice(new BigDecimal(60));
    // entityManager.merge(book); //Omit if the current JPA transaction context exists
    }
  • Jimmer's approach, make up a mutilated object and directly update

    boolean matched = sqlClient.update(
    Immutables.createBook(draft -> {
    draft.setId(100L);
    draft.setPrice(new BigDecimal(60));
    // No other properties except `id` and `price` are specified
    // So no other properties except `price` will be affected
    })
    ).getTotalAffectedRowCount() != 0;

Note: Cannot Expose Directly

The ability to save data structures of arbitrary shapes is too powerful so that it cannot be exposed directly, otherwise, it will lead to huge security vulnerabilities. For example:

warning
BookController.java
@RestController
public class BookController {

private final JSqlClient sqlClient;

public BookController(JSqlClient sqlClient) {
this.sqlClient = sqlClient;
}

@PutMapping("/book")
pubic int saveBook(
@RequestBody Book book
) {
return sqlClient
.save(book)
.getTotalAffectedRowCount();
}
}

This method can work and is very powerful. The client can upload any data structure with Book as the aggregate root for the server to save.

But this is also dangerous. You cannot limit the complexity of the data structure uploaded by the client. The client can arbitrarily modify associated data of any depth through this API.

Even if you try to verify and limit the shape of the book parameter, it is still very easy to overlook and make mistakes.

warning

Therefore, the powerful Jimmer data saving capability can only be used as an underlying support internally in the service, and cannot expose this capability directly to remote clients by using dynamic entities as input parameters, because this will result in the security door wide open.

To safely expose Jimmer's data saving capabilities, please continue reading the next article: Exposing Features