JPA Bidirectional Onetomany json endless loop - java

the program under this particular environment:
EJB3.0 + JPA + jersey Web Service
First Entity :
#Entity
#Table(name = "student_by_test_yao")
public class StudentTest implements Serializable {
#Id
#GeneratedValue
private Integer id;
private String name;
#ManyToOne
#JoinColumn(name = "class_id")
private ClassTest classes;
public StudentTest() {}
}
Second Entity:
#Entity
#Table(name = "class_by_test_yao")
public class ClassTest implements Serializable{
#Id
#GeneratedValue
private Integer id;
private String name;
#OneToMany(mappedBy = "classes",cascade = CascadeType.ALL, fetch=FetchType.EAGER)
private List<StudentTest> students;
public ClassTest() {}
}
When I get the ClassTest 's students list.
Exception is:
com.fasterxml.jackson.databind.JsonMappingException:
Infinite recursion (StackOverflowError)
If I change the fetch FetchType.LAZY the Exception is:
org.hibernate.LazyInitializationException:
failed to lazily initialize a collection of role:
cn.gomro.mid.core.biz.goods.test.ClassTest.students,
could not initialize proxy - no Session
How to resolve my problems?

#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "userId")
private User user;
it really worked. I just tried that on Bi-Directional #ManyToOne mapping. It fixed
com.fasterxml.jackson.databind.JsonMappingException:
Infinite recursion (StackOverflowError)

Try to add #JsonIgnore annotation to one of fields to avoid looping

For bidirectional relationships you can use these annotations:
#JsonManagedReference for the parent and #JsonBackReference for the child.
Also, this link might help:
Jackson – Bidirectional Relationships

Snippet must help you.
#JsonIgnore
#ManyToOne
#JoinColumn(name = "columnName", referencedColumnName = "id")
private Class class;

Related

Spring Boot: How to create similar another entity by using existing entity class

I'm developing one simple app where where I have an one entity class class Employee. And now I want to create/copy new similar entity called ActiveEmployees from existing Employee. I want to add functionality that If I hit the new api endpoint ->POST: http://locahost:8080/api/employee/active/john -> So, it should save existing Employee John Record in the new table active_employees with the all Table data.
#Entity
#Table(name="employee")
public class Employee{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column
#NotNull
private String firstNname;
#Column
#NotNull
private String lastNname;
#Column
#NotNull
private String department;
#JsonManagedReference
#OneToOne(fetch = FetchType.LAZY,
mappedBy = "employee",
cascade = CascadeType.ALL,
orphanRemoval = true)
ActiveEmployee activeEmployee;
... Constructor, getters and setters
}
#Entity
#Table(name="active_employees")
public class ActiveEmployees {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#JsonBackReference
#OneToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
#JoinColumn(name = "employee_id")
private Employee employee;
}
I think you should use inheritance mapping in hibernate instead having two table with same fields. There are multiple strategies. Check and use best one which fits your requirement.
Read the tutorial here https://www.javatpoint.com/hibernate-inheritance-mapping-tutorial
You can use inhertiance with #MappedSuperclass. But if I will design this application I will add boolean field "active" to Employee class.

JPA - 2 #ManyToMany relationships between 2 entities (one of them with extra Entity)

