Query Spring Entity using join on list propery - spring

I have an entity (say Person) and the person contains list of address.
class Person {
val addresses : List<Address>
.
.
}
class Address {
val street : String
val city: String
val state: String
.
.
}
Now My requirement is, I want to pass a list of city and find all people living in those city. Can someone please help me to find if there's a way to write Spring data JPA query (like findByAddress_City) for this requirement?

Related

How to tell Spring Data MongoDB to store nested fields of a document that are also documents in their own collection?

I have two collections called persons and addresses. The idea is to have person hold an address in the field address. I use Spring Data MongoDB to persist those mentioned documents.
My usual way of crafting the "relation" between Person > Address was to store the ID of the address and give it to the person object. Later when I find() a person I resolve the address object by it's id and voila I have my person + address.
However I find this somewhat every cumbersome since in my code I just want to add the Address object as whole and not only it's ID so I can work with it while also saving it to the repository at any point of time.
I therefore started a little unit test to see how Spring Data MongoDB saves the Address object if it's just a field of Person and is not saved by it's own Repository.
This is what I came up with:
import org.springframework.data.mongodb.core.mapping.Document
import org.springframework.data.mongodb.repository.MongoRepository
import org.springframework.stereotype.Repository
#Document("person")
data class Person(
val id: String,
val name: String,
val age: Int,
var address: Address
)
#Document("addresses")
data class Address(
val id: String,
val street: String?,
val number: Int?
)
#Repository
interface PersonRepository : MongoRepository<Person, String>
#Repository
interface AddressRepository : MongoRepository<Address, String>
And this is the unit test - that fails with the last steps as I was expecting:
internal class FooTest #Autowired constructor(
private val personRepository: PersonRepository,
private val addressRepository: AddressRepository
) {
#Test
fun `some experiment`() {
val testPerson = Person("001", "Peter", 25, Address("011","Lumberbumber", 12))
personRepository.save(testPerson)
val person = personRepository.findAll()[0]
assertThat(person).isNotNull
assertThat(person.address).isNotNull
assertThat(person.address.street).isEqualTo("Lumberbumber")
assertThat(person.address.number).isEqualTo(12)
// works because address was just copied into the object structure
// of `person` and was not seen as a standalone document
val address = addressRepository.findAll()[0]
assertThat(address.street).isEqualTo("Lumberbumber") // fails
assertThat(address.number).isEqualTo(12) // fails
// As expected `address` was not persisted alongside the `person` document.
}
}
So I thought about using AbstractMongoEventListener<Person> to intercept the saving process and pick the Address object out from Person here and do a addressRepository.save(addressDocument) while putting a lightweight address object (only having the ID) back in the Person document.
The same I'd to in the reverse when doing a find for Person and assembling Person and Address together again.
#Component
class MongoSaveInterceptor(
val addressRepository: AddressRepository
) : AbstractMongoEventListener<Person>() {
override fun onBeforeConvert(event: BeforeConvertEvent<Person>) {
val personToSave = event.source
val extractedAddress = personToSave.address
val idOfAddress = addressRepository.save(extractedAddress).id
personToSave.address = Address(idOfAddress, null, null)
}
override fun onAfterConvert(event: AfterConvertEvent<Person>) {
val person = event.source
val idOfAddress = person.address.id
val foundAddress = addressRepository.findById(idOfAddress)
foundAddress.ifPresent {
person.address = it
}
}
}
It works that way and might be a workaround solution for my requirement.
BUT
I feel that there has to be something like that already working and I might just need to find the proper configuration for that.
That's where I am stuck atm and need some guidance.
Another research showed me it's about #DBRef (https://www.baeldung.com/cascading-with-dbref-and-lifecycle-events-in-spring-data-mongodb) I have to use. This way Spring Data MongoDB stores the embedded document class and resolves it when loading the parent document object from the database.

Posting a resource with ManyToOne relationship

