Skip to main content

Feature Introduction

Here is the English translation of the file, with the code indentation preserved:

Basic Usage

Jimmer can query data structures of any shape, with control over the queried data structure as fine-grained as GraphQL.

Next, we will demonstrate its usage in three scenarios.

1. Query Partial Objects

info

A partial object refers to querying only part of an object's properties, with less information than an isolated object.

Usage1: Query Entity ObjectsUsage2: Query DTOs

Query Code

BookTable table = Tables.BOOK_TABLE;
List<Book> books = sqlClient
.createQuery(table)
.where(table.name().eq("Learning GraphQL"))
.select(
table.fetch(
Fetchers.BOOK_FETCHER
.name()
)
)
.execute();

DTO代码

export yourpackage.Book 
-> package yourpackage.dto;

BookView {
id
name
}

After compilation, Java/Kotlin type BookView will be automatically generated.

Query Code

BookTable table = Tables.BOOK_TABLE;
List<BookView> books = sqlClient
.createQuery(table)
.where(table.name().eq("Learning GraphQL"))
.select(
table.fetch(BookView.class)
)
.execute();

Output Result

[
{"id":10,"name":"Learning GraphQL"},
{"id":11,"name":"Learning GraphQL"},
{"id":12,"name":"Learning GraphQL"}
]

Output Result

[
BookView(id=10, name=Learning GraphQL),
BookView(id=11, name=Learning GraphQL),
BookView(id=12, name=Learning GraphQL)
]

2. Include Associated Objects

info

Select an entity as the aggregate root, and query not only the aggregate root object but also its associated objects, with no restrictions on depth or breadth.

This level of control over the format is as fine-grained as GraphQL.

Usage1: Query Entity ObjectsUsage2: Query DTOs

Query Code

BookTable table = Tables.BOOK_TABLE;
List<Book> books = sqlClient
.createQuery(table)
.where(table.name().eq("Learning GraphQL"))
.select(
table.fetch(
Fetchers.BOOK_FETCHER
.allScalarFields()
.store(
Fetchers.BOOK_STORE_FETCHER
.allScalarFields()
)
.authors(
Fetchers.AUTHOR_FETCHER
.allScalarFields()
)
)
)
.execute();

DTO Code

export yourpackage.Book 
-> package yourpackage.dto;

BookView {
#allScalars
store {
#allScalars
}
authors {
#allScalars
}
}

After compilation, Java/Kotlin type BookView will be automatically generated.

Query Code

BookTable table = Tables.BOOK_TABLE;
List<BookView> books = sqlClient
.createQuery(table)
.where(table.name().eq("Learning GraphQL"))
.select(
table.fetch(BookView.class)
)
.execute();

Output Result

[
{
"id": 1,
"name": "Learning GraphQL",
"edition": 1,
"price": 50,
"store": {
"id": 1,
"name": "O'REILLY",
"website": null
},
"authors": [
{
"id": 2,
"firstName": "Alex",
"lastName": "Banks",
"gender": "MALE"
},
{
"id": 1,
"firstName": "Eve",
"lastName": "Procello",
"gender": "FEMALE"
}
]
},
{
"id": 2,
"name": "Learning GraphQL",
"edition": 2,
"price": 55,
"store": {
"id": 1,
"name": "O'REILLY",
"website": null
},
"authors": [
{
"id": 2,
"firstName": "Alex",
"lastName": "Banks",
"gender": "MALE"
},
{
"id": 1,
"firstName": "Eve",
"lastName": "Procello",
"gender": "FEMALE"
}
]
},
{
"id": 3,
"name": "Learning GraphQL",
"edition": 3,
"price": 51,
"store": {
"id": 1,
"name": "O'REILLY",
"website": null
},
"authors": [
{
"id": 2,
"firstName": "Alex",
"lastName": "Banks",
"gender": "MALE"
},
{
"id": 1,
"firstName": "Eve",
"lastName": "Procello",
"gender": "FEMALE"
}
]
}
]

Output Result

[
BookView(
id=1,
name=Learning GraphQL,
edition=1,
price=50.00,
store=BookView.TargetOf_store(
id=1,
name=O'REILLY,
website=null
),
authors=[
BookView.TargetOf_authors(
id=2,
firstName=Alex,
lastName=Banks,
gender=MALE
),
BookView.TargetOf_authors(
id=1,
firstName=Eve,
lastName=Procello,
gender=FEMALE
)
]
),
BookView(
id=2,
name=Learning GraphQL,
edition=2,
price=55.00,
store=BookView.TargetOf_store(
id=1,
name=O'REILLY,
website=null
),
authors=[
BookView.TargetOf_authors(
id=2,
firstName=Alex,
lastName=Banks,
gender=MALE
),
BookView.TargetOf_authors(
id=1,
firstName=Eve,
lastName=Procello,
gender=FEMALE
)
]
),
BookView(
id=3,
name=Learning GraphQL,
edition=3,
price=51.00,
store=BookView.TargetOf_store(
id=1,
name=O'REILLY,
website=null
),
authors=[
BookView.TargetOf_authors(
id=2,
firstName=Alex,
lastName=Banks,
gender=MALE
),
BookView.TargetOf_authors(
id=1,
firstName=Eve,
lastName=Procello,
gender=FEMALE
)
]
)
]

3. Recursive Query

info

If an entity contains Self-correlation properties, you can perform a recursive query.

As of now, the GraphQL protocol does not support recursive queries.

Usage1: Query Entity ObjectsUsage2: Query DTOs

Query Code

TreeNode rootNode = sqlClient
.findById(
Fetchers.TREE_NODE_FETCHER
.allScalarFields()
// Recursion upwards
.recursiveParent()
// Recursion downward
.recursiveChildNodes()
10L
);