I am trying to solve JPA problem. I have 2 main entities - CameraItem and Chain (which represents ordered list of cameras)
Now there have to be 2 #ManyToMany relationships between CameraItem and Chain.
Each CameraItem has at least one parent Chain. As one CameraItem can belong to different Chains, and each Chain can have multiple CameraItems this is the first simple direct #ManyToMany relationship.
Chains can be connected with each other via CameraItem. In other words, CameraItem is holding the connection between Chains. But this is not simple #ManyToMany relationship, because we also need information about direction of the Chains connection. So it is #ManyToMany relationship with new Entity as Baeldung describes here https://www.baeldung.com/jpa-many-to-many. Entity ConnectionPoint is holding the information about the direction as a String.
I paste the classes here:
CHAIN CLASS:
#Entity
#Table(name = "chain")
public class Chain {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#NotBlank(message = "Chain name is mandatory")
private String name;
#Column(name = "PLANT_NAME")
private String plantName;
private String description;
private String status;
private Boolean hasPlant;
#CreationTimestamp
#Column(name = "creation_time")
private LocalDateTime creationTime;
#OneToMany(mappedBy = "camera_item")
private List<CameraItem> cameraItems = new ArrayList<>();
#OneToMany(mappedBy = "chain")
Set<ConnectionPoint> connectionPoints;
CAMERA ITEM CLASS:
#Entity
#Table(name = "camera_item")
public class CameraItem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne
#JoinColumn
private Camera camera;
private String name;
private Integer positionInChain;
#ManyToMany(mappedBy = "cameraItems", fetch = FetchType.LAZY)
private List<Chain> parentChainIds;
#OneToMany(mappedBy = "cameraItem")
Set<ConnectionPoint> connectionPoints;
CONNECTION POINT CLASS:
#Entity
#Table(name = "connection_point")
public class ConnectionPoint {
#Id
private Long id;
#Column(name = "direction")
private String direction;
#ManyToOne
#JoinColumn(name = "chain")
private Chain chain;
#ManyToOne
#JoinColumn(name = "camera_item")
private CameraItem cameraItem;
When I run the application I get this error:
org.hibernate.AnnotationException: mappedBy reference an unknown
target entity property:
no.trafsys.videodashboard.model.entity.CameraItem.camera_item in
no.trafsys.videodashboard.model.entity.Chain.cameraItems
Does somebody know where the problem can be?
I use #OneToMany annotations in Chain and CameraItem entities and #ManyToOne in ConnectionPoint like Baeldung in his tutorial.
Thank you in advance for any help
I don't think there is issue in ConnectionPoint. I think the issue is that:
In Chain class,
#OneToMany(mappedBy = "camera_item") // One-to-Many defined here
private List<CameraItem> cameraItems = new ArrayList<>();
while in CameraItem class, corresponding property is defined as follow:
#ManyToMany(mappedBy = "cameraItems", fetch = FetchType.LAZY) // Many-To-Many
private List<Chain> parentChainIds;
Try changing the mapping type to #ManyToMany in Chain class as well. It might work.
PS: I am not entirely sure of this, but this feels like the issue[incorrect mapping type]. Wanted to add this as a comment, but due to space issues, adding it as an answer.
#Entity
#Table(name = "chain")
public class Chain {
//..
#OneToMany(mappedBy = "camera_item")
private List<CameraItem> cameraItems = new ArrayList<>();
//..
}
mappedBy parameter can only be in one side of the relation. I suspect camera_item is database table column name. So your cameraItems needs #JoinTable(name = "camera_item"... annotation

How to swap #JsonBackReference and #JsonManagedReference based on which Entity is reference

I'm trying to find a way to swap the #JsonBackRefence and the #JsonManagedReference based on what entity I reference from the associated repository.
Site.java
#Entity
#Table(name = "Site")
public class Site {
#Id
private String id;
#OneToMany(mappedBy="site")
#JsonManagedReference
private List<Building> buildings;
}
Building.java
#Entity
#Table(name = "building")
public class Building{
#Id
private String id;
#ManyToOne
#JoinColumn(name = "SITE_ID")
#JsonBackReference
private Site site;
}
SiteRepository.java
public List<Site> findAll(); //Works as intended
BuildingRepository.java
public Building findById(buildingId); //Works if references are swapped
However when calling findById(buildingId), I want to have the #JsonBackReference swapped. Therefore, the #JsonBackReference is in Site.java and the #JsonManagedReference is in the Building.java entity.
Note: #JsonIdentityInfo almosts handles it, but it gives me too much information ie: when I call findById(buildingId) from the BuildingRepository it gives me all the buildings for the site joined to the building found.
If I understand you correctly the #JsonIgnoreProperties annotation should help you:
#JsonIgnoreProperties("site")
#OneToMany(mappedBy="site")
private List<Building> buildings;
#JsonIgnoreProperties("buildings")
#ManyToOne
private Site site;

Problems with bidirectional relationship in hibernate

I have two entities: Sale and SaleProduct.
I am trying to persist the Sale entity, but I don't know how to make a service to it, because always that I try to save a Sale ir requires a SaleProduct.
This is the Sale entity:
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Sale {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
#NotNull(message = "Sale product!")
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "SALE_ID")
private List<SaleProduct> products;
#NotNull(message = "Sale needs some value!")
private int saleValue;
private Date creationDate = new Date();
}
And here is the SaleProduct entity:
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Data
#Builder
public class SaleProduct {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
private int quantity;
#NotNull(message = "Insert the product, please")
#ManyToOne(fetch = FetchType.EAGER)
private Product product;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Sale sale;
}
I am trying to persist the Sale with this method:
public void insertSale(Sale sale) throws Exception {
Product product = manager.find(Product.class, 2L);
SaleProduct saleProduct = new SaleProduct();
saleProduct.setProduct(product);
sale.setProducts(Arrays.asList(saleProduct));
saleProduct.setSale(sale);
manager.persist(sale);
}
I am sending this JSON:
{
"saleValue" : "200"
}
When I try to select the sale I created I get these errors:
Caused by: com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.tax.entity.Sale.products, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->com.tax.entity.Sale["products"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:210)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:177)
at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:190)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:674)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:156)
at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContentsUsing(CollectionSerializer.java:160)
at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:102)
at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:94)
at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:24)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:251)
at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:846)
at org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider.writeTo(ResteasyJackson2Provider.java:207)
at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.writeTo(AbstractWriterInterceptorContext.java:131)
at org.jboss.resteasy.core.interception.ServerWriterInterceptorContext.writeTo(ServerWriterInterceptorContext.java:60)
at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.proceed(AbstractWriterInterceptorContext.java:120)
at org.jboss.resteasy.security.doseta.DigitalSigningInterceptor.aroundWriteTo(DigitalSigningInterceptor.java:145)
at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.proceed(AbstractWriterInterceptorContext.java:124)
at org.jboss.resteasy.plugins.interceptors.encoding.GZIPEncodingInterceptor.aroundWriteTo(GZIPEncodingInterceptor.java:100)
at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.proceed(AbstractWriterInterceptorContext.java:124)
at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:98)
at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:466)
... 33 more
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.tax.entity.Sale.products, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:567)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:205)
at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:146)
at org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:261)
at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:88)
at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:24)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:575)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:666)
... 50 more
One more problem: when I saleProduct.setSale(sale); I get this exception: com.sun.jdi.InvocationException occurred invoking method
In order to accomplish bidirectional mapping , you got to have "mappedBy" property in OneToMany annotation
In the entity Sale use
#OneToMany(mappedBy="product", cascade=cascadeType.ALL, fetch=fetchType.LAZY)
The #JoinColumn annotation must be on the onwing side the relationship, that is, it should be on the #ManyToOne annotation:
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "SALE_ID")
private Sale sale;
The mappedBy attribute must be placed on the inverse side, that is where #OneToMany annotation is used:
#OneToMany(mappedBy="sale" cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<SaleProduct> products;
Create an instance of Sale, say sale.
Create an instance of SaleProduct, say saleProduct.
Add saleProduct into the list products. (I don't know why you named the list products even though it is containing SaleProducts).
Assign sale to saleProduct: saleProduct.setSale(sale). Now both entity instances are wired together.
And at last persist: entityManager.persist(sale);
As the exception says
No validator could be found for constraint 'javax.validation.constraints.Size' validating type 'com.tax.entity.Product'. Check configuration for 'product'
So remove #Size from the field "product" since it is not a collection field. You could put it on the other side of the relation "Sale.products"

Spring MVC Hibernate Many to Many Relationship get org.hibernate.exception.SQLGrammarException: Unknown column in 'on clause'

I have 3 class which is have a relation like this
#Entity
#Table(name="tbl_mhs")
public class ModelStudent {
#Id
#GeneratedValue
#Type(type="java.lang.Integer")
#Column(name="id_student")
private Integer id;
#ManyToMany(cascade = {CascadeType.ALL})
#LazyCollection(LazyCollectionOption.FALSE)
#JsonIgnore
#JoinTable(name="tbl_course_selected",
joinColumns={#JoinColumn(name="id_student")},
inverseJoinColumns={#JoinColumn(name="id_course")})
private List<ModelCourse> course;
and here the 2nd class
#Entity
#Table(name="tbl_makul")
public class ModelCourse {
#Id
#GeneratedValue
#Type(type="java.lang.Integer")
#Column(name="id_course")
private Integer id;
#ManyToMany(cascade = {CascadeType.ALL})
#LazyCollection(LazyCollectionOption.FALSE)
#JsonIgnore
#JoinTable(name="tbl_course_selectedl",
joinColumns={#JoinColumn(name="id_course")},
inverseJoinColumns={#JoinColumn(name="id_student")})
private List<ModelStudent> student;
and then the third class
#Entity
#Table(name="tbl_materi")
public class ModelBook {
#Id
#GeneratedValue
#Type(type="java.lang.Integer")
#Column(name="id_book")
private Integer id;
#ManyToOne
#JoinColumn(name="id_course")
private ModelCourse course;
What i want here is getting all of the book from course which is selected by student. So in the DAOImpl my code is like this
List<ModelBook> books = new ArrayList<ModelBook>();
Criteria criteria = getCurrentSession().createCriteria(ModelBook.class);
criteria.createAlias("course", "courseAlias");
criteria.createAlias("courseAlias.student", "studentAlias");
criteria.add(Restrictions.eq("studentAlias.id", student_id));
return criteria.list()
when I executed my application I got this exception
nested exception is org.hibernate.exception.SQLGrammarException: Unknown column 'student9_.id_student' in 'on clause'
What I'm doing wrong here?
Thank you for the answer, sorry for my bad english
Try below code for many-to-many. Also, use variable names instead of column names if you use HQL
#ManyToMany(mappedBy = "course", cascade = {CascadeType.ALL})
#LazyCollection(LazyCollectionOption.FALSE)
#JsonIgnore
private List<ModelStudent> student;

Categories