JPA Circular dependency on same entity resulting in recursive output - json

I am having a Folder class which in-turn can have many sub-folders. In this case i am trying to make use of same entity that single entity is being used to achieve the same.
Something like below.
Folders
----SubFolder
----SubFolder
Below are the classes.
FolderData.java
#Entity
#JsonIdentityInfo(generator =ObjectIdGenerators.IntSequenceGenerator.class,property="projectId")
public class FolderData {
#Id
#SequenceGenerator(name = "seq-gen", initialValue = 1)
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "seq-gen")
private Integer parentId;
private int moduleId;
private int subProjectId;
private String folderName;
private Integer folderId;
private int projectId;
#ManyToOne(fetch = FetchType.LAZY,cascade=CascadeType.ALL)
#JoinColumn(name="folder_child")
#JsonIgnore
private FolderData folderData;
#OneToMany(mappedBy = "folderData")
#JsonIgnoreProperties("folderList")
private Set<FolderData> folderList=new HashSet<>();
}
FodlerController.java
#RestController
#RequestMapping("/folder")
public class FodlerController {
#Autowired
private FolderService folderService;
#GetMapping(produces = "application/json")
public List<FolderData> getFolderList(){
return folderService.findAllFromTheList();
}
#PostMapping(produces = "application/json", consumes = "application/json")
public void createFolder(#RequestBody FolderData folderData) {
if(folderData.getId()==null && folderData.getFolderId()==null) {
System.out.println("id is null");
folderData.setFolderId(new Random().nextInt());
folderService.save(folderData);
}
else {
folderService.doChildAddition(folderData);
}
}
}
FolderService.java
#Service
public class FolderService {
#Autowired
private FolderRepo folderRepo;
public FolderData save(FolderData folderData) {
return folderRepo.save(folderData);
}
public FolderData getFolderDataByParentId(Integer id) {
return folderRepo.getOne(id);
}
public List<FolderData> findAllFromTheList() {
return folderRepo.findAll();
}
public FolderData getFolderDataByfolderId(Integer folderId) {
return folderRepo.findFolderByFolderId(folderId);
}
public void doChildAddition(FolderData childFolder) {
FolderData parentFolder=folderRepo.findFolderByFolderId(childFolder.getFolderId());
childFolder.setFolderData(parentFolder);
FolderData childFolderSaved = folderRepo.saveAndFlush(childFolder);
//parentFolder.getFolderList().add(folderRepo.getOne(childFolderSaved.getId()));
//folderRepo.save(parentFolder);
}
}
Json Request for creating parent and the response collected which includes autogenerated folder id:
{
"moduleId":1,
"subProjectId":1,
"folderName":"One",
"projectId":1
}
[
{
"projectId": 0,
"moduleId": 1,
"subProjectId": 1,
"folderName": "One",
"folderId": 963031296,
"folderList": [],
"id": 1
}
]
Creating child for the parent and the response collected.
{
"moduleId":1,
"subProjectId":2,
"folderName":"Two",
"projectId":1,
"folderId": -963031296
}
[
{
"projectId": 0,
"moduleId": 1,
"subProjectId": 1,
"folderName": "One",
"folderId": 963031296,
"folderList": [
{
"projectId": 0,
"moduleId": 1,
"subProjectId": 2,
"folderName": "Two",
"folderId": 963031296,
"id": 2
}
],
"id": 1
},
2
]
For the above response i am getting the No 2 with the response since i am using
#JsonIdentityInfo(generator =ObjectIdGenerators.IntSequenceGenerator.class,property="parentId")
else the whole object would have come instead of 2.
Example output had i not used JsonIdentityInfo
[
{
"projectId": 0,
"moduleId": 1,
"subProjectId": 1,
"folderName": "One",
"folderId": 963031296,
"folderList": [
{
"projectId": 0,
"moduleId": 1,
"subProjectId": 2,
"folderName": "Two",
"folderId": 963031296,
"id": 2
}
],
"id": 1
},
{
"projectId": 0,
"moduleId": 1,
"subProjectId": 2,
"folderName": "Two",
"folderId": 963031296,
"id": 2
}
]