This question is primarily on How to design a rest API and whats the best practice. I am trying to figure out REST endpoint for POST service, consider below sample code:
class Person {
String name;
#OneToMany
List<Address> addresses;
}
class Address {
String someAttribute;
#ManyToOne
Person person;
}
To add a new Address the rest endpoint could look like POST api/**v1**/address. The main problem is with the RequestBody since we will only have the id of Person so we need to pass the json like below:
{
"someAttribute": "someValue"
"personId": 1
}
And in Controller method, we can't simply use #RequestBody Address address as parameter to Rest endpoint method as we do not have personId in Address class.
I would like to understand whats the best practice here and how should the Rest endpoint be?
PS: Above code is a dummy code just to explain question easily.
update
to avoid confusion I have updated endpoint url.
Please go through #PathVariables annotations in Spring API .
Using #PathVariables you get the person_id and address_id from URL.
Creates a person (initially with n address)
POST api/{api_version}/person
Updates a person (only update Person details not address)
PUT api/{api_version}/person/{person_id}
Add a new address to person
POST api/{api_version}/person/{person_id}/addresses
Updates an existing person address
PUT api/{api_version}/person/{person_id}/addresses/{address_id}
Class Structure
class Address {
Integer id;
String someAttribute;
}
class Person {
Long id;
String name;
#OneToMany
List<Address> addresses;
}
Sample Resource Implementation
#RequestMapping(value="api/{api_version}/person/{person_id}/addresses/{address_id}", method = RequestMethod.PUT)
public ResponseEntity<ApiCustomResponse> updateAddress(
#PathVariable("person_id") Long person_id,
#PathVariable("address_id") Integer address_id, #RequestBody Address address) {
...
}

Is possible to get data from jpa/hibernate withou define entity or model in spring boot?

Is possible to get data from jpa/hibernate withou define entity or model in spring boot? if possible, how to? thank you...
Yes its possible
for serverside and database you first read
https://spring.io/guides/gs/relational-data-access/
and for restful ,you must use RequestEntity<> to get a field(s) without a model and send or receive data from JSON.
// Field class is a response and this is not model class
public class Field{
private String name;
//getter and seter
}
#PostMapping("/test")
public void getFields(RequestEntity<Map<String,String>> entity){
Field field = new Field();
field.setName(entity.getBody().get("name"));
System.out.println(field);
}
It is possible to fetch the data without having it mapped to an entity by using nativeQuery and a Tuple class.
List<Tuple> resultList = entityManager
.createNativeQuery("select id as id, street as street, city as city, country as country from address ", Tuple.class
.getResultList();
Tuple firstResult = resultList.get(0);
assertEquals(1L , ((Number) firstResult.get("id")).longValue());
For more information you can read Vlad Mihalcea's blog post
https://vladmihalcea.com/the-best-way-to-map-a-projection-query-to-a-dto-with-jpa-and-hibernate/

Nested objects data modeling for cassandra

I am using spring data cassandra and having trouble understanding how the data model should be. I understand that cassandra tables are generally denormalized so that means if I have a customer object that looks like this
{
name: "Test Customer",
address: {
city: "New york",
state: "NY"
}
}
The corresponding POJOs would like this
#Table
public class Customer {
#PrimaryKey //just using this as key for this example
private String name;
private Address address;
}
public class Address {
private String city;
private String state;
}
So I want to store only the customer objects but have some way to retrieve the address object associated with the customer object. What are some of the common strategies to handle this.
Should I be using the composite/compound key in some way or create a separate POJO where I can store attributes from both objects in a denormlized form or some other way. Any hints would be appreciated.
In Cassandra tables are created on the query which you want to perform (Query driven model).
So please share the query or set of queries which you want to perform on table.

JPA CrudRepository for findIn method

In one requirement I need find in a list by id. Below is the example.
class Employee{
Long id;
Contractor contractor;
}
class Contractor {
Long id;
List<Address> addressList;
}
class Address {
Long id;
String line;
}
Now in EmployeeController.java class I need to find out by employee Id and address Id.
I have CrudRepository as below.
interface EmployeeRepository extends CrudRepository<Employee, Long> {
Employee findOne(Long id);
// I want one method here that can find by employee id and address id.
Employee findByIdAndAddress();
}
When I try to run the spring application up it gives the below exception.
PropertyReferenceException: No property Address found for type Employee!
Thank you!
Well, the exception itself is quite clear, it tries to find a property "address" on Employee, which does not exist, so you'll have to join Contractor and Address to make that work. However, as far as I can see in the list of reserved keywords, you can't do that with method naming.
So the solution is to write your own query, for example:
#Query("SELECT e FROM Employee e JOIN e.contractorList c JOIN c.addressList a " +
"WHERE e.id = :id AND a.id = :addressId")
Employee findByIdAndAddress(#Param("id") Long id, #Param("addressId") Long addressId);

Resources