Spring boot application with JPA, many to many relation with additional attributes returning infinite recursion error - spring-data-jpa

I tried to return the entity directly as response resulted in error as "Could not write JSON: Infinite recursion (StackOverflowError)", I would like to get the response in both forms with out transforming the data. Can some one suggest work around. Adding the Entity structure for your reference.
Employee Entity
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long employeeId;
private String firstName;
private String lastName;
private String role;
#OneToMany(mappedBy = "employee")
private Set<EmployeeStream> streams = new HashSet<>();
}
Stream Entity
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Stream {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long streamId;
private String streamName;
#OneToMany(mappedBy = "stream")
private Set<EmployeeStream> employees = new HashSet<>();
}
EmployeeStream Entity with additional Attribute experience
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class EmployeeStream {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "EMPLPOYEE_ID")
private Employee employee;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "STREAM_ID")
private Stream stream;
private double experience;
}
I would like to retrieve data in both forms
{
"employeeId": 1,
"firstName": "George",
"lastName": "Stephen",
"role": "developer",
"streams": [
{
"id": 1,
"stream": {
"streamId": 1,
"streamName": "Java"
},
"experience": 1.2
},
{
"id": 2,
"stream": {
"streamId": 2,
"streamName": "Node JS"
},
"experience": 2
}
]
}
{
"stream": {
"streamId": 1,
"streamName": "Java",
"employees": [
{
"id": 1,
"employee": {
"employeeId": 1,
"firstName": "George",
"lastName": "Stephen",
"role": "developer"
},
"experience": 2
},
{
"id": 2,
"employee": {
"employeeId": 1,
"firstName": "Davis",
"lastName": "Tom",
"role": "developer"
},
"experience": 0.5
}
]
}
}

You need to use #JsonBackReference and #JsonManagedReference as used in the below in below code.
Employee entity
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long employeeId;
private String firstName;
private String lastName;
private String role;
#OneToMany(mappedBy = "employee")
#JsonManagedReference
private Set<EmployeeStream> streams = new HashSet<>();
}
Stream entity
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Stream {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long streamId;
private String streamName;
#OneToMany(mappedBy = "stream")
#JsonManagedReference
private Set<EmployeeStream> employees = new HashSet<>();
}
EmployeeStream entity
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
public class EmployeeStream {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "EMPLPOYEE_ID")
#JsonBackReference
private Employee employee;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "STREAM_ID")
#JsonBackReference
private Stream stream;
private double experience;
}
You can find more details here and here
Update: Previously #JsonBackReference and #JsonManagedReference was misplaced, corrected the same.
Note: If still faced any problem then try removing #JsonManagedReference from Stream and Employee entities.

Related

Get Embed Document based on DBRef in MongoDB using Spring data

I'm developing a spring boot application. I've 2 models that is
PersonEntity
#Data
#Document(collection = "products")
public class ProductEntity implements Serializable {
private static final long serialVersionUID = -8118791879505582652L;
#MongoId(FieldType.STRING)
private String id;
#Indexed
#Field("name")
private String name;
#Field("category")
#DBRef(lazy = true)
private CategoryEntity category;
// getters and setters
}
CategoryEntity
#Data
#Document(collection = "categories")
public class CategoryEntity implements Serializable {
private static final long serialVersionUID = -636356399519451958L;
#MongoId(value = FieldType.STRING)
private String id;
#Field("name")
private String name;
#Field("desc")
private String desc;
public CategoryEntity(String id) {
super();
this.id = id;
}
// getters and setters
}
when I'm saving data into DB then stored data is
Products Document
{
"_id": {
"$oid": "6141eeab92432109463ae8c4"
},
"name": "Adidas Running Shoes",
"category": {
"$ref": "categories",
"$id": "614049d91042cf40b5b9b304"
}
}
Categories Document
{
"_id": {
"$oid": "614049d91042cf40b5b9b304"
},
"name": "Shoes",
"desc": "Men's running Shoes",
}
But when I'm try to findAll() the ProductEntity the data by using Spring-Data-MongoDB the category will return null.
public List<ProductModel> getProducts() {
List<ProductEntity> entities = productRepo.findAll();
return entities.stream().map(entity -> {
ProductModel product = new ProductModel();
BeanUtils.copyProperties(entity, product);
if (Objects.nonNull(entity.getCategory())) {
BeanUtils.copyProperties(entity.getCategory(), product.getCategory());
}
return product;
}).collect(Collectors.toList());
}
and I need the output will be in below format.
{
"_id": {
"$oid": "6141eeab92432109463ae8c4"
},
"name": "Adidas Running Shoes",
"category": {
"_id": {
"$oid": "614049d91042cf40b5b9b304"
},
"name": "Shoes",
"desc": "Men's running Shoes",
}
}
Please let me know how to get desired output.
I've done a minor change and issue has been resolved. I've changed
from
#MongoId(value = FieldType.STRING)
private String id;
to
#MongoId(value = FieldType.OBJECT_ID)
private ObjectId id;
in the both entities and code works!