It's easy, #Prash
Since you have used a Set in a undirectional dependency you must supply an uniqueness of a joined (depended) entities. Any set is using hashCode() and equals() to determine an availability of the concurent item that is want to be added. Your FolderData class doesn't have hashCode() and equals() so from the point of view of JVM all the deserialized objects are same and because of the Set nature it's normal to keep only last example

Related

Create a model and insert value with a #Query

i have this class:
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
public class ViewOrderRTO {
private List<LineProductModel> products;
private OrderModel order;
}
that i use for display data saved in db. LineProductModel is a class that has these field: total price, quantity,id, a #ManyToOne relationship with productModel and a #Many to one relationship with OrderModel.
The question is: can i use #Query in something like this "SELECT new alongpath.ViewOrderRTO(....) ..." and insert a list of lineProduct and the other fields all in one query? or i have to split the work and get all the single LineProductModel and put in a list first and then create the ViewModel?
btw is a spring boot project and i'm using mysql
I "solved" the problem removing the #ManyToOne mapping inside the LineProductModel and adding the #OneToMany mapping in the Order Model now i have this:
#Entity
#Table(name = "order_commission")
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
public class OrderModel {
#Id
#SequenceGenerator(name = "order_sequence",
sequenceName = "order_sequence",
initialValue = 0,
allocationSize = 1
)
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "order_sequence")
private Long id;
#OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true)
private List<LineProductModel> linePoducts;
private BigDecimal total;
private LocalDateTime orderedAt;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "fk_user")
private UserModel user;
public OrderModel(List<LineProductModel> lineProducts,
BigDecimal total,
LocalDateTime orderedAt,
UserModel user) {
this.linePoducts = lineProducts;
this.total = total;
this.orderedAt = orderedAt;
this.user = user;
}
}
and this is the json i get while performing a #GetMapping:
{
"id": 0,
"linePoducts": [
{
"id": 0,
"quantity": 2,
"lineProductPrice": 6,
"product": {
"id": 0,
"eanCode": "1111111111111",
"name": "pipo",
"price": 3,
"quantity": 8,
"weight": 10,
"description": "test",
"category": {
"id": 0,
"name": "cibo"
}
}
},
{
"id": 1,
"quantity": 7,
"lineProductPrice": 21,
"product": {
"id": 1,
"eanCode": "2111111111111",
"name": "pipobevanda",
"price": 3,
"quantity": 3,
"weight": 10,
"description": "test",
"category": {
"id": 1,
"name": "bevanda"
}
}
},
{
"id": 2,
"quantity": 1,
"lineProductPrice": 3,
"product": {
"id": 2,
"eanCode": "3111111111111",
"name": "pipoverdura",
"price": 3,
"quantity": 9,
"weight": 10,
"description": "test",
"category": {
"id": 2,
"name": "verdura"
}
}
}
],
"total": 0,
"orderedAt": "2022-08-10T20:48:15",
"user": {
"id": 0,
"firstName": "-------",
"lastName": "------",
"email": "--------",
"password": "------",
"countryOfResidence": "italy",
"birthdate": "-------",
"balance": 100
}
}
i have to make some changes about the information that displays, but is there a better solution or this one is ok?

how to parse a json similar string and map it into a pojo

