Skip to main content

Remote Associations

Remote associations are Jimmer's first thoughts on microservice architecture.

Through vertical splitting, entity types can be divided into different microservices, or in other words, divided into different databases. The associations (including one-to-one, many-to-one, one-to-many and many-to-many) between entity types belonging to different microservices are remote associations.

tip

Jimmer automatically implements automatic query of remote associated data.

That is, if the object fetcher contains remote association properties of any depth, it will query different parts of the data across multiple microservices, and finally merge the query results of different parts into the data structure expected by the object fetcher as a whole return. No need for developers to implement remote queries and data splicing themselves.

Databases

In this article, each entity is divided as follows:

info
  • BOOK_AUTHOR_MAPPING as the many-to-many association intermediate table, can be divided into either the book-service service or the author-service service. In this example, it is divided into the book-service service, the reason will be explained later.

  • Solid arrows indicate foreign key references within the same database, whose validity can be freely chosen by developers.

  • Dashed arrows indicate foreign key references across database boundaries, which must be pseudo foreign keys.

To simplify the discussion, the database initialization scripts for the three services are as follows:

LanguageExampleService (Subproject)Database Initialization SQL
Java

jimmer-examples/java/jimmer-cloud

store-service

jimmer-cloud/store-service/src/main/resources/store.sql

book-service

jimmer-cloud/book-service/src/main/resources/book.sql

author-service

jimmer-cloud/author-service/src/main/resources/author.sql

Kotlin

jimmer-examples/kotlin/jimmer-cloud-kt

store-service

jimmer-cloud-kt/store-service/src/main/resources/store.sql

book-service

jimmer-cloud-kt/book-service/src/main/resources/book.sql

author-service

jimmer-cloud-kt/author-service/src/main/resources/author.sql

info

The accompanying examples jimmer-examples/java/jimmer-cloud or jimmer-examples/kotlin/jimmer-cloud-kt use H2, each service will automatically initialize its own database after startup.

Entity Definition

In a separate subproject (the accompanying examples are jimmer-examples/java/jimmer-cloud/model or jimmer-examples/kotlin/jimmer-cloud-kt/model), define all entity types.

These entity types constitute the global model. Entity types belong to different microservices. Entities between different microservices constitute remote associations, which can actually be understood as the interaction contracts between entities belonging to different microservices.

BookStore.java
@Entity(microServiceName = "store-service")
public interface BookStore {

@OneToMany(mappedBy = "store")
List<Book> books();

...other code omitted...
}
Book.java
@Entity(microServiceName = "book-service")
public interface Book {

@Nullable
@ManyToOne
BookStore store();

@ManyToMany
List<Author> authors();

...other code omitted...
}
Author.java
@Entity(microServiceName = "author-service")  
public interface Book {

@ManyToMany(mappedBy = "authors")
List<Book> books();

...other code omitted...
}
  • The microServiceName of the @Entity annotation of the entity class is specified, so that it is clear which microservice each entity belongs to, i.e. which database.

    In this example, the entity types BookStore, Book and Author belong to the services store-service, book-service and author-service respectively.

    Here the microservice is the name of each service in the registry center, for spring-cloud it is the global configuration sping.application.name.

  • The associations between entities belonging to different microservices are remote associations.

    • BookStore.books and Book.store: BookStore belongs to store-service, while Book belongs to book-service

    • Book.authors and Author.books: Book belongs to book-service, while Author belongs to author-service

  • The many-to-one association Book.store must be nullable, because the remote association means its foreign key must reference data in another database, that is, it must be a pseudo foreign key. And in Jimmer, the field corresponding to a pseudo foreign key must be nullable.

    Please refer to Real and Pseudo Foreign Keys.

  • For the bidirectional many-to-many association between books and authors, Book.authors is the owning side, while Author.books is the inverse side (mappedBy of @ManyToMany is configured).

    info

    Jimmer stipulates that the intermediate table of remote associations must belong to the microservice where the owning side entity belongs.

    Here the question left in the previous section is answered, why in this example the intermediate table BOOK_AUTOHOR_MAPPING is defined in the book-service database instead of the author-service database.

Registry Center

Microservice applications need a registry center, which is a basic feature of microservice architecture, no explanation is needed in this article.

