Could not write content: failed to lazily initialize a collection of role - json
I have One-To-Many relationship, here is my code
#Entity
#Table(name = "catalog")
public class Catalog {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "catalog_id")
private int catalog_id;
#NotEmpty
#Size(min = 3, max = 255)
#Column(name = "name", nullable = false)
private String name;
#OneToMany(mappedBy="mycatalogorder")
private List<Order> orders;
#OneToMany(mappedBy="mycatalog")
private List<CatalogItem> items;
// setters and getters
}
#Entity
#Table(name = "catalogitem")
public class CatalogItem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "catalogitem_id")
private int catalogitem_id;
#NotEmpty
#Size(min = 3, max = 255)
#Column(name = "name", nullable = false)
private String name;
#NotEmpty
#Column(name = "price", nullable = false)
private Double price;
#OneToOne(mappedBy="ordercatalogitem", cascade=CascadeType.ALL)
private OrderItem morderitem;
#ManyToOne
#JoinColumn(name="catalog_id", nullable=false)
private Catalog mycatalog;
// setters and getters
}
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "order_id")
private int order_id;
#NotEmpty
#Size(min = 3, max = 255)
#Column(name = "name", nullable = false)
private String name;
#NotEmpty
#Size(min = 3, max = 1024)
#Column(name = "note", nullable = false)
private String note;
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(pattern = "ddmmYYYY HH:mm:ss")
#Column(name = "created", nullable = false)
private Date created;
#OneToMany(mappedBy="myorder")
private Set<OrderItem> orderItems;
#ManyToOne
#JoinColumn(name="catalog_id", nullable=false)
private Catalog mycatalogorder;
#PrePersist
protected void onCreate() {
created = new Date();
}
// setters and getters
}
#Entity
#Table(name = "orderitem")
public class OrderItem {
#Id
#Column(name="catalogitem_id", unique=true, nullable=false)
#GeneratedValue(generator="gen")
#GenericGenerator(name="gen", strategy="foreign", parameters=#Parameter(name="property", value="catalogitem"))
private int catalogitem_id;
#Column(name = "quantity")
private int quantity;
#OneToOne
#PrimaryKeyJoinColumn
private CatalogItem ordercatalogitem;
#ManyToOne
#JoinColumn(name="order_id", nullable=false)
private Order myorder;
// setters and getters
}
And I am getting the exception:
org.springframework.http.converter.HttpMessageNotWritableException:
Could not write content: failed to lazily initialize a collection of
role: com.example.helios.model.Catalog.items, could not initialize
proxy - no Session; nested exception is
com.fasterxml.jackson.databind.JsonMappingException: failed to lazily
initialize a collection of role:
com.example.helios.model.Catalog.items, could not initialize proxy -
no Session
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:271)
org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:100)
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:222)
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:183)
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:80)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:126)
My versions is:
SpringFramework 4.2.4.RELEASE
Hibernate 4.3.11.Final
Jackson 2.7.4
Jacksontype 2.7.1
This is the normal Hibernate behaviour
In one to many relations, hibernate loads the father entity (Catalog in your case) but it will load the children entities List (List items and List orders in your case) in a LAZY mode
This means you can't access to these objects because they are just proxies and not real objects
This is usefull in order to avoid to load the full DB when you execute a query
You have 2 solution:
Load children entities in EAGER mode (I strongly suggest to you to not do it because you can load the full DB.... but it is something related to your scenario
You don't serialize in your JSON the children entities by using the com.fasterxml.jackson.annotation.JsonIgnore property
Angelo
A third option which can be useful if you don't want to use EAGER mode and load up everything is to use Hibernate::initialize and only load what you need.
Session session = sessionFactory.openSession();
Catalog catalog = (Catalog) session.load(Catalog.class, catalogId);
Hibernate.initialize(shelf);
More information
I had the same problem but a fixed by:
#OneToMany
#JoinColumn(name = "assigned_ingredient", referencedColumnName = "ingredient_id")
#Fetch(FetchMode.JOIN) // Changing the fetch profile you can solve the problem
#Where(clause = "active_ind = 'Y'")
#OrderBy(clause = "meal_id ASC")
private List<Well> ingredients;
you can have more information here: https://vladmihalcea.com/the-best-way-to-handle-the-lazyinitializationexception/
It's caused by an infinite loop when parsing datas to JSON.
You can solve this by using #JsonManagedReference and #JsonBackReference annotations.
Definitions from API :
JsonManagedReference (https://fasterxml.github.io/jackson-annotations/javadoc/2.5/com/fasterxml/jackson/annotation/JsonManagedReference.html) :
Annotation used to indicate that annotated property is part of two-way
linkage between fields; and that its role is "parent" (or "forward")
link. Value type (class) of property must have a single compatible
property annotated with JsonBackReference. Linkage is handled such
that the property annotated with this annotation is handled normally
(serialized normally, no special handling for deserialization); it is
the matching back reference that requires special handling
JsonBackReference: (https://fasterxml.github.io/jackson-annotations/javadoc/2.5/com/fasterxml/jackson/annotation/JsonBackReference.html):
Annotation used to indicate that associated property is part of
two-way linkage between fields; and that its role is "child" (or
"back") link. Value type of the property must be a bean: it can not be
a Collection, Map, Array or enumeration. Linkage is handled such that
the property annotated with this annotation is not serialized; and
during deserialization, its value is set to instance that has the
"managed" (forward) link.
Example:
Owner.java:
#JsonManagedReference
#OneToMany(mappedBy = "owner", fetch = FetchType.EAGER)
Set<Car> cars;
Car.java:
#JsonBackReference
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "owner_id")
private Owner owner;
Another solution is to use #JsonIgnore which will just set null to the field.
Here is my solution for this task with Hibernate. I marked hibernate releation with #JsonIgnore and use custom field for jackson, in which I check if the field is loaded. If you need serialize collection to json then you should manualy call collection getter during hibernate transaciton.
#JsonIgnore
#OneToMany(mappedBy = "myorder")
private List<OrderItem> orderItems = new ArrayList<>();
#JsonProperty(value = "order_items", access = JsonProperty.Access.READ_ONLY)
private List<OrderItem> getOrderItemsList() {
if(Hibernate.isInitialized(this.relatedDictionary)){
return this.relatedDictionary;
} else{
return new ArrayList<>();
}
}
#JsonProperty(value = "order_items", access = JsonProperty.Access.WRITE_ONLY)
private void setOrderItemsList(List<OrderItem> orderItems) {
this.orderItems = orderItems;
}
I know this is an old post but this might still help someone facing a similar issue. To solve the problem, iterate through the list of items and set the lazy-loadable collection to null. Then set your mapper to include NON-NULL
for (Catalog c : allCatalogs) {
c.setItems(null);
}
objectMapper.setSerializationInclusion(Include.NON_NULL)
Using FetchType.LAZY , if still getting the error "Could not write content: failed to lazily initialize a collection of role" , that may be probably caused by somewhere in the logic (perhaps in a controller) , Catalog is being tried to be deserialized that contains list of catalog items which is a proxy but the transaction has already ended to get that.
So create a new model ('CatalogResource' similar to catalog but without the list of items).
Then create a catalogResource object out of the Catalog (which is returned from the query)
public class CatalogResource {
private int catalog_id;
private String name;
private List<Order> orders;
}
I think the best solution to your problem (which also is the simplest) is to set your FetchType to LAZY and simply annotate the oneToMany collection fields using #transient.
Setting FetchType to EAGER isn't a good idea most times.
Best of luck.
"You don't serialize in your JSON the children entities by using the com.fasterxml.jackson.annotation.JsonIgnore property"
Add #JsonIgnore for hibernate lazy loading properties eg. #ManyToOne. That should work
Related
Spring boot: How to Save data to specific DTYPE
I have this entity: public class StatementLinesEntity { #Id #GeneratedValue(strategy = GenerationType.IDENTITY) private Long statementLinesId; #CreationTimestamp #Temporal(TemporalType.DATE) private Date dateOperation; private String operationNature; private BigDecimal amount; private String debitAmount; And this entity has Inheritance of type SINGLE_TABLE: #Inheritance(strategy = InheritanceType.SINGLE_TABLE) public class OperationCreditEntity { #Id #Column(nullable = false, updatable = false) #GeneratedValue(strategy = GenerationType.AUTO) private Long operationCreditId; #CreatedDate private Date operationDate; #OneToOne private StatementLinesEntity statementLine; And these 3 enteties inherite of it : #Entity #DiscriminatorValue("Espece") public class OperationEspecesEntity extends OperationCreditEntity { private String cin; private String nomEmetteur; private String prenomEmetteur; ============================= #DiscriminatorValue("Virement") public class OperationVirementEntity extends OperationCreditEntity { private String rib; =========================== #Entity #DiscriminatorValue("Cheque") public class OperationChequeEntity extends OperationCreditEntity{ private int numeroCheque; Let's suppose I have a List<StatementLinesEntity> consist of 2 lines, on line has debitAmount = C and operationNature = Virement and second line has debitAmount = C and operationNature = Espece. My goal is to persist each line in a specific DTYPE. example first line should be persisted in OperationCreditEntity table DTYPE = Virement and the second should be persisted in OperationCreditEntity table DTYPE = Espece
The model to me should be more like: #Entity public class StatementLinesEntity { #Id #GeneratedValue(strategy = GenerationType.IDENTITY) private Long statementLinesId; #CreationTimestamp #Temporal(TemporalType.DATE) private Date dateOperation; #OneToOne(mappedBy = "statementLine") private OperationCreditEntity operation; private BigDecimal amount; private String debitAmount; } #Inheritance(strategy = InheritanceType.SINGLE_TABLE) abstract public class OperationCreditEntity { #Id #GeneratedValue(strategy = GenerationType.AUTO) private Long operationCreditId; #CreatedDate private Date operationDate; #OneToOne private StatementLinesEntity statementLine; } Any method then that takes in StatementLinesEntity instances can then take in one that references an OperationCreditEntity instance (which can be any one of its subclasses). There is no need to manage, parse or handle String operationNature strings directly, as the operation type will determine the operation nature. This might change other signatures, serialization (such as JSON though), so if you can't use this and are 'stuck' with your existing StatementLinesEntity data representation YOU need to handle how to create your OperationCreditEntity instances from that data. There is no tool to automatically do it for you. It is as simple as a utility of the form: OperationCreditEntity createOperationInstance(StatementLinesEntity statementLine) { String operationNature = statementLine.getOperationNature(); OperationCreditEntity returnVal = null; if "Espece".equals(operationNature) { returnVal = new OperationEspecesEntity(); } else if "Virement".equals(operationNature) { returnVal = new OperationVirementEntity(); } else if "Cheque".equals(operationNature) { returnVal = new OperationChequeEntity(); } else { throw new IllegalStateException(); } returnVal.setStatementLine(statementLine); return returnVal; } Just call save using your OperationCreditEntity repository when ever you call this method to get it put into the same transactional context you are making changes to. Also note, those OperationCreditEntity subclasses have data you will need to find a way to fill in on your own; I personally think this data will likely be tied to data available when defining/creating a StatementLinesEntity, so should be generated/created then, not after the fact, but that is up to you. Added just to be complete: Yes, you can access the column used to store discriminator values directly in a base entity class. Nothing stops or prevents you from mapping the column as you would any other database column. For Hibernate, it uses "DTYPE", so #Inheritance(strategy = InheritanceType.SINGLE_TABLE) public class OperationCreditEntity { #Id #GeneratedValue(strategy = GenerationType.AUTO) private Long operationCreditId; #CreatedDate private Date operationDate; #Column(name="DTYPE",insertable=false, updatable=false) private String typeValue; } Notice I marked this as insertable/updatable=false though. It is provider specific if it complains about controlling this value in this way; many try to do so with the hope of changing it. Changing an entity 'type' is not supported. A Caterpillar does not become a Butterfly just by changing a string value. Any caches that hold OperationCreditEntity or some specific subclass type aren't magically going to have the object type changed; JPA requires you to delete the entity and create a new instance (of the proper class) for that data, preferably after flushing the delete operation. Also note, you can query and use Entity Type Expressions (TYPE) without having a column or other mapping for it. "Select line from OperationCreditEntity operation join operation.statementLine line where TYPE(operation) IN (OperationEspecesEntity, OperationChequeEntity) and line.somethingElse = :someValue"
Spring Boot infinite recursion [duplicate]
When trying to convert a JPA object that has a bi-directional association into JSON, I keep getting org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError) All I found is this thread which basically concludes with recommending to avoid bi-directional associations. Does anyone have an idea for a workaround for this spring bug? ------ EDIT 2010-07-24 16:26:22 ------- Codesnippets: Business Object 1: #Entity #Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})}) public class Trainee extends BusinessObject { #Id #GeneratedValue(strategy = GenerationType.TABLE) #Column(name = "id", nullable = false) private Integer id; #Column(name = "name", nullable = true) private String name; #Column(name = "surname", nullable = true) private String surname; #OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL) #Column(nullable = true) private Set<BodyStat> bodyStats; #OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL) #Column(nullable = true) private Set<Training> trainings; #OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL) #Column(nullable = true) private Set<ExerciseType> exerciseTypes; public Trainee() { super(); } //... getters/setters ... } Business Object 2: import javax.persistence.*; import java.util.Date; #Entity #Table(name = "ta_bodystat", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})}) public class BodyStat extends BusinessObject { #Id #GeneratedValue(strategy = GenerationType.TABLE) #Column(name = "id", nullable = false) private Integer id; #Column(name = "height", nullable = true) private Float height; #Column(name = "measuretime", nullable = false) #Temporal(TemporalType.TIMESTAMP) private Date measureTime; #ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) #JoinColumn(name="trainee_fk") private Trainee trainee; } Controller: import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletResponse; import javax.validation.ConstraintViolation; import java.util.*; import java.util.concurrent.ConcurrentHashMap; #Controller #RequestMapping(value = "/trainees") public class TraineesController { final Logger logger = LoggerFactory.getLogger(TraineesController.class); private Map<Long, Trainee> trainees = new ConcurrentHashMap<Long, Trainee>(); #Autowired private ITraineeDAO traineeDAO; /** * Return json repres. of all trainees */ #RequestMapping(value = "/getAllTrainees", method = RequestMethod.GET) #ResponseBody public Collection getAllTrainees() { Collection allTrainees = this.traineeDAO.getAll(); this.logger.debug("A total of " + allTrainees.size() + " trainees was read from db"); return allTrainees; } } JPA-implementation of the trainee DAO: #Repository #Transactional public class TraineeDAO implements ITraineeDAO { #PersistenceContext private EntityManager em; #Transactional public Trainee save(Trainee trainee) { em.persist(trainee); return trainee; } #Transactional(readOnly = true) public Collection getAll() { return (Collection) em.createQuery("SELECT t FROM Trainee t").getResultList(); } } persistence.xml <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <persistence-unit name="RDBMS" transaction-type="RESOURCE_LOCAL"> <exclude-unlisted-classes>false</exclude-unlisted-classes> <properties> <property name="hibernate.hbm2ddl.auto" value="validate"/> <property name="hibernate.archive.autodetection" value="class"/> <property name="dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/> <!-- <property name="dialect" value="org.hibernate.dialect.HSQLDialect"/> --> </properties> </persistence-unit> </persistence>
JsonIgnoreProperties [2017 Update]: You can now use JsonIgnoreProperties to suppress serialization of properties (during serialization), or ignore processing of JSON properties read (during deserialization). If this is not what you're looking for, please keep reading below. (Thanks to As Zammel AlaaEddine for pointing this out). JsonManagedReference and JsonBackReference Since Jackson 1.6 you can use two annotations to solve the infinite recursion problem without ignoring the getters/setters during serialization: #JsonManagedReference and #JsonBackReference. Explanation For Jackson to work well, one of the two sides of the relationship should not be serialized, in order to avoid the infite loop that causes your stackoverflow error. So, Jackson takes the forward part of the reference (your Set<BodyStat> bodyStats in Trainee class), and converts it in a json-like storage format; this is the so-called marshalling process. Then, Jackson looks for the back part of the reference (i.e. Trainee trainee in BodyStat class) and leaves it as it is, not serializing it. This part of the relationship will be re-constructed during the deserialization (unmarshalling) of the forward reference. You can change your code like this (I skip the useless parts): Business Object 1: #Entity #Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})}) public class Trainee extends BusinessObject { #OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL) #Column(nullable = true) #JsonManagedReference private Set<BodyStat> bodyStats; Business Object 2: #Entity #Table(name = "ta_bodystat", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})}) public class BodyStat extends BusinessObject { #ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) #JoinColumn(name="trainee_fk") #JsonBackReference private Trainee trainee; Now it all should work properly. If you want more informations, I wrote an article about Json and Jackson Stackoverflow issues on Keenformatics, my blog. EDIT: Another useful annotation you could check is #JsonIdentityInfo: using it, everytime Jackson serializes your object, it will add an ID (or another attribute of your choose) to it, so that it won't entirely "scan" it again everytime. This can be useful when you've got a chain loop between more interrelated objects (for example: Order -> OrderLine -> User -> Order and over again). In this case you've got to be careful, since you could need to read your object's attributes more than once (for example in a products list with more products that share the same seller), and this annotation prevents you to do so. I suggest to always take a look at firebug logs to check the Json response and see what's going on in your code. Sources: Keenformatics - How To Solve JSON infinite recursion Stackoverflow (my blog) Jackson References Personal experience
You may use #JsonIgnore to break the cycle (reference). You need to import org.codehaus.jackson.annotate.JsonIgnore (legacy versions) or com.fasterxml.jackson.annotation.JsonIgnore (current versions).
The new annotation #JsonIgnoreProperties resolves many of the issues with the other options. #Entity public class Material{ ... #JsonIgnoreProperties("costMaterials") private List<Supplier> costSuppliers = new ArrayList<>(); ... } #Entity public class Supplier{ ... #JsonIgnoreProperties("costSuppliers") private List<Material> costMaterials = new ArrayList<>(); .... } Check it out here. It works just like in the documentation: http://springquay.blogspot.com/2016/01/new-approach-to-solve-json-recursive.html
Also, using Jackson 2.0+ you can use #JsonIdentityInfo. This worked much better for my hibernate classes than #JsonBackReference and #JsonManagedReference, which had problems for me and did not solve the issue. Just add something like: #Entity #Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})}) #JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#traineeId") public class Trainee extends BusinessObject { #Entity #Table(name = "ta_bodystat", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})}) #JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#bodyStatId") public class BodyStat extends BusinessObject { and it should work.
Also, Jackson 1.6 has support for handling bi-directional references... which seems like what you are looking for (this blog entry also mentions the feature) And as of July 2011, there is also "jackson-module-hibernate" which might help in some aspects of dealing with Hibernate objects, although not necessarily this particular one (which does require annotations).
This worked perfectly fine for me. Add the annotation #JsonIgnore on the child class where you mention the reference to the parent class. #ManyToOne #JoinColumn(name = "ID", nullable = false, updatable = false) #JsonIgnore private Member member;
Now Jackson supports avoiding cycles without ignoring the fields: Jackson - serialization of entities with birectional relationships (avoiding cycles)
Working fine for me Resolve Json Infinite Recursion problem when working with Jackson This is what I have done in oneToMany and ManyToOne Mapping #ManyToOne #JoinColumn(name="Key") #JsonBackReference private LgcyIsp Key; #OneToMany(mappedBy="LgcyIsp ") #JsonManagedReference private List<Safety> safety;
For me the best solution is to use #JsonView and create specific filters for each scenario. You could also use #JsonManagedReference and #JsonBackReference, however it is a hardcoded solution to only one situation, where the owner always references the owning side, and never the opposite. If you have another serialization scenario where you need to re-annotate the attribute differently, you will not be able to. Problem Lets use two classes, Company and Employee where you have a cyclic dependency between them: public class Company { private Employee employee; public Company(Employee employee) { this.employee = employee; } public Employee getEmployee() { return employee; } } public class Employee { private Company company; public Company getCompany() { return company; } public void setCompany(Company company) { this.company = company; } } And the test class that tries to serialize using ObjectMapper (Spring Boot): #SpringBootTest #RunWith(SpringRunner.class) #Transactional public class CompanyTest { #Autowired public ObjectMapper mapper; #Test public void shouldSaveCompany() throws JsonProcessingException { Employee employee = new Employee(); Company company = new Company(employee); employee.setCompany(company); String jsonCompany = mapper.writeValueAsString(company); System.out.println(jsonCompany); assertTrue(true); } } If you run this code, you'll get the: org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError) Solution Using `#JsonView` #JsonView enables you to use filters and choose what fields should be included while serializing the objects. A filter is just a class reference used as a identifier. So let's first create the filters: public class Filter { public static interface EmployeeData {}; public static interface CompanyData extends EmployeeData {}; } Remember, the filters are dummy classes, just used for specifying the fields with the #JsonView annotation, so you can create as many as you want and need. Let's see it in action, but first we need to annotate our Company class: public class Company { #JsonView(Filter.CompanyData.class) private Employee employee; public Company(Employee employee) { this.employee = employee; } public Employee getEmployee() { return employee; } } and change the Test in order for the serializer to use the View: #SpringBootTest #RunWith(SpringRunner.class) #Transactional public class CompanyTest { #Autowired public ObjectMapper mapper; #Test public void shouldSaveCompany() throws JsonProcessingException { Employee employee = new Employee(); Company company = new Company(employee); employee.setCompany(company); ObjectWriter writter = mapper.writerWithView(Filter.CompanyData.class); String jsonCompany = writter.writeValueAsString(company); System.out.println(jsonCompany); assertTrue(true); } } Now if you run this code, the Infinite Recursion problem is solved, because you have explicitly said that you just want to serialize the attributes that were annotated with #JsonView(Filter.CompanyData.class). When it reaches the back reference for company in the Employee, it checks that it's not annotated and ignore the serialization. You also have a powerful and flexible solution to choose which data you want to send through your REST APIs. With Spring you can annotate your REST Controllers methods with the desired #JsonView filter and the serialization is applied transparently to the returning object. Here are the imports used in case you need to check: import static org.junit.Assert.assertTrue; import javax.transaction.Transactional; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.annotation.JsonView;
There's now a Jackson module (for Jackson 2) specifically designed to handle Hibernate lazy initialization problems when serializing. https://github.com/FasterXML/jackson-datatype-hibernate Just add the dependency (note there are different dependencies for Hibernate 3 and Hibernate 4): <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-hibernate4</artifactId> <version>2.4.0</version> </dependency> and then register the module when intializing Jackson's ObjectMapper: ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new Hibernate4Module()); Documentation currently isn't great. See the Hibernate4Module code for available options.
#JsonIgnoreProperties is the answer. Use something like this :: #OneToMany(mappedBy = "course",fetch=FetchType.EAGER) #JsonIgnoreProperties("course") private Set<Student> students;
You Should use #JsonBackReference with #ManyToOne entity and #JsonManagedReference with #onetomany containing entity classes. #OneToMany( mappedBy = "queue_group",fetch = FetchType.LAZY, cascade = CascadeType.ALL ) #JsonManagedReference private Set<Queue> queues; #ManyToOne(cascade=CascadeType.ALL) #JoinColumn(name = "qid") // #JsonIgnore #JsonBackReference private Queue_group queue_group;
In my case it was enough to change relation from: #OneToMany(mappedBy = "county") private List<Town> towns; to: #OneToMany private List<Town> towns; another relation stayed as it was: #ManyToOne #JoinColumn(name = "county_id") private County county;
I also met the same problem. I used #JsonIdentityInfo's ObjectIdGenerators.PropertyGenerator.class generator type. That's my solution: #Entity #Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})}) #JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") public class Trainee extends BusinessObject { ...
VERY IMPORTANT: If you are using LOMBOK, make shure to exclude attributes of collections like Set, List, etc... Like this: #EqualsAndHashCode(exclude = {"attributeOfTypeList", "attributeOfTypeSet"})
Be sure you use com.fasterxml.jackson everywhere. I spent much time to find it out. <properties> <fasterxml.jackson.version>2.9.2</fasterxml.jackson.version> </properties> <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>${fasterxml.jackson.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${fasterxml.jackson.version}</version> </dependency> Then use #JsonManagedReference and #JsonBackReference. Finally, you can serialize your model to JSON: import com.fasterxml.jackson.databind.ObjectMapper; ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(model);
You can use #JsonIgnore, but this will ignore the json data which can be accessed because of the Foreign Key relationship. Therefore if you reqiure the foreign key data (most of the time we require), then #JsonIgnore will not help you. In such situation please follow the below solution. you are getting Infinite recursion, because of the BodyStat class again referring the Trainee object BodyStat #ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) #JoinColumn(name="trainee_fk") private Trainee trainee; Trainee #OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL) #Column(nullable = true) private Set<BodyStat> bodyStats; Therefore, you have to comment/omit the above part in Trainee
I have the same problem after doing more analysis i came to know that, we can get mapped entity also by just keeping #JsonBackReference at OneToMany annotation #Entity #Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})}) public class Trainee extends BusinessObject { #Id #GeneratedValue(strategy = GenerationType.TABLE) #Column(name = "id", nullable = false) private Integer id; #Column(name = "name", nullable = true) private String name; #Column(name = "surname", nullable = true) private String surname; #OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL) #Column(nullable = true) #JsonBackReference private Set<BodyStat> bodyStats;
you can use DTO pattern create class TraineeDTO without any anotation hiberbnate and you can use jackson mapper to convert Trainee to TraineeDTO and bingo the error message disapeare :)
If you cannot ignore the property, try modifying the visibility of the field. In our case, we had old code still submitting entities with the relationship, so in my case, this was the fix: #JsonProperty(access = JsonProperty.Access.WRITE_ONLY) private Trainee trainee;
For some reason, in my case, it wasn't working with Set. I had to change it to List and use #JsonIgnore and #ToString.Exclude to get it working. Replace Set with List: //before #OneToMany(mappedBy="client") private Set<address> addressess; //after #OneToMany(mappedBy="client") private List<address> addressess; And add #JsonIgnore and #ToString.Exclude annotations: #ManyToOne #JoinColumn(name="client_id", nullable = false) #JsonIgnore #ToString.Exclude private Client client;
If you use #JsonManagedReference, #JsonBackReference or #JsonIgnore annotation it ignore some fields and solve Infinite Recursion with Jackson JSON. But if you use #JsonIdentityInfo which also avoid the Infinite Recursion and you can get all the fields values, so I suggest that you use #JsonIdentityInfo annotation. #JsonIdentityInfo(generator= ObjectIdGenerators.UUIDGenerator.class, property="#id") Refer this article https://www.toptal.com/javascript/bidirectional-relationship-in-json to get good understanding about #JsonIdentityInfo annotation.
This post: https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion has a full explanation. If you are using Jackson with older versions, you can try #jsonmanagedreference + #jsonbackreference. If your Jackson is above 2 (1.9 also doesn't work as I know), try #JsonIdentityInfo instead.
As someone using Spring Data and Lombok, this is how I solved it for myself. #Entity #Data public class Foo extends BaseEntity { #OneToMany(fetch = FetchType.EAGER) #JoinColumn(name = "foo_id") #JsonIgnoreProperties("parent_foo") #EqualsAndHashCode.Exclude private Set<Bar> linkedBars; } #Entity #Data public class Bar extends BaseEntity { #Column(name = "foo_id") private Long parentFooId; #ManyToOne(fetch = FetchType.EAGER) #JoinColumn(name = "foo_id", insertable = false, updatable = false) #JsonIgnoreProperties({"linkedBars"}) private Foo parentFoo; } The JsonIgnoreProperties annotation stops infinite recursion as many answers have discussed above. #EqualsAndHashCode.Exclude prevents the StackOverflowError caused by hashCode and equals being called recursively. Using Set over List resolves the MultipleBagFetchException which occurs when you add multiple collection fields. You can also use #Fetch(value = FetchMode.SUBSELECT) to avoid the cartesian product, but I haven't tried it personally since my use case didn't need it. The explicit definition of parentFooId in Bar is to allow mapping Foo entities with Bars.
I had this problem, but I didn't want to use annotation in my entities, so I solved by creating a constructor for my class, this constructor must not have a reference back to the entities who references this entity. Let's say this scenario. public class A{ private int id; private String code; private String name; private List<B> bs; } public class B{ private int id; private String code; private String name; private A a; } If you try to send to the view the class B or A with #ResponseBody it may cause an infinite loop. You can write a constructor in your class and create a query with your entityManager like this. "select new A(id, code, name) from A" This is the class with the constructor. public class A{ private int id; private String code; private String name; private List<B> bs; public A(){ } public A(int id, String code, String name){ this.id = id; this.code = code; this.name = name; } } However, there are some constrictions about this solution, as you can see, in the constructor I did not make a reference to List bs this is because Hibernate does not allow it, at least in version 3.6.10.Final, so when I need to show both entities in a view I do the following. public A getAById(int id); //THE A id public List<B> getBsByAId(int idA); //the A id. The other problem with this solution, is that if you add or remove a property you must update your constructor and all your queries.
In case you are using Spring Data Rest, issue can be resolved by creating Repositories for every Entity involved in cyclical references.
I'm a late comer and it's such a long thread already. But I spent a couple of hours trying to figure this out too, and would like to give my case as another example. I tried both JsonIgnore, JsonIgnoreProperties and BackReference solutions, but strangely enough it was like they weren't picked up. I used Lombok and thought that maybe it interferes, since it creates constructors and overrides toString (saw toString in stackoverflowerror stack). Finally it wasn't Lombok's fault - I used automatic NetBeans generation of JPA entities from database tables, without giving it much thought - well, and one of the annotations that were added to the generated classes was #XmlRootElement. Once I removed it everything started working. Oh well.
The point is to place the #JsonIgnore in the setter method as follow. in my case. Township.java #Access(AccessType.PROPERTY) #OneToMany(fetch = FetchType.LAZY) #JoinColumn(name="townshipId", nullable=false ,insertable=false, updatable=false) public List<Village> getVillages() { return villages; } #JsonIgnore #Access(AccessType.PROPERTY) public void setVillages(List<Village> villages) { this.villages = villages; } Village.java #ManyToOne(fetch = FetchType.EAGER) #JoinColumn(name = "townshipId", insertable=false, updatable=false) Township township; #Column(name = "townshipId", nullable=false) Long townshipId;
I have faced same issue, add jsonbackref and jsonmanagedref and please make sure #override equals and hashCode methods , this definitely fix this issue.
Spring Data JPA #OneToMany infinite loop exception
OneToMany relationship causing infinite loop using Spring Data JPA with hibernate as provider The problem here is not the type of exception but the infinite loop that causes this exception I tried #JsonIgnoreProperties which gives me another error => 'Could not write JSON: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer' The post referencing the solution does not have a solution that adresses my problem. One says use #JsonManagedReference and #JsonBackReference that does stop the recursion but excludes the object (UserGroup in 'myUser' entity) from the result which I need when I want an object of 'myUser' entity. The other one says about overriding ToString method which I don't do. Another one explains why there is an infinite loop and suggest as solution to not do that way. I quote "Try to create DTO or Value Object (simple POJO) without cycles from returned model and then return it." And this one Difference between #JsonIgnore and #JsonBackReference, #JsonManagedReference explains the difference but doing so I will have the same problem as the first one 'myUser' entity #Entity public class MyUser { #Id #GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String firstName; private String lastName; private String email; private Integer age; //#JsonIgnoreProperties({"myUsers"}) #ManyToOne(fetch = FetchType.LAZY) #JoinColumn(name = "userGroupId") private UserGroup userGroup; 'UserGroup' entity #Entity public class UserGroup { #Id #GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private Integer groupOrder; #OneToMany ( mappedBy = "userGroup", cascade = CascadeType.ALL, orphanRemoval = true ) private List<MyUser> myUsers;
change the getUserGroup() method in your MyUser class as follows. #Entity public class MyUser { #Id #GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String firstName; private String lastName; private String email; private Integer age; //#JsonIgnoreProperties({"myUsers"}) #ManyToOne(fetch = FetchType.LAZY) #JoinColumn(name = "userGroupId") private UserGroup userGroup; public UserGroup getUserGroup() { userGroup.setMyUsers(null); return userGroup; } }
you need to add #JsonIgnore annotation at #OneToMany like this #JsonIgnore #OneToMany ( mappedBy = "userGroup", cascade = CascadeType.ALL, orphanRemoval = true ) private List<MyUser> myUsers;
I think I'm getting the point of your problem. You want to fetch MyUser including the userGroup data without the circular reference. Based from the solutions you enumerated, I suggest you should still use the #JsonBackReference and #JsonManagedReference to prevent recursion on your entities and for the solution on your problem, you can try to use a mapper (MapStruck) and map the userGroup details to a DTO during the retrieval of data from the service. DTOs: public class MyUserDto { private Long id; private String firstName; private String lastName; private String email; private Integer age; private UserGroupDto userGroupDto; } public class UserGroupDto { private Long id; private Integer groupOrder; } Mapper (MapStruck): #Mapper(componentModel = "spring") public interface MyUserMapper { MyUserMapper INSTANCE = Mappers.getMapper(MyUserMapper.class); UserGroupDto userGroupToDto(UserGroup userGroup); #Mapping(source = "myUser.userGroup", target = "userGroupDto") MyUserDto myUserToDto(MyUser myUser); } After retrieving the data from your repository, you may then call the myUserToDto method to map the entity to a DTO. This is just one way of solving your problem.
JsonView returning empty json objects
I am trying to implement a JsonView to selectively serialize fields from an entity but the json that is serialized has empty objects with no fields. Below is my code: ViewClass: public class AuditReportView { public interface Summary {} } Entity: #Entity #SequenceGenerator(name = "AUDIT_REPORT_SEQUENCE_GENERATOR", sequenceName = "EJB_AUDIT_REPORT_SEQ", initialValue = 1, allocationSize = 1) #Table(name = "DEVICE_AUDIT_REPORT") #Data public class AuditReport implements Serializable { private static final long serialVersionUID = 1246376778314918671L; #Id #GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "AUDIT_REPORT_SEQUENCE_GENERATOR") #Column(name = "ID", nullable = false) #JsonView(AuditReportView.Summary.class) private Long id; #Column(name = "DEVICE_ID", nullable = false) #JsonView(AuditReportView.Summary.class) private String deviceId; #Column(name = "REPORT_TIMESTAMP", nullable = false) #JsonView(AuditReportView.Summary.class) private Calendar reportTimestamp; #Column(name = "USER_ID", nullable = false) #JsonView(AuditReportView.Summary.class) private long userId; #Column(name = "USERNAME", nullable = false) #JsonView(AuditReportView.Summary.class) private String username; #Column(name = "START_DATE", nullable = false) #JsonView(AuditReportView.Summary.class) private Calendar startDate; #Column(name = "END_DATE", nullable = false) #JsonView(AuditReportView.Summary.class) private Calendar endDate; #OneToMany(mappedBy = "auditReport", fetch = FetchType.LAZY, orphanRemoval = true, cascade={CascadeType.ALL}) private Set<AuditEntry> auditEntries = new HashSet<AuditEntry>(); } Controller: #JsonView(AuditReportView.Summary.class) #RequestMapping(method = RequestMethod.GET, value = "auditReportSummary") public #ResponseBody ResponseEntity<?> getAuditReportSummary() { final List<AuditReport> auditReports = auditDAO.getAuditReportSummary(); return new ResponseEntity<>(auditReports, HttpStatus.OK); } Json from Postman: [ {}, {}, {} ] The database only has 3 results and when I debug it is definately pulling them out, it is just that no members are being serialized. I'm using Spring 4.3.7 and Jackson 2.8.7. Any ideas of what could be wrong or where to start debugging the issue? Thanks
You must create getters and setters methods for attributes. I did it and it worked.
I guess the issue is due to the #ResponceBody ResponseEntity<?> Please try with the following code : #JsonView(AuditReportView.Summary.class) #RequestMapping(method = RequestMethod.GET, value = "auditReportSummary" produces = MediaType.APPLICATION_JSON_VALUE) public List<AuditReport getAuditReportSummary() { final List<AuditReport> auditReports = auditDAO.getAuditReportSummary(); return auditReports; } I am not much sure about it, but you can try if it works..
Try adding a default constructor - ex: public AuditReport() {} The default constructor is generated by the java compiler if no custom constructor is specified in the code. However if a custom constructor is specified, the default constructor is no longer automatically added which can break serialization libraries / spring, etc.. BUT - you haven't specified a constructor - how could this be? One thing I noticed is that you're using Lombok - due to the Data annotation. Lombok can generate constructors for classes. So its possible one of the annotations or libraries you're using is adding a constructor, making the compiler skip generation of a default constructor, which may be breaking your serialization. So, I hope adding a default constructor works out for you.
Play 2/Ebean ManyToMany JSON Results
I'm using Play! 2.1.4 framework with Ebean/MySQL. The system basically keeps track of courses, and their requirements. Eg, if you want to take Advanced Art, you must first take regular Art. This is a ManyToMany Relationship, as I understand, but I could be wrong. Here's my model: #Entity public class Course { public static Model.Finder<Long,Course> find = new Model.Finder<Long,Course>(Long.class, Course.class); #Id private Long id; private String name; #ManyToMany(cascade = CascadeType.ALL) #JoinTable( name = "course_course", joinColumns = #JoinColumn(name = "course_id1"), inverseJoinColumns = #JoinColumn(name = "course_id2")) private Set<Course> courseRequirements = new HashSet<Course>(); private String availability; private Long category; #Lob private String description; #Lob private String otherRequirements; private Long bucketId; // Getters and setters... Here's how I return the JSON to my frontend: List<Course> courses = Ebean.find(Course.class).fetch("courseRequirements").findList(); JsonContext jsonContext = Ebean.createJsonContext(); return ok(jsonContext.toJsonString(courses)); However, in the "courseRequirements" key in the JSON, it opens up a new array with full course objects in it. I just want to get an array of the course IDs it requires, for example: courseRequirements: [3, 5] means that: this course requires you to take courses with ID 3 and 5 first. I rewrote my getter to this: public Set getCourseRequirements() { Set requiredCourseIds = new HashSet(); for(Course course : courseRequirements) { requiredCourseIds.add(course.getId()); } return requiredCourseIds; } because I thought that Ebean will pull that when trying to fill in the JSON key, but that's not the case. How can I change it so that it only returns an array of course IDs for the courseRequirements key, instead of full objects? Thanks!
Instead of toJsonString(Object o) you should use toJsonString(Object o, boolean pretty, JsonWriteOptions options). With JsonWriteOptions "You can explicitly state which properties to include in the JSON output for the root level and each path." http://www.avaje.org/static/javadoc/pub/com/avaje/ebean/text/json/JsonWriteOptions.html