I am trying to map the following JSON to my POJO using Jackson. I have the following JSON and following POJOs. kindly let me know how to map the JSON to POJO.
JSON string :
{
"Application": {
"id": "0",
"name": "MyApp",
"users": [
{
"User": {
"id": "2",
"name": "Beth Jones"
}
}
],
"groups": [
{
"Group": {
"id": "1",
"name": "SimpleGroup",
"users": [
{
"User": {
"id": "2",
"name": "Beth Jones"
}
}
]
}
}
]
}
}
The POJO according to the client specification is below :
package com.example.custom;
//import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.example.Application;
import com.example.Group;
import com.example.User;
import java.util.Collection;
//#JsonIgnoreProperties(ignoreUnknown = true)
public class MyApplication extends Application {
private Collection<User> users;
private Collection<Group> groups;
public MyApplication(String id, String name) {
super(id, name);
}
public void setUsers(Collection<User> users) {
this.users = users;
}
public void setGroups(Collection<Group> groups) {
this.groups = groups;
}
#Override
public Collection<User> getUsers() {
return this.users;
}
#Override
public User getUser(String userId) {
for (User user: MyParser.myApp.getUsers()) {
if (user.getId().equals(userId))
return user;
}
return null;
}
#Override
public Collection<Group> getGroups() {
return this.groups;
}
#Override
public Group getGroup(String groupId) {
for (Group group: MyParser.myApp.getGroups()) {
if (group.getId().equals(groupId))
return group;
}
return null;
}
#Override
public String toString() {
return "MyApplication{" +
"users=" + users +
", groups=" + groups +
'}';
}
}
Mapping Logic :
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MyParser.myApp = mapper.readValue(rewriter.getText(),MyApplication.class);
The resulting object is not able to capture anything as it is all null. Kindly help. Thanks in advance.
I think you should model your JSON correctly, In the users list you shouldn't specify it again that the key is User, that should be preassumed that a list of users will only contain user, same goes for groups list.
IMHO the JSON should look something like this :
{
"application": {
"id": "0",
"name": "MyApp",
"users": [ . ==> Since this is a user List, it will definitely contains user.
{
"id": "2",
"name": "Beth Jones"
}
],
"groups": [
{
"id": "1",
"name": "SimpleGroup",
"users": [
{
"id": "2",
"name": "Beth Jones"
}
]
}
]
}
}
Now the POJO also needs some modification, I am just adding the bare-minimum POJO.
class Application { <====== Top Level Class
private Long id;
private String name;
private List<User> users; // Application has some Users
private List<Group> groups; // Application has some groups
}
class User {
private Long id;
private String name;
}
class Group {
private Long id;
private String name;
private List<User> users; // Each group has some associated users.
}
Now you can use any standard JSON library for Java and convert your JSON into POJO. This will simplify your structure and you won't face null issues with this structure.

Display certain items from array of objects in recyclerview

From the code below, I want to display request_id, request_title, status, no of trucks in recyclerview using retrofit 2.0. How to do this?
Here is my nested array of objects.
[
{
"id": 96,
"request_id": 24365,
"request_title": "tsfghjjlfdsg;lhj",
"transport_co_id": 1,
"status": 1,
"truck_info": [
{
"driver_mobile": 2147483647,
"driver_name": "dsdsd",
"truck_no": 1111
},
{
"driver_mobile": 2147483647,
"driver_name": "add",
"truck_no": 727
}
]
},
{
"id": 97,
"request_id": "test123",
"request_title": "test title",
"transport_co_id": 1,
"status": 1,
"truck_info": [
{
"driver_mobile": 2147483647,
"driver_name": "dsdsd",
"truck_no": 1111
},
{
"driver_mobile": 2147483647,
"driver_name": "add",
"truck_no": 1
}
]
}
]
create retrofit interface , class
public class ChampApiClient {
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}

Jackson: JSON parsing for array of parent elements : Root name '' does not match expected ('List')

