How to use another variable name or How to flatten entity in JPA Projection for nested object - spring-boot

I'm making an api for querying nested entity with Spring Data JPA Projection.
My Code
The Entity:
#Entity
class User {
#Id
var userId: String
var name: String
var age: Int
#OneToOne
#JoinColumn(name = "userId")
var address: Address
}
#Entity
class Address {
var userId: String
var street: String
var city: String
var country: String
}
The Repository:
interface UserView {
val name: String
val address: AddressView
interface AddressView {
val city: String
val country: String
}
}
#Repository
interface UserRepository : JPARepository<User, String> {
fun findAll(): List<UserView>
}
Expected Response
{
"name": "example",
"city": "example-city",
"country": "example-country"
}
My code produces
{
"name": "example",
"address": {
"city": "example-city",
"country": "example-country"
}
}
I Tried
I tried another view to flatten object:
interface UserView {
val name: String
val addressCity: String
val addressCountry: String
}
But this case, the variable naming is too complicate.
I want to solve this problem with projection. How can I solve this problem?

In JPA, you can do this using #NamedNativeQuery only:
#NamedNativeQuery(
name = "getUser",
query = "SELECT u.name, a.city, a.country FROM User u and Address a where a.userId = u.id ", resultClass=UserView .class)
#Entity
class User {
...
}
For Reference hibernate-named-query

Try this:
data class UserView(name: String, city: String, country: String)
#Repository
interface UserRepository : JPARepository<User, String> {
#Query(value = "select new your.pkg.UserView(u.name, u.address.city, u.address.country) from User u")
fun findAllFlat(): List<UserView>
}

You can use #Value and combine more fields or even access the fields of objects.
From the spring-data-rest documentation:
You can create a projection that combines the two data fields in the preceding example together, as follows:
#Projection(name = "virtual", types = { Person.class })
public interface VirtualProjection {
#Value("#{target.firstName} #{target.lastName}")
String getFullName();
}
Spring’s #Value annotation lets you plug in a SpEL expression that takes the target object and splices together its firstName and lastName attributes to render a read-only fullName.
Flattening also works for me:
#Value("#{target.document.title}")
String getDocumentTitle();

Related

How to combine key and value using Jackson (Spring boot)

I have json like below.
{
"USER0001": {
"name": "hoge",
"age": 20
},
"USER0002": {
"name": "huga",
"age": 10
}
}
and, this is my User data class.
data class User(
val id: String,
val name: String,
val age: Int
)
then, I want to convert json to user list when request is send controller.
listOf(
User("USER0001", "hoge", 20),
User("USER0002", "huga", 10),
)
and my controller .
#RestController
class MyController() {
fun test(#RequestBody users: List<User>) {
// some code. I want to use users as List<User>
}
}
I try using #JsonComponent like below,
class Deserializer : JsonDeserializer<List<User>>() {
override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): List<User> {
val treeNode = parser.codec.readTree<TreeNode>(parser)
val fieldNames = treeNode.fieldNames()
val result = mutableListOf<User>()
while(fieldNames.hasNext()) {
val fieldName = fieldNames.next()
val userJson = treeNode.get(fieldName)
// I can't use this code as String type.
val name = userJson.get("name")
// How Can I make User model ???
}
return result
}
}
then, I don't know how to make User object in deserializer method.
do you know how to do this ?
thank you reading.
this is simple way.
#RestController
class MyController() {
fun test(#RequestBody Map<String, UserDetail>) {
// some code...
}
}

How can optimised the code for Derived Query Methods in Spring Data JPA Repositories

Entity Class:
#Document(collection = “personErasure")
class PersonErasureModel {
#Id
var id: UUID = UUID.randomUUID()
lateinit var personRequestId: UUID
var fName: String? = null
var validationState: PersonValidationState? = null
var erasureState: ErasureState? = null
}
//1. Enum class
enum class PersonValidationState {
PROCESS,
ELIGIBLE,
INELIGIBLE
}
//2. Enum Class
enum class ErasureState {
IN_PROGRESS,
COMPLETED
}
Repository:
#Repository
interface PersonErasureRepository : ReactiveMongoRepository<PersonErasureModel, UUID> {
fun countByPersonRequestIdAndValidationStateIn(
personRequestId: UUID,
statuses: List<PersonValidationState>
): Mono<Long>
fun countByPersonRequestIdAndErasureState(
personRequestId: UUID,
erasureState: ErasureState
): Mono<Long>
}
Here in PersonErasureRepository both methods are similar. But both PersonValidationState and ErasureState are two different properties in model class.
Can we use a single method which can handle both the use case? Then how can name the Derived query method ?