DTO代码

export yourpackage.TreeNode 
-> package yourpackage.dto;

RecursiveTreeNodeView {
id
// Recursion upwards
parent* ❶
// Recursion downward
childNodes* ❷
}

After compilation, Java/Kotlin type RecursiveTreeNodeView will be automatically generated.

Query Code

RecursiveTreeNodeView rootNode = sqlClient
.findById(
RecursiveTreeNodeView.class,
10L
);

Output Result

{
"id": 10,
"name": "Woman",
"parent": {
"id": 9,
"name": "Clothing",
"parent": {
"id": 1,
"name": "Home",
"parent": null
}
},
"childNodes": [
{
"id": 11,
"name": "Casual wear",
"childNodes": [
{
"id": 12,
"name": "Dress",
"childNodes": []
},
{
"id": 13,
"name": "Miniskirt",
"childNodes": []
},
{
"id": 14,
"name": "Jeans",
"childNodes": []
}
]
},
{
"id": 15,
"name": "Formal wear",
"childNodes": [
{
"id": 16,
"name": "Suit",
"childNodes": []
},
{
"id": 17,
"name": "Shirt",
"childNodes": []
}
]
}
]
}

Output Result

RecursiveTreeNodeView(
id=10,
name=Woman,
parent=RecursiveTreeNodeView.TargetOf_parent( ❶
id=9,
name=Clothing,
parent=RecursiveTreeNodeView.TargetOf_parent( ❶
id=1,
name=Home,
parent=null
)
),
childNodes=[
RecursiveTreeNodeView.TargetOf_childNodes(
id=11,
name=Casual wear,
childNodes=[
RecursiveTreeNodeView.TargetOf_childNodes(
id=12,
name=Dress,
childNodes=[]
),
RecursiveTreeNodeView.TargetOf_childNodes(
id=13,
name=Miniskirt,
childNodes=[]
),
RecursiveTreeNodeView.TargetOf_childNodes(
id=14,
name=Jeans,
childNodes=[]
)
]
),
RecursiveTreeNodeView.TargetOf_childNodes(
id=15,
name=Formal wear,
childNodes=[
RecursiveTreeNodeView.TargetOf_childNodes(
id=16,
name=Suit,
childNodes=[]
),
RecursiveTreeNodeView.TargetOf_childNodes(
id=17,
name=Shirt,
childNodes=[]
)
]
)
]
)

Repository Code Style

The above code is only intended to demonstrate Jimmer's powerful control over the format of the queried data through three scenarios and does not organize the code structure.

In actual development, we must organize the code in some way, with data operation-level code placed in the Repository.

caution

The previous examples showed two usages: querying entity objects and querying DTO objects. To control the complexity of this document, the following sections will only discuss how to organize code for querying entity objects.

The Simplest Repository

Now let's write a BookRepository for querying Book

BookRepository.java
@Repository
pubic class BookRepository {

private final JSqlClient sqlClient;

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

@Nullable
public Book findBookById(long id) {
return sqlClient.findById(Book.class, id);
}

public List<Book> findBooksByName(@Nullable String name) {
BookTable table = Tables.BOOK_TABLE;
return sqlClient
.createQuery(table)
.whereIf(
name != null && !name.isEmpty(),
table.name().ilike(name)
)
.select(table)
.execute();
}
}
  • JSqlClient in Java code and KSqlClient in Kotlin code are the API entry points provided by Jimmer for Java and Kotlin developers.

    In actual projects, the object is a global object. This chapter document is used for quick preview and does not introduce details. Readers can ignore specific details for the time being and just know that sqlClient is the API entry point.

  • The purpose of this article is to control the format of returned objects, not to introduce complex query conditions (this part of the content is introduced in Quick Preview/Arbitrary Dynamic Queries).

    So these two methods symbolically use Book.id and Book.name as filters.

  • Jimmer is technically neutral, but using Spring-style code often serves to simplify explanation, so this example uses Spring-style writing.

    However, for convenience of non-Spring users to read, it deliberately does not use Jimmer support for Spring Data here, but uses the relatively primitive injection of sqlClient, which reduces interference from Spring to a minimum.

  • Tables.BOOK_TABLE in Java code is code that Jimmer automatically generates at compile time.

If there is an instance bookRepository of the above class, take findBookById as an example:

System.out.println(bookRepository.findBookById(1L));

The output result is as follows:

{
"id" : 1,
"name" : "Learning GraphQL",
"edition" : 1,
"price" : 50.00,
"store" : {
"id" : 1
}
}

The output format is fixed and does not match the topic “Query Any Data Structure Shape” we are discussing now, so we need to improve BookRepository.

Improved Repository

Let's make some minor improvements to the previous BookRepository class

BookRepository.java
@Repository
pubic class BookRepository {

private final JSqlClient sqlClient;

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

@Nullable
public Book findBookById(
long id,
Fetcher<Book> fetcher
) {
return sqlClient.findById(
fetcher,
id
);
}

public List<Book> findBooksByName(
@Nullable String name,
@Nullable Fetcher<Book> fetcher
) {
BookTable table = Tables.BOOK_TABLE;
return sqlClient
.createQuery(table)
.whereIf(
name != null && !name.isEmpty(),
table.name().ilike(name)
)
.select(
table.fetch(fetcher)
)
.execute();
}
}

In this example, we add a parameter of type Fetcher<Book> for each query method, through which we can flexibly control the format of the queried object (i.e. the shape of the queried data structure).

tip

This is the recommended usage. The Repository is only responsible for filtering, sorting, paging and other operations, but does not control the format of the returned data. Instead, it exposes the control of the data format through the Fetcher<E> parameter to Let the upper layer business logic decide.