I am getting the following exception after writing the below code.
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Root name 'Filing' does not match expected ('List') for type [collection type; class java.util.List, contains [simple type, class MasMonthlyReportDetail]]
JSON Object
{
"Filing":
[
{
"periodInfo":
{
"date": "06-05-2013",
"year": "2015",
"Month": "January"
},
"employerInfo":
{
"name": "Y",
"place": "Y",
"country": "N",
},
"employeeInfo":
[
{
"name": "785-23-0370",
"dob": "05/25/1952",
}
],
"messages":
[
{
"defaultMessage" : "xx",
"messageType" : "yy",
"messageCode" : "102"
}
]
},
{
"periodInfo":
{
"date": "06-05-2013",
"year": "2015",
"Month": "January"
},
"employerInfo":
{
"name": "Y",
"place": "Y",
"country": "N",
},
"employeeInfo":
[
{
"name": "785-23-0370",
"dob": "05/25/1952",
}
],
"messages":
[
{
"defaultMessage" : "xx",
"messageType" : "yy",
"messageCode" : "102"
}
]
}
]
}
Main Class
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
List<MasMonthlyReportDetail> lcd = objectMapper.readValue(new File(filePath),new TypeReference<List<MasMonthlyReportDetail>>(){});
MasMonthlyReportDetail.java
#JsonRootName(value="Filing")
public class MasMonthlyReportDetail {
private PeriodInfo periodInfo;
private EmployerInfo employerInfo;
List<EmployeeInfo> employeeInfo;
List<Messages> messages;
public PeriodInfo getPeriodInfo() {
return periodInfo;
}
public void setPeriodInfo(PeriodInfo periodInfo) {
this.periodInfo = periodInfo;
}
public EmployerInfo getEmployerInfo() {
return employerInfo;
}
public void setEmployerInfo(EmployerInfo employerInfo) {
this.employerInfo = employerInfo;
}
public List<EmployeeInfo> getEmployeeInfo() {
return employeeInfo;
}
public void setEmployeeInfo(List<EmployeeInfo> employeeInfo) {
this.employeeInfo = employeeInfo;
}
public List<Messages> getMessages() {
return messages;
}
public void setMessages(List<Messages> messages) {
this.messages = messages;
}
}
I made the following changes and the code worked for me.
Main Class:
InputStream inputStream = resource.getInputStream();
ObjectMapper objectMapper = new ObjectMapper();
MasMonthlyReportDetailHolder masMonthlyReportDetailHolder = objectMapper.readValue(inputStream, MasMonthlyReportDetailHolder.class);
List<MasMonthlyReportDetail> masMonthlyReportDetail = masMonthlyReportDetailHolder.getMasMonthlyReportDetail();
MasMonthlyReportDetailHolder class:
public class MasMonthlyReportDetailHolder {
private List<MasMonthlyReportDetail> masMonthlyReportDetail;
#JsonProperty("Filing")
public List<MasMonthlyReportDetail> getMasMonthlyReportDetail() {
return masMonthlyReportDetail;
}
public void setMasMonthlyReportDetail(
List<MasMonthlyReportDetail> masMonthlyReportDetail) {
this.masMonthlyReportDetail = masMonthlyReportDetail;
}
}
Adding #JsonProperty("Filing") is the key to avoid this issue. In case of any other procedure, do let me know.
have u tried this ?
jacksonObjectMapper.reader(MasMonthlyReportDetail.class).withRootName("Filing").readValue(jsonAsString);

How to achieve same output after migrating from jackson to gson as json processor?

I am developing a restful webservices using resteasy. Earlier, the provider was jackson.
And the result output is
[
{
"status": {
"id": 22,
"name": "VERIFY",
"note": ""
}
},
{
"status": {
"id": 23,
"name": "ACCEPTED",
"note": ""
}
},
{
"status": {
"id": 24,
"name": "POSTPONED",
"note": "for cancel update"
}
},
{
"status": {
"id": 29,
"name": "AMC(NEW)"
}
},
{
"status": {
"id": 30,
"name": "AMC(ASSIGNED)"
}
}
]
Later, we moved to gson as provider. http://eclipsesource.com/blogs/2012/11/02/integrating-gson-into-a-jax-rs-based-application/
The output change as :
[
{
"id": 0,
"name": "DISABLE"
},
{
"id": 1,
"name": "ENABLE"
},
{
"id": 31,
"name": "REJECTED",
"note": ""
},
{
"id": 25,
"name": "ASSIGNED"
}
]
The class definition
package com.apt.common.web.pojo;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Status {
Integer id;
String name;
private String note;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString(){
return name;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Status other = (Status) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
}
Output is totally different.
Using jackson
{
"status": {
"id": 22,
"name": "VERIFY",
"note": ""
}
}
using gson
{
"id": 0,
"name": "DISABLE"
}
Code for generating json using gson is
Gson gson=new Gson();
Type fooType = new TypeToken<List<Status>>() {}.getType();
return gson.toJson(statuses,fooType);
How can i achieve the same output using gson?
I guess that in order to achieve the same result using Gson, you'll need another class to wrap your Status class, with an attribute called status, because what Gson includes in the serialized JSON are the names of the attributes, not the names of the classes!
So you'll need something like:
public class MyClass {
private Status status;
//...
}
And then in the serialization:
//...
Type fooType = new TypeToken<List<MyClass>>() {}.getType();
//...
In other words, for Gson, this JSON piece:
{
"status": {
"id": 22,
"name": "VERIFY",
"note": ""
}
}
represents this class structure:
class MyClass
Status status;
class Status
int id;
String name;
String note;
If you don't want to create another class, you can always write a custom serializer for your Status class...