graphql and spring data jpa. Select only requested fields
See original GitHub issueHi there. I know my question might look silly and already asked many times. But anyway I wonder is there any elegant way to map requested fields in graphql queries to fields in SQL queries using Spring Data layer (JPA) on the fly. Let’s consider a simple example. Imagine you have Persons and Products. In Spring Jpa Model you may present them like this below:
import org.springframework.data.repository.CrudRepository
import org.springframework.stereotype.Repository
import javax.persistence.*
@Entity
@Table(name = "persons")
class PersonEntity (
val name: String,
var email: String,
var age: Int,
var login: String,
@OneToMany(mappedBy = "person", fetch = FetchType.LAZY,
cascade = arrayOf(CascadeType.ALL)
)
val products: List<ProductEntity>
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null
}
@Entity
@Table(name = "products")
class ProductEntity(
val price: Float,
val description: String,
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "person_id", nullable = false)
val person: PersonEntity
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null
}
@Repository
interface PersonRepository : CrudRepository<PersonEntity, Long>
Then the same representation exists in graphql layer:
import com.expediagroup.graphql.spring.operations.Query
import org.springframework.stereotype.Component
data class Person (
val id: Long,
val name: String,
val email: String,
val age: Int,
val login: String,
val products: List<Product>
) {
companion object {
fun fromEntity(entity: PersonEntity): Person {
return Person(
entity.id!!,
entity.name,
entity.email,
entity.age,
entity.login,
entity.products.map(Product.Companion::fromEntity).toList()
)
}
}
}
data class Product (
val id: Long,
val price: Float,
val description: String
) {
companion object {
fun fromEntity(entity: ProductEntity) = Product(entity.id!!, entity.price, entity.description)
}
}
@Component
class PersonQuery(val personRepo: PersonRepository): Query {
fun listAllPersons() = personRepo.findAll().map(Person.Companion::fromEntity).toList()
}
So, when Spring Boot App is launched next scheme is generated:
schema {
query: Query
}
type Person {
age: Int!
email: String!
id: Long!
login: String!
name: String!
products: [Product!]!
}
type Product {
description: String!
id: Long!
price: Float!
}
type Query {
listAllPersons: [Person!]!
}
And an api user can request any number of fields from Person:
{
listAllPersons{
id,
email,
products {
price
}
}
}
The code above selects all fields from db, including unnecessary ones. I know I can play around some custom data fetchers and loaders but it requires taking into account all the possible field combinations in queries, right? I am asking about some on the fly solution that transform graphql query to sql query using spring. Maybe Spring Data Projections is good friend here. Does anybody know how to achive that?
Issue Analytics
- State:
- Created 4 years ago
- Reactions:1
- Comments:6 (2 by maintainers)
Top GitHub Comments
@smyrick If i need to add all possible inputs to scheme for all possible requests then I will lose the flexibility that gives me graphql, right? I don’t need graphql api then. I can use rest or rpc api instead.
@dariuszkuc Thanks. The example that I posted in the top is artificial and simplified. In the wild, this rarely happens. About defining GraphQL field as a function. It’s cool that the function won’t be called if corresponding field is not requested. But how about N+1 problem? According to the example with product and reviews from your wiki. If I requested N products the function getReviews will be called N times, right? So I need to do at least N+1 requests to db to fetch all data. Performance will be poor then.
Constructing database requests dynamically based on DataFetchingEnvironment is the way to go. But it requires to manually build db query for every graphql query and manually map graphql fields to db columns. Something like this guy presented here - https://github.com/graphql-java/graphql-java/issues/1180#issuecomment-439726096 It’s possible to do that with EntityManager and Criteria Builder Api but actually it’s not trivial and requires a lot of manual low level coding.
In fact I am asking about something which can do this stuff behind the scenes. Solution like Join-Monster in JS world https://join-monster.readthedocs.io/en/latest/#example-code It would be great if something like this appeared for Java or Kotlin.
I am going to close the issue to keep our issues clear, but if anyone else has comments feel to continue the discussion here for others to find later.