Good morning, I am trying to receive from spring boot with a ajax call a JSON with a series of fields containing daughter tables and also father.
I already had the problem of infinite recursion and such and fixed it with #JsonManagedReference and #JsonBackReference. What is happening now? I need to at least get to the data in the parent table to get a value but this one, when we get to the answer in Ajax it doesn't appear.
How could I do so that I could get to that data without falling into infinite recursion?
These would be the tables, I remove the fields so that it is not very long.
#Entity
#Table(name="actividad_servicio")
public class Actividad_Servicio implements Serializable {
#JsonBackReference
#ManyToOne
#JoinColumn(foreignKey = #ForeignKey(name = "fk_actividad_servicio_tipo_actividad_servicio_id"), name="tipo_actividad_servicio_id", nullable = false, columnDefinition = "int")
private Tipo_Actividad_Servicio tipo_actividad_servicio;
#JsonBackReference
#ManyToOne
#JoinColumn(foreignKey = #ForeignKey(name = "fk_actividad_servicio_empleado_id"), name="empleado_id",columnDefinition = "int")
private Empleado empleado;
#JsonManagedReference
#OneToMany(mappedBy = "actividad_servicio", cascade=CascadeType.ALL, orphanRemoval = true)
private Set<Actividad_Servicio_Alumno> actividad_servicio_alumno = new HashSet<Actividad_Servicio_Alumno>();
#JsonManagedReference
#OneToOne(mappedBy = "actividad_servicio", cascade=CascadeType.ALL, orphanRemoval = true)
private Horario_Actividad_Servicio horario_actividad_servicio = new Horario_Actividad_Servicio();
#JsonManagedReference
#OneToMany(mappedBy = "actividad_servicio", cascade=CascadeType.ALL, orphanRemoval = true)
private Set<Factura> factura = new HashSet<Factura>();
}
#Entity
#Table(name="horario_actividad_servicio")
public class Horario_Actividad_Servicio implements Serializable {
#JsonBackReference
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(foreignKey = #ForeignKey(name = "fk_horario_actividad_servicio_actividad_servicio_id"), name="actividad_servicio_id", referencedColumnName = "id", columnDefinition = "int")
private Actividad_Servicio actividad_servicio;
}
#Entity
#Table(name="tipo_actividad_servicio")
public class Tipo_Actividad_Servicio implements Serializable {
#JsonManagedReference
#OneToMany(mappedBy = "tipo_actividad_servicio", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Actividad_Servicio> actividad_servicio = new HashSet<Actividad_Servicio>();
}
Before returning the response back to Ajax, I look at the content of the service_activity object and it shows me the service_activity_type and its attributes.
#RestController
#RequestMapping("/actividades/rest")
public class ActividadRestController {
#Autowired
private Actividad_ServicioService actividad_servicioService;
#GetMapping
public Response findOne(#RequestParam int id, Model model) {
Response response = new Response();
try {
Actividad_Servicio actividad_servicio = actividad_servicioService.findById(id);
response = new Response("Done", actividad_servicio);
} catch (Exception e) {
response = new Response("Error", Constants.Error);
e.printStackTrace();
}
return response;
}
}
But when it comes back to Ajax I have inspected the content what I get is this:
Success: - {…}
- data: {…}
+ actividad_servicio_alumno: Array []
+ factura: Array []
+ horario_actividad_servicio: Object { id: 10, l: null, hora_inicio_l: "", …}
<prototype>: Object { … }
status: "Done"
<prototype>: Object { … }
But the service_activity_type does not appear in order to obtain the value of some field. I know that this collides with the issue of infinite recursion but I suppose that there is something there is an annotation that happens to me that I have left to put so that I can obtain those parent values.
Thank you very much for your help.
First, don't use Entity class directly as response, it will create many problem when you want to ignore partially.
Create DTO class for the response to serialize only those fields you want to send. Map entity data in response using any model mapping tools like Modelmapper or MapStruct.
Related
I have 2 DTOs "OrderItem" and "Ingredient", both classes has #ManyToMany annotation:
#Entity
#Table
#NoArgsConstructor
#Data
public class OrderItem {
private #Id #GeneratedValue #NotNull long id;
#ManyToOne(optional = false)
#JoinColumn(nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
private Order order;
#ManyToOne(optional = false)
#JoinColumn(nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
private Food food;
private int quantity;
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(
name = "order_item_ingredient",
joinColumns = #JoinColumn(name = "order_item_id"),
inverseJoinColumns = #JoinColumn(name = "ingredient_name")
)
private Set<Ingredient> ingredients = new HashSet<>();
}
#Entity
#Table
#Data
#NoArgsConstructor
public class Ingredient {
private #Id String ingredientName;
private float basePrice;
private boolean addable;
#ManyToMany(mappedBy = "ingredients",cascade=CascadeType.ALL)
private Set<Food> foods= new HashSet<>();
#ManyToMany(mappedBy = "ingredients",cascade=CascadeType.ALL)
private Set<OrderItem> orderItems= new HashSet<>();
public Ingredient(String ingredientName, float basePrice, boolean addable) {
this.ingredientName = ingredientName.toLowerCase();
this.basePrice = basePrice;
this.addable = addable;
}
}
And I'm looking to add a new OrderItem using a POST request using the following #PostMapping controller function:
#PostMapping("{id}/orderItem")
public ResponseEntity<OrderItem> createMenuItem(
#PathVariable(value = "id") Long orderId,
#RequestBody OrderItem orderItem) {
Order order = orderService.getOrder(orderId)
.orElseThrow(() -> new ResourceNotFoundException("order '" + orderId + "' is not found"));
orderItem.setOrder(order);
orderItemRepository.save(orderItem);
return new ResponseEntity<>(orderItem, HttpStatus.CREATED);
}
When I send a post request to localhost:8080/1/orderItem with the following body:
{
"order":"1",
"food":"burger",
"quantity":"1"
}
It works fine and a new order_item database record is created, but when I send the same request with the following body:
{
"order":"1",
"food":"burger",
"quantity":"1",
"ingredients": [{"ingredientName":"leaf"}]
}
It fails and gives the following SQL error:
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'leaf' for key 'ingredient.PRIMARY'
I know that this record already exists, but how do I tell Spring Boot that I want it to look for an existing Ingredient instead of trying to create a new one?
I have an ugly solution in my mind, and that is to send the OrderItem object alongside a list of strings where each element represents a primary key for Ingredient class, then iterate through that list element by element calling the repository to get the Ingredient object then manually add it to OrderItem.ingredients, but I'm sure that is not the best solution out there.
Being defined on the OrderItem class, the relation ingredients is considered as a composition on the cascading strategy point of view. Therefore, the CascadeType.ALL implies the attempt to create the ingredient.
To avoid this, you can change the direction of this relation reverse the mappedBy information.
But then again, if you keep a CascadeType.ALL on the ingredient side, you will be in trouble if you create an ingredient with an existing orderItem. You can win on both sides an use CascadeType.ALL.
check JPA Hibernate many-to-many cascading
I'm trying to create a Rest API for a school project.Therefor I'm trying to save/edit a nested Object.
I have two bidirectional entities which look like this:
EntityA
#Entity
public class EntityA {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
#Column(name = "id", nullable = false)
#JsonProperty("id")
private int id;
#Column(name = "field1", nullable = false, length = -1)
#JsonProperty("field1")
private String field1;
#Column(name = "field2", nullable = false, length = -1)
#JsonProperty("field2")
private String field2;
#OneToMany(mappedBy = "entityA", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JsonProperty("entityB")
private List<EntityB> entityB;
public EntityA() {
}
//Getter+Setter
}
EntityB
#Entity
public class EntityB {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
#Column(name = "id", nullable = false)
#JsonProperty("id")
private int id;
#Column(name = "field1", nullable = false)
#JsonProperty("field1")
private Date field1;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(...)
#JsonProperty("entityA")
private EntityA entityA;
public EntityB() {
}
//Getter+Setter
}
As RequestBody I will get JSON which should look like this.
{
"field1": "Test",
"field2": "User",
"entityB": [
{
"field1": "30.03.2022"
}
]
}
Right now Spring will automatically map the fields but as soon I try to save it to my DB I will get an error, because the relation in EntityB for EntityA is empty.
I've seen a solution, that I should loop through the EntityB list and add EntityA. I tried it with a for-each but it still sais it null.
What am I doing wrong?
public EntityA createEntityA(EntityA entityA) {
for(EntityB entityB : entityA.getEntityB()){
entityB.setEntityA(entityA);
}
return entityARepository.save(entityA);
}
Edit:
Controller
#PostMapping(value = {"/json/entitya/"})
#ResponseBody
public EntityA createEntityAJson(#RequestBody EntityA entityA) {
return entityAService.createEntityA(entityA);
}
Service
#Service
public class EntityAService {
#Autowired
private EntityARepository entityARepository;
public EntityA createEntityA(EntityA entityA) {
return entityARepository.save(entityA); //in this line the error appears
}
}
Error message
null value in column "entityA" violates not-null constraint
#Service
public class EntityAService {
#Autowired
private EntityARepository entityARepository;
#Autowired
private EntityBRepository entityBRepository;
public EntityA createEntityA(EntityA entityA) {
// create an empty arrayList to stock the entities B retrieveed from the DB
List<EnityB> lst = new ArrayList<>();
// get the entities B from the JSON and sabe it to the DB
for(EntityB entityB : entityA.getEntityB()){
entityB.setEntityA(entityA);
entityBRepository.save(entityB); // you should save entities B to the DataBase before
Optional<EntityB > opt = entityBRepository.findById(entityB.getId());
EntityB b = opt.get();
// add the entities B retrieved from the DB to the arrayList
lst.add(b);
}
// set the EntityB list with the new List from the DB ( include ids ..)
entityA.setEntityB(lst);
// save the entityA to the DB
return entityARepository.save(entityA);
}
}
I'm guessing that what is happening here is that the id fields which are of a non-nullable datatype or some other hidden field from the JPA annotations get set to the wrong value by the json deserialization for JPA to understand that they are new entities. Creating these entities manually in the Java code might solve the issue.
You shouldn't reuse your entity classes as data transfer object for your API. Having classes containing both database-specific annotations and annotations for JSON serialization is a bad idea and it goes against the single-responsibility principle (SRP).
Create separate DTO classes for your API endpoint, then read the entities from the database an copy the values from the DTO object to the entities before saving.
// Receive DTO
// Read entity from DB if update or create new entities if insert
// Copy values from DTO to entitiy
// Save entity
I think your problems will go away if you apply this pattern.
I am developing rest web app with spring framework, Hibernate and JSON. Please Assume that I have two entities like below:
BaseEntity.java
#MappedSuperclass
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id" )
public abstract class BaseEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
public long getId() {
return id;
}
}
University.java
public class University extends BaseEntity {
private String uniName;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,orphanRemoval = true)
#JoinColumn(name = "university_id")
private List<Student> students=new ArrayList<>();
// setter an getter
}
Student.java
public class Student extends BaseEntity{
private String stuName;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "university_id",updatable = false,insertable = false)
private University university;
// setter an getter
}
when I call my rest api to list University every things work fine as I expect, but when I call my rest api to list student eagerly my JSON response is
[
{
"id": 1,
"stuName": "st1",
"university": {
"id": 1,
"uniName": "uni1"
}
},
{
"id": 2,
"stuName": "st2",
"university": 1
}
]
but my desired response is:
[
{
"id": 1,
"stutName": "st1",
"university":
{
"id": 1,
"uniName": "uni1"
}
},
{
"id": 2,
"stutName": "st2",
"university":
{
"id": 1,
"uniName": "uni1"
}
}
Update 1: my hibernate annotation working fine I have JSON issue
Requirements :
I need both side fetch eagerly(the university side is Ok)
I need university object in student side for every student(when I fetching student eagerly)
What kind of serialization or JSON config I need to do that for matching my desired response?
Update 2:
by removing #JsonIdentityInfo and editing student side like below:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "university_id",updatable = false,insertable = false)
#JsonIgnoreProperties(value = "students", allowSetters = true)
private University university;
the json response still same
I need my desired response that is mentioned above.
thanks
Remove #JsonIdentityInfo from base class, this is causing university object to serialize only id.
Can you add #JoinColumn to Student entity as well
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = student_id")
Also check your University entity class's foreign key.The foreign key should be from other entity right?
#JoinColumn(name = "university_id",foreignKey = #ForeignKey(name = "student_id")) ??
Else alternatively you can use the "mappedBy" as well.
#JoinColumn(name = "university_id", mappedBy="university")
private List<Student> students=new ArrayList<>();
You can add this and check
University
public class University {
#Fetch(value = FetchMode.SELECT)
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "university_id")
#JsonIgnore
private List<Student> students;
}
Student
public class Student{
#ManyToOne
#JoinColumn(name = "university_id", insertable = true, updatable = true, nullable = true)
private University university;
}
I understand you do not want to include University.students in your JSON.
Remove #JsonIdentityInfo
#MappedSuperclass
//#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id" )
public abstract class BaseEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
public long getId() {
return id;
}
}
Add #JsonIgnore to students to avoid circle
public class University extends BaseEntity {
private String uniName;
#JsonIgnore
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,orphanRemoval = true)
#JoinColumn(name = "university_id",foreignKey = #ForeignKey(name = "university_id"))
private List<Student> students=new ArrayList<>();
// setter an getter
}
If you need University.students to be serialized in other contexts give http://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion a read. Other options to deal with bidirectional relationships are explained there.
Add #jsonignore for getter method
and add #jsonProperty to the field
like
#JsonProperty(access = Access.READ_ONLY)
private String password;
Recently added some feature to jackson like Readonly and writeonly
you can refer this:
http://fasterxml.github.io/jackson-annotations/javadoc/2.6/com/fasterxml/jackson/annotation/JsonProperty.Access.html
You might want to try using #JsonRawValue as an annotation for your university property. The behavior you're encountering is due to reference collapsing - since it's the same University twice, the serializer tries to be smart and just return a reference the second time it's encountered.
EDIT: The toString():
#Override
public String toString() {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(this);
}
The way you map your relationsip, even if it is "working fine", does not comply with jpa spec for bi-directional relationship. See this question and related answers.
To summarize usually, the owner of the relationship is the many-to-one side and the one-to-many side is annotated with mappedBy. Your mapping solution, with the one-to-many side owning the relationship is not usual / recommended (as described in the answers above) but technically possible. (#manyToOne side misses some attributes like "updatable=false" in your example)
Then, with JPA and recent Hibernate version, the lazy loading policy is the following:
OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER
So I would suggest you to use this default lazy loading policy, and to change the owner of your manyToOne relationship as it does not seem like a good idea to get all the students via a single University resource request. (Have you heard about pagination?)
Doing so, and also excluding students collection from Marshalling, using for example #JsonIgnore, should do the trick.
I had the same problem. Hibernate (or eclipselink) are not the problem.
The only constraint in JPA is the FetchType.EAGER .
In the BaseEntity I have added a standard method
public String getLabel(){
return "id:"+this.getId();
}
this method would be abstract, but I had a lot of class and i didn't want to change it all so I added a default value.
In parent entity, in this case University, override the method
#Override
public String getLabel{
return this.uniName;
}
For each parent class, use a particular field as a label for your entity
Define a MyStandardSerializer:
public class StandardJsonSerializer extends JsonSerializer<EntityInterface> {
#Override
public void serializeWithType(EntityInterface value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonProcessingException {
serialize(value, jgen, provider);
}
#Override
public void serialize(EntityInterface value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeNumberField("id", (long) value.getId());
jgen.writeStringField("label", value.getLabel());
jgen.writeEndObject();
}
}
In the student class, on univrsity add:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "university_id",updatable = false,insertable = false)
#JsonSerialize(using=StandardJsonSerializer.class)
private University university;
Now you have resolved circularity.
When you need a label, override the method in the parent entity.
When you need some particular fields, create a specific Serializer.
So, my problem goes like this: I created a RESTfull web service in Java using Jersey. Also, to map and use the MySQL database, I used hibernate (jpa).
I have a POJO named "horarios". Also, a POJO named "turmas". They are refered to each other using a #ManyToMany notaion in Hibernate. They go as this:
Turmas.java
#Entity
public class Turmas {
#Id
#GeneratedValue
#Column(name = "turmas_id", unique = true, nullable = false)
int Id;
#Column
String codigo;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "turmas_horarios", joinColumns = {
#JoinColumn(name = "turmas_id", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "horarios_id",nullable = false, updatable = false) })
private List<horarios> Horarios;
}
//... getters and setters
Horarios.java
#Entity
public class horarios {
#Id
#GeneratedValue
#Column(name = "horarios_id", nullable = false, unique = true)
int Id;
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "Horarios")
private List<Turmas> turmas;
#Column
private int horario;
#Column
private int dia;
//...getters and setters
}
And here goes the HorariosController:
#Path("/horarios")
public class HorariosController {
#Path("/setHorarios")
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response setHorarios(horarios h) throws Exception
{
horarios h1 = new horarios();
h1.setDia(h.getDia());
h1.setHorario(h.getHorario());
h1.setTurmas(h.getTurmas());
Querys.Persist(h1);
return Response.status(200).build();
}
}
//...GET and DELETE methods
The problem goes like this: For every column, it is very easy to treat the json object. But I really can't do it using a many to many type.. here goes an example of a json I'm sending:
{"dia": 6,"horario": 15,"turmas": {"codigo":"xxxx"}}
Which is valid according to http://www.jsonlint.com/. Though, when I try the POST method, it gives me the return:
Can not deserialize instance of java.util.ArrayList out of START_OBJECT token
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#1b23d8c; line: 3, column: 15] (through reference chain: modules.horarios["turmas"])
And for that reason I can't extract the value I put in the Turmas' fields.
The final question is: How (and what do I have to change) to read the JSON file and put in the Join Column the relationship between a passed Turmas and Horarios?
Like how can I read the turmas' passed Id (for example) without getting that error?
you have to change your json like below. As per your horaio entity definition your horario should have list of turmas.
{"dia": 6,"horario": 15,"turmas": [{"codigo":"xxxx"}]}
I'm struggling with Hibernate entities and JSON in these days and, although there is a lot of questions regarding the object, I'm yet not capable to serialize in presence of circular dependencies. I tried with both Gson and jackson but I didn't get a lot of progresses.
Here is an excerpt from my objects.
This is the "parent" class.
#Entity
public class User extends RecognizedServerEntities implements java.io.Serializable
{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval = false)
#Cascade({CascadeType.SAVE_UPDATE})
private Set<Thread> threads = new HashSet<Thread>(0);
//...other attributes, getters and setters
}
and this is the "children" class
#Entity
#Table(name = "thread")
public class Thread extends RecognizedServerEntities implements java.io.Serializable
{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "author", nullable = true)
private User user;
//...other attributes, getters and setters
}
I've written a simple class to test both gson and jackson features; as said, they both raise an exception.
public class MyJsonsTest
{
private static User u;
public static void main(String[] args)
{
u = new User("mail", "password", "nickname", new Date());
u.setId(1); // Added with EDIT 1
// testGson();
testJackson();
}
private static void testJackson()
{
Thread t = new Thread("Test", u, new Date(), new Date());
t.setId(1); // Added with EDIT 1
u.getThreads().add(t);
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
try
{
mapper.writeValue(new File("result.json"), u);
}
catch {/[various exceptions catched, but a JsonMappingException was thrown]}
}
private static void testGson()
{
Gson gson = new Gson();
System.out.println(u.toString());
System.out.println(gson.toJson(u, User.class));
Thread t = new Thread("Test", u, new Date(), new Date());
u.getThreads().add(t);
//This raise an exception overflow
System.out.println(gson.toJson(u, User.class));
}
}
To solve the problem, on jackson side, I tried to use this annotation
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
on both User and Thread class. However, it doesn't solve the problem.
On gson side, I read about the GraphAdapterBuilder class, but I wasn't able to properly use it. I don't find any jar, so I copy/pasted the source code from here. However, there is a compile time error at this line
private final ConstructorConstructor constructorConstructor = new ConstructorConstructor();
because the ConstructorConstructor() is undefined; the right syntax should be
ConstructorConstructor(Map<Type>, InstanceCreator<?> instanceCreators)
So, is there a definitive solution to this problem? Obviously, I can't use transient variables.
EDIT 1
I finally found the issue with jackson. In the test class, I forgot to initialize the id field (in real scenarios it is initialized by the database) and this is the reason of the exception. When I finally set the id, all works. This is the output
{
"id" : 1,
"email" : "mail",
"password" : "password",
"nick" : "nickname",
"registeredDate" : 1414703168409,
"threads" : [ {
"id" : 1,
"thread" : null,
"user" : 1,
"title" : "Test",
"lastModifiedDate" : 1414703168410,
"createdDate" : 1414703168410,
"messages" : [ ],
"threads" : [ ]
} ],
"messages" : [ ]
}
When dealing with circular dependencies you need to build a parent-children JSON hierarchy, because the marshalling must be cascaded from root to the inner-most child.
For bi-directional associations, when the Parent has a one-to-many children collection and the child has a many-to-one reference to Parent, you need to annotate the many-to-one side with #JsonIgnore:
#Entity
#Table(name = "thread")
public class Thread extends RecognizedServerEntities implements java.io.Serializable
{
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Integer id;
#org.codehaus.jackson.annotate.JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "author", nullable = true)
private User user;
//...other attributes, getters and setters
}
This way you will no longer have a Json serializing-time circular dependency.
Jackson
As said, I was able to solve the problem using
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id", scope=MyEntity.class)`
for each entity as suggested here.
The scope attribute was necessary to make sure that the name "id" is unique within the scope. Actually, without the scope attribute, as you can see here, it throws an exception saying
com.fasterxml.jackson.databind.JsonMappingException: Already had POJO for id java.lang.String) [com.fasterxml.jackson.annotation.ObjectIdGenerator$IdKey#3372bb3f] (through reference chain: ParentEntity["children"]->java.util.ArrayList[0]->ChildEntity["id"])
...stacktrace...
Caused by: java.lang.IllegalStateException: Already had POJO for id (java.lang.String) [com.fasterxml.jackson.annotation.ObjectIdGenerator$IdKey#3372bb3f]
...stacktrace...
Gson
I still haven't found a clean way to serialize circular dependencies.
I have done this using org.codehaus.jackson.annotate.JsonManagedReference and org.codehaus.jackson.annotate.JsonBackReference in this way...
look at how i used #JsonManagedReference
#Id
#TableGenerator(name="CatId", table="catTable",pkColumnName="catKey",pkColumnValue="catValue", allocationSize=1)
#GeneratedValue(strategy=GenerationType.TABLE, generator="CatId")
#Column(name = "CategId", unique = true, nullable = false)
private long categoryId;
private String productCategory;
#JsonManagedReference("product-category")
#OneToMany(targetEntity=ProductDatabase.class,mappedBy="category", cascade=CascadeType.ALL, fetch=FetchType.EAGER)
private List<ProductDatabase> catProducts;
and then at the other end i used #JsonBackReference as shown below.
#Id#GeneratedValue
private int productId;
private String description;
private int productPrice;
private String productName;
private String ProductImageURL;
#JsonBackReference("product-category")
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "CategId")
private Category category;
just apply these annotations and check if it works for you.
Its not good design to serialize Hibernate POJO to client. As you may send some data to client location, which he is not authorize to view. You should create client POJO and copy data from hibernate POJO to client POJO, which you want to send to client. If you don't want to do that, you can use #JsonIgnore or Fetch all data eagerly.