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
A partial object refers to querying only part of an object's properties, with less information than an isolated object.
Usage1: Query Entity Objects | Usage2: Query DTOs |
---|---|
Query Code
| DTO代码
After compilation, Java/Kotlin type |
Query Code
| |
Output Result
| Output Result
|
2. Include Associated Objects
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 Objects | Usage2: Query DTOs |
---|---|
Query Code
| DTO Code
After compilation, Java/Kotlin type |
Query Code
| |
Output Result
| Output Result
|
3. Recursive Query
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 Objects | Usage2: Query DTOs |
---|---|
Query Code
| DTO代码
After compilation, Java/Kotlin type |
Query Code
| |
Output Result
| Output Result
|
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
.
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
- Java
- Kotlin
@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();
}
}
@Repository
class BookRepository(
private val sqlClient: KSqlClient
) {
fun findBookById(id: Long): Book? =
sqlClient.findById(Book::class, id)
fun findBooksByName(name: String?): List<Book> =
sqlClient
.createQuery(Book::class) {
name?.takeIf { it.isNotEmpty() }?.let {
where(table.name ilike name);
}
select(table)
}
.execute()
}
-
JSqlClient
in Java code andKSqlClient
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
andBook.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:
- Java
- Kotlin
System.out.println(bookRepository.findBookById(1L));
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
- Java
- Kotlin
@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();
}
}
@Repository
class BookRepository(
private val sqlClient: KSqlClient
) {
fun findBookById(
id: Long,
fetcher: Fetcher<Book>
): Book? =
sqlClient.findById(
fetcher,
id
)
fun findBooksByName(
name: String? = null,
fetcher: Fetcher<Book>? = null
): List<Book> =
sqlClient
.createQuery(Book::class) {
name?.takeIf { it.isNotEmpty() }?.let {
where(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).
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.