How to get data with referenced object in Spring data mongo?

This is City document class which i used.
#Document("city")
public class City {
#Id
private String id;
#Indexed(unique = true)
private String name;
#DBRef(lazy = true)
private District district;
public City() {
}
public City(String id) {
this.id = id;
}
public City(String id, String name, District district) {
this.id = id;
this.name = name;
this.district = district;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public District getDistrict() {
return district;
}
public void setDistrict(District district) {
this.district = district;
}
}
and this is the District document class
#Document("district")
public class District {
#Id
private String id;
#Indexed
private String name;
public District() {
}
public District(String id) {
this.id = id;
}
public District(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and this is the district collection data.
/* 1 */
{
"_id" : ObjectId("5d9482f3ff7ab743f0542070"),
"name" : "Ampara",
"_class" : "com.test.sample.model.District"
}
/* 2 */
{
"_id" : ObjectId("5d9482f3ff7ab743f0542071"),
"name" : "Anuradhapura",
"_class" : "com.test.sample.model.District"
}
and this is the city collection data.
/* 1 */
{
"_id" : ObjectId("5d948333ff7ab743f0542089"),
"name" : "Polgampola",
"district" : {
"$ref" : "district",
"$id" : ObjectId("5d9482f3ff7ab743f0542070")
},
"_class" : "com.test.sample.model.City"
}
/* 2 */
{
"_id" : ObjectId("5d948333ff7ab743f054208a"),
"name" : "Porawagama",
"district" : {
"$ref" : "district",
"$id" : ObjectId("5d9482f3ff7ab743f0542070")
},
"_class" : "com.test.sample.model.City"
}
/* 3 */
{
"_id" : ObjectId("5d948333ff7ab743f054208b"),
"name" : "Akkaraipattu",
"district" : {
"$ref" : "district",
"$id" : ObjectId("5d9482f3ff7ab743f0542071")
},
"_class" : "com.test.sample.model.City"
}
this is the repository class for the above city document class
#Repository
public interface CityDao extends MongoRepository<City,String> {
List<City> findByDistrict(String id);
}
in the city class, i have used referenced document class called District. this a another document. I need to get the cities which belong to one district. findByDistrict(String id) method doesn't return values. Its just returning empty List. So How can i do this ?
I have tried with another alternative.As #pvpkirn said, You cannot query with #DBRef. But i tried with MongoOperations. With this we can pass the Filter objects as Json object. Using this i got the answer. This is what i tried.
public List<City> findByDistrictId(String districtId) {
final Bson filter = eq("district.$id", new ObjectId(districtId));
FindIterable<Document> documents = super.mongoOperations.getCollection("city").find(filter);
List<City> cities = new ArrayList<>();
for(Document document : documents){
City city = new City(document.getObjectId("_id").toString(),document.getString("name"),new District(districtId));
cities.add(city);
}
return cities;
}
in here i have used com.mongodb.client.model.Filters.eq to get the relevant bson object.

Fetch document by array of value in MONGODB and Spring data?

How to fetch the document by array of value from MONGODB using SPRING DATA.?
My document
{
"id": "5b18fdef89d67e272025f2e3",
"date": "2018-05-10 11:31:37",
"active": true,
"obsolete": false,
"tenant": {
"id": "5ad847e54925fb0fa4424e1a"
},
"plan": {
"id": "5ad7115f7b4152204c86fce6"
},
"log": [
"5b18fdef89d67e272025f2e4"
],
"lineItem": [
"5b18fdef89d67e272025f2e5",
"5b18fdef89d67e2720259899",
"5b18fdef89d67e272025sd5s"
]
}
{
"id": "5b18fdef89d67e272025f232",
"date": "2018-05-12 11:31:37",
"active": true,
"obsolete": false,
"tenant": {
"id": "5ad847e54925fb0fa4424e1a"
},
"plan": {
"id": "5ad7115f7b4152204c86fce6"
},
"log": [
"5b18fdef89d67e272025f23434"
],
"lineItem": [
"5b18fdef89d67e272025f111",
"5b18fdef89d67e2720259222",
"5b18fdef89d67e272025s333"
]
}
I want filter the document by lineItem array. If I give the lineItem value "5b18fdef89d67e2720259222" it will be return document which holds the same lineitem.
Model class
#Document(collection = "trn_inventory")
public class Inventory {
#Id
private String id;
private String date;
private List<String> lineitem;
private String tenant, plan;
private List<String> log;
private boolean active, obsolete;
//getters and setters
}
Repository
#Repository
public interface InventoryRep extends MongoRepository<Inventory, String> {
public List<Inventory> findByLineitemIn(String lineitem);
}
I have done the mistake in Repository. After spend the long time, I found it myself (stupid coder). The problem is parameter of the findByLineitemIn method is String that should be List type.
Here is the corrected Repository...
#Repository
public interface InventoryRep extends MongoRepository<Inventory, String> {
public List<Inventory> findByLineitemIn(List<String> lineitem);
}
How foolish is this.. Isn't it? I think this will helpful for some other coder like me. ;)

How to store prices in MongoDB for best practices?

First thanks in advance for the help.
I have an application with Spring Boot and mongo using Spring Data and I have something like this:
#Document("products")
class Product {
#Id
private String $id;
private String $name;
private Money price;
private Money deliveryPrice;
}
class Money {
private BigDecimal amount;
private Currency currency;
}
If I save a product it saves like:
{
_id: ObjectId("AAAA"),
name: "Product 1",
price: {
amount: "100",
currency: "EUR"
},
deliveryPrice: {
amount: "10",
currency: "EUR"
}
}
It's correct but I don't like how it saves.
I would like to store like:
{
_id: ObjectId("AAAA"),
name: "Product 1",
price: DecimalNumber(100),
deliveryPrice: DecimalNumber(10),
currency: "EUR"
}
That way I am not duplicating information that is not needed in all the prices value.
How can I solve this?
Is this the best solution? In case not how should it be?
Thanks
class Money {
private BigDecimal amount;
#Transient
private Currency currency;
}
Now the currency property will not be persisted in MongoDB.

iterate objects in java 8 streams

I'd like to iterate the following mongo db collection in java8
{ "_id" : ObjectId("59d1dc99a1a79605342f15a7"), "_class" : "com.test.User", "vehicle" : [ { "name" : "aaa", "color" : "blue" } ], "count" : 1,"createdBy" : "Admin", "lastModifiedBy" : "Admin" }
Expected O/P fields:
_id,name,color,count,createdBy
I tried the below
queryvalue.stream().flatMap(vehicle->vehicle.getVehicles().stream()).collect(Collectors.toList());
queryvalue is the one where i'm pulling the data from spring mongo db repository. I'm new to java8 pls help.
Below are the classes:
Class User{
#CreatedDate
protected DateTime createdDate;
#LastModifiedDate
protected DateTime lastModifiedDate;
List<Vehicle> vehicle;
public void setCreatedDate(DateTime createdDate) {
this.createdDate = createdDate;
}
public String getCreatedDate() {
return createdDate;
}
public List<Vehicle> getVehicles() {
return vehicle;
}
public void setVehicle(List<Vehicle> vehicle) {
this.vehicle= vehicle;
}
-- similarly for lastModifieddate and comments etc--
}
Class Vehicle{
private String name;
private string color;
public void setColor(String color){
this.color = color;
return this;
}
public String getColor(){
return color;
}
public void setName(String name){
this.name = name;
return this;
}
public void getName(){
return name;
}
}
And am pulling using this:
List<Vehicle>queryvalue = repository.findByVehicleName(vehiclename);