In the accompanying examples, it is jimmer-examples/java/jimmer-cloud/registry-center or jimmer-examples/kotlin/jimmer-cloud-kt/registry-center

Implementing Services

Since the implementations of the microservices store-service, book-service and author-service are highly similar, this document only explains how to implement store-service.

note

Although this document only explains store-service, to experience the effect of remote associations, all three services need to be fully implemented.

Complete code can be found in the accompanying examples jimmer-examples/java/jimmer-cloud or jimmer-examples/kotlin/jimmer-cloud-kt.

Enable Microservice Support in Jimmer

Any microservice must enable Jimmer's microservice support. There are two ways:

  • Use Spring Boot Starter

    Modify spring configuration file application.yml (or application.properties)

    spring:
    application:
    name: store-service

    jimmer:
    micro-service-name: ${spring.application.name}
    ...other configurations omitted...

    eureka:
    client:
    serviceUrl:
    defaultZone: http://localhost:7000/eureka/

    Here, "store-service" is used both as the name of the current service in spring-cloud registry center and as the microservice configuration in Jimmer.

  • When using low-level APIs

    JSqlClient sqlClient = JSqlClient
    .newBuilder()
    .setMicroServiceName("...")
    ...other configurations omitted...
    .build();

Once jimmer.micro-service-name is configured, Jimmer knows which entity types belong to the current service (here is BookStore).

  • Using Jimmer's ORM APIs to operate on the entity types belonging to the current microservice will execute correctly, just like everything explained in previous documents.

  • Using Jimmer's ORM APIs to operate on entity types belonging to other microservices will throw exceptions.

tip

As long as each service ensures spring.application.name and jimmer.micro-service-name are the same, Jimmer will automatically implement automatic query of remote associated data without requiring developers to write any code.

Implement Business Query Logic

BookStoreService.java
@GetMapping("/store/{id}/detail")
public @FetchBy("COMPLEX_FETCHER") BookStore findStoreDetail(
@PathVariable("id") long id
) {
return storeRepository.findNullable(id, COMPLEX_FETCHER);
}

public static final Fetcher<BookStore> COMPLEX_FETCHER =
Fetchers.BOOK_STORE_FETCHER
.allScalarFields()
.books(
Fetchers.BOOK_FETCHER
.allScalarFields()
.authors(
Fetchers.AUTHOR_FETCHER
.allScalarFields()
)
);
  • ❶ Query root aggregate, queried directly from the database belonging to the current service store-service

  • ❷ Query associated objects through remote association property BookStore.books

    Access remote microservice book-service indirectly to get data via HTTP protocol

  • ❸ Query associated objects through remote association property Book.authors

    Access remote microservice author-service indirectly to get data via HTTP protocol

tip

Users only need to implement queries for root aggregate objects, queries for associated properties (including remote associations) are fully automated by Jimmer.

Therefore, even without writing any Java/Kotlin code for the other two microservices book-service and author-service, as long as their configuration files are modified to support microservices, this method can still run correctly.

Run Effects

Assuming store-service is started on localhost with port 7001, accessing http://localhost:7001/store/1/detail will get the result:

{
"id":1,
"name":"O'REILLY",
"website":null,
"books":[
{
"id":1,
"name":"Learning GraphQL",
"edition":1,
"price":50,
"authors":[
{
"id":1,
"firstName":"Eve",
"lastName":"Procello",
"gender":"FEMALE"
},
{
"id":2,
"firstName":"Alex",
"lastName":"Banks",
"gender":"MALE"
}
]
},
{
"id":2,
...omitted...
},
{
"id":3,
...omitted...
},
{
"id":4,
"name":"Effective TypeScript",
"edition":1,
"price":73,
"authors":[
{
"id":3,
"firstName":"Dan",
"lastName":"Vanderkam",
"gender":"MALE"
}
]
},
{
"id":5,
...omitted...
},
{
"id":6,
...omitted...
},
{
"id":7,
"name":"Programming TypeScript",
"edition":1,
"price":47.5,
"authors":[
{
"id":4,
"firstName":"Boris",
"lastName":"Cherny",
"gender":"MALE"
}
]
},
{
"id":8,
...omitted...
},
{
"id":9,
...omitted...
}
]
}