Mutation
Introduction to GraphQL Input
In GraphQL, GraphQLObjects returned by queries are dynamic objects of arbitrary shapes. However, if mutation operations accept object parameters, they must be GraphQLInputs, which are static objects of fixed shapes.
See GraphQLInput to learn more.
Take the GraphQL declaration file in the example schema.graphqls as an example:
type Book implements CommonEntity { ❶
id: Long!
name: String!
edition: Int!
price: BigDecimal!
store: BookStore
authors: [Author!]!
createdTime: LocalDateTime!
modifiedTime: LocalDateTime!
tenant: String!
}
input BookInput { ❷
id: Long
name: String!
edition: Int
price: BigDecimal!
storeId: Long
authorIds: [Long!]!
}
...other code omitted...
-
❶ Types declared with the
type
keyword are dynamic types used to express arbitrary data structures, used as GraphQL output types -
❷ Types declared with the
input
keyword are static types used to express fixed data structures, used as GraphQL input types
Define Jimmer Input DTO
Jimmer Input DTOs are introduced in great detail in Save Command/Input DTO, which will not be repeated here.
Jimmer provides two ways to define Input DTOs:
- Automatically generate Input DTOs using the DTO language
- Manually define Input DTOs based on MapStruct
Using the DTO language can achieve our goal very efficiently, so this article adopts this approach.
-
In the project where the entity is defined, create the directory
src/main/dto
-
Under
src/main/dto
, create subdirectoriescom/yourcompany/yourproject/model
according to the package path where the entities are located -
Under the directory created in the previous step, create the file
Book.dto
. The file must have the same name as the entity class and the extension must bedto
-
Edit this file and use the DTO language to define various DTO shapes for the Book entity
Book.dtoinput BookInput {
#allScalars(Book)
id(store)
id(authors) as authorIds
}
...other DTO definitions omitted...
After compilation, the following Input DTO will be generated automatically:
- Java
- Kotlin
@GeneratedBy(file = "<your_project>/src/main/dto/Book.dto")
public class BookInput implements Input<Book> { ❶
@Nullable
private Long id;
private String name;
private int edition;
private BigDecimal price;
@Nullable
private Long storeId;
private List<Long> authorIds;
@Override
public Book toEntity() { ❷
...omitted...
}
...other members omitted...
}
@GeneratedBy(file = "<your_project>/src/main/dto/Book.dto")
data class BookInput(
val id: Long? = null,
val name: String = "",
val edition: Int = 0,
val price: BigDecimal,
val storeId: Long? = null,
val authorIds: List<Long> = emptyList()
): Input<Book> { ❶
override fun toEntity(): Book = ❷
...omitted...
...other members omitted...
}
-
❶ The
BookInput
class implements the interfaceorg.babyfish.jimmer.Input
, which supports thetoEntity
method to convert the current Input DTO object to a Jimmer dynamic entity object. -
❷ Implements the
Input.toEntity
method
Implement GraphQL mutation
- Java
- Kotlin
package com.example.business;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.MutationMapping;
import org.springframework.stereotype.Controller;
...other imports omitted...
@Controller
public class BookStoreService {
private final BookStoreRepository bookStoreRepository;
public BookStoreService(BookStoreRepository bookStoreRepository) {
this.bookStoreRepository = bookStoreRepository;
}
@MutationMapping ❶
@Transactional
public Book saveBook(
@Argument BookInput input ❷
) {
// `save(input)` is shorthand for `save(input.toEntity())`
return bookRepository.save(input); ❸
}
}
package com.example.business
import org.springframework.graphql.data.method.annotation.Argument
import org.springframework.graphql.data.method.annotation.MutationMapping
import org.springframework.stereotype.Controller
...other imports omitted...
@Controller
class BookStoreService(
private val bookStoreRepository: BookStoreRepository
) {
@MutationMapping ❶
@Transactional
fun saveBook(
@Argument input: BookInput ❷
): Book =
// `save(input)` is shorthand for `save(input.toEntity())`
bookRepository.save(input) ❸
}
-
❶ Use annotation
@org.springframework.graphql.data.method.annotation.MutationMapping
-
❷ Use the static Input DTO type
BookInput
to allow users to pass only data structures of specified shapes to conform to GraphQLInput -
❸ Save command to save arbitrary data structures in one line
Here
bookRepository.save(input)
is actually shorthand forbookRepository.save(input.toEntity())
.tipNo matter how simple or relatively complex the data structure defined by the
BookInput
type, it can be saved in one line. This is the core value of the save command feature.