how to get specific column in Spring JPA?

I can GET below data by findAll() from db
[
{ id : 1,
name : "John",
age : "23",
sex : "male"
},
{ id : 2
...
]
And what I want to do is getting specific data from db like below.
[
{ id : 1,
name : "John"
},
{ id : : 1,
name : "Peter"
}
...
]
so I tried like this.
Repository
#Repository
public interface PersonDAO extends JpaRepository<Person, Integer> {
#Query(value ="select p.id , p.name from person p", nativeQuery = true)
Collection<Object> getPersonIdAndName();
}
Controller
#GetMapping ("/getPersonIdAndName")
public Collection<Object> getPerson() {
return personDAO.getPersonIdAndName();
}
but the result was like this (by postman)
[
[
1,
"John"
],
[
2,
"Peter"
]
....
]
I want a type key : value, not just value,
because I want to fetch this to React.js state.
how can I fix?
I'd appreciate for your help
You need to Use a Projection. Create a new Interface
public interface PersonProjection {
Integer getId();
String getName();
}
After that in you repository use the projection in the return type
#Repository
public interface PersonDAO extends JpaRepository<Person, Integer> {
#RestResource(exported = false)
Query("select p.id, p.name from Person as p")
Collection<PersonProjection> getPersonIdAndName();
}
Alternately, if you want to #Override the findAll() method you can create a customized repository. Here there is a GitHub repo example https://github.com/federicogatti/Spring-Custom-Repository-Example

How to handle List<Map<String, Object>> return type from query resolver in .graphqls

I have a graphql implementation, below is my .graphqls file
schema {
query: Query
}
type Product {
id: Int!
name: String!
description: String!
productTypes: [ProductType]!
}
type City {
id: Int!
name: String!
}
type ProductType {
id: Int!
type: String!
product: Int
}
type Query {
products: [Product]!
cities: [City]!
productTypes: [ProductType]!
}
This is my query resolver code
public class Query implements GraphQLQueryResolver {
#Autowired
ProductRespository productRespository;
#Autowired
EntityManager em;
#Autowired
CityRepository cityRepository;
#Autowired
ProductTypeRepository productTypeRepository;
public List<Map<String, Object>> products() {
javax.persistence.Query q = em.createQuery("select new
map(p.name as productName, p.id as productId) from Product p");
List<Map<String, Object>> list = q.getResultList();
return list;
}
public Iterable<City> cities() {
return cityRepository.findAll();
}
}
when I am running the application I am getting below error
Type java.util.Map cannot be
mapped to a GraphQL type! Since GraphQL-Java deals with erased types at
runtime, only non-parameterized classes can represent a GraphQL type.
This allows for reverse-lookup by java class in interfaces and union
types
So how I can handle this kind of return type in Query Section of .graphqls
Thanks in Advance

How to ignore specific fields on income in Spring boot?

I have my domain class as follows
#Getter
#Setter
public class Student {
private Long id;
private String firstName;
private String lastName;
}
And I have this controller
#RestController
#RequestMapping("/student")
public class StudentController {
#PostMapping(consumes = "application/json", produces = "application/json")
public ResponseEntity<Student> post(#RequestBody Student student) {
//todo save student info in db, it get's an auto-generated id
return new ResponseEntity<>(student, HttpStatus.CREATED);
}
}
Now what I want is to configure serializer in a way that it ignores the id field on income, so I get only firstName and lastName, but serialize it when I'm returning the object to the caller.
Its easy to use it with jackson. There is an annotation named #JsonProperty(access = Access.READ_ONLY) where you can define if the property should be de- or serialized. Just put that annotation on your id field.
#JsonProperty(access = Access.READ_ONLY)
private Long id;
The Controller:
#PostMapping(consumes = "application/json", produces = "application/json")
public ResponseEntity<Student> post(#RequestBody Student student) {
//here we will see the that id is not deserialized
System.out.println(student.toString());
//here we set a new Id to the student.
student.setId(123L);
//in the response we will see that student will serialized with an id.
return new ResponseEntity<>(student, HttpStatus.CREATED);
}
Requestbody:
{
"id":1,
"firstName": "Patrick",
"lastName" : "secret"
}
Output of toString():
Student [id=null, firstName=Patrick, lastName=secret]
Response:
{
"id": 123,
"firstName": "Patrick",
"lastName": "secret"
}
P.S. It will also work if you dont send an id property:
{
"firstName": "Patrick",
"lastName" : "secret"
}

Resources