I'm trying to build a simple application with Quarkus. Currently, I have two entity classes, which are related one-to-many:
#Entity
public class Person extends PanacheEntity {
public String name;
public LocalDate birthdate;
#OneToMany(mappedBy = "person")
public List<Address> addresses;
public static Person findByNameFirst(String name) {
return find("name", name).firstResult();
}
}
#Entity
public class Address extends PanacheEntity {
public String street;
...etc...
#ManyToOne
public Person person;
}
These are used by a simple REST webservice, which should store a Person to the database, select it again an return it:
#GET
#Path("storePerson")
#Produces(MediaType.APPLICATION_JSON)
#Transactional
public Person storePerson(
#QueryParam("name")String name,
#QueryParam("birthdate")String birthdate)
{
LocalDate birth = LocalDate.parse(birthdate, DateTimeFormatter.BASIC_ISO_DATE);
Person person = new Person(name, birth);
person.persistAndFlush();
Person p2 = Person.findByNameFirst(name);
return p2;
}
When calling the webservice the first time, the result is a JSON object with the stored data, which is as expected. When called again, an internal server error is thrown:
org.hibernate.LazyInitializationException: Unable to perform requested lazy initialization [Person.addresses] - no session and settings disallow loading outside the Session
As I understand, the error is thrown because the transaction only lasts until the storePerson method ends, but the conversion to JSON is happening outside of the method.
How can I prevent this error? I have read about the hibernate parameter "enable_lazy_load_no_trans" but it seems it is not supported in Quakus' application.properties.
The idea is to use a mapper framework such as MapStruct.
We don't recommend to directly expose your entities for 2 reasons:
the issue you have,
API management in the long run: you might have to change your model and not your API or the opposite.
There is an example here: https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-quarkus .
The Quarkus version used is a bit old but AFAICS it should still work with latest Quarkus.
You can make the error go away by using Hibernate.initialize(person.addresses), then the collection gets initialized before the transaction ends.
I'm using hibernate for my web service.
I'm able to list all the records, but unable to get just one.
The table contains:
ID (VARCHAR) VALUE(BIT)
celiac 1
rate 1
suggestions 0
The error shown is:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.pfc.restaurante.models.Device#id="xxxxxx"]
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:894)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
And the main code:
#JsonAutoDetect
#Entity
#Table(name = "SETTINGS")
public class Settings implements Serializable{
#Id
#Column(name="ID")
private String id;
#Column(name="VALUE", nullable=false)
private boolean value;
(...)
}
//////////////////7
#Controller
#RequestMapping("/settingsService")
public class SettingsServiceController {
#Autowired
SettingsService settingsService;
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public #ResponseBody Settings find(#PathVariable("id") String id){
return settingsService.find(id);
}
(...)
}
I've read around that it could be because DB incongruence with my entity (some nullable = true when it shouldn't), but I've checked it already and there is no such a thing.
Could someone lend me a hand?
Thanks in advance!
Your error refers to an entity named 'Device' but your code shows an entity 'Settings'. Are they the same?
I've seen this error only in 2 situations:
The main entity does not exist in the DB and Session.load() is used. Use Session.get() and check for null instead.
Broken relationships. Consider this: EntityA owns a relation to EntityB. EntityB is deleted while the FK in EntityA is left untouched. So, whenever HB tries to load the link A-B the error happens. This can happen when running a normal search or even when saving/refreshing EntityA (HB needs to refresh the link as well).
I am getting a list of entities and attempting to add more values to it and have them persist to the data base... I am running into some issues doing this... Here is what I have so far...
Provider prov = emf.find(Provider.class, new Long(ID));
This entity has a many to many relationship that I am trying to add to
List<Organization> orgList = new ArrayList<Organization>();
...
orgList = prov.getOrganizationList();
So I now have the list of entities associated with that entity.
I search for some entities to add and I place them in the orgList...
List<Organization> updatedListofOrgss = emf.createNamedQuery("getOrganizationByOrganizationIds").setParameter("organizationIds", AddThese).getResultList();
List<Organization> deleteListOfOrgs = emf.createNamedQuery("getOrganizationByOrganizationIds").setParameter("organizationIds", DeleteThese).getResultList();
orgList.addAll(updatedListofOrgss);
orgList.removeAll(deleteListOfOrgs);
As you can see I also have a list of delete nodes to remove.
I heard somewhere that you don't need to call persist on such an opperation and that JPA will persist automatically. Well, it doesn't seem to work that way. Can you persist this way, or will I have to go throught the link table entity, and add these values that way?
public class Provider implements Serializable {
#Id
#Column(name="RESOURCE_ID")
private long resourceId;
...
#ManyToMany(fetch=FetchType.EAGER)
#JoinTable(name="DIST_LIST_PERMISSION",
joinColumns=#JoinColumn(name="RESOURCE_ID"),
inverseJoinColumns=#JoinColumn(name="ORGANIZATION_ID"))
private List<Organization> organizationList;
...//getters and setters.
}
The link table that links together organizations and providers...
public class DistListPermission implements Serializable {
#Id
#Column(name="DIST_LIST_PERMISSION_ID")
private long distListPermissionId;
#Column(name="ORGANIZATION_ID")
private BigDecimal organizationId;
#Column(name="RESOURCE_ID")
private Long resourceId;
}
The problem is that you are missing a cascade specification on your #ManyToMany annotation. The default cascade type for #ManyToMany is no cascade types, so any changes to the collection are not persisted. You will also need to add an #ElementDependent annotation to ensure that any objects removed from the collection will be deleted from the database. So, you can change your Provider implementation as follows:
#ManyToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
#ElementDependent
#JoinTable(name="DIST_LIST_PERMISSION",
joinColumns=#JoinColumn(name="RESOURCE_ID"),
inverseJoinColumns=#JoinColumn(name="ORGANIZATION_ID"))
private List<Organization> organizationList;
Since your Provider class is managed, you should not need to merge the entity; the changes should take effect automatically when the transaction is committed.
I have bi-directional relationship like this...
Person.java
public class Person{
#JsonIgnore
#OneToMany(targetEntity=PersonOrganization.class, cascade=CascadeType.ALL,
fetch=FetchType.EAGER, mappedBy="person")
private Set<PeopleOrg> organization;
.....
}
PersonOrganization.java
public class PersonOrganization{
#JsonIgnore
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="PERSONID", nullable=false)
private Person person;
}
Even with #JsonIgnore annotation I am getting infinite recursion error when trying to retrieve Person records. I have tried new annotations in 1.6 version. #JsonBackReference and #JsonManagedReference. Even then I am getting infinite recursion..
With #JsonBackReference("person-organization") on Person and #JsonManagedReference("person-organization") on PersonOrganization
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.entity.Person["organization"]->org.hibernate.collection.PersistentSet[0]->com.entity.PersonOrganization["person"]->com.entity.Person["organization"]->org.hibernate.collection.PersistentSet[0]...
Even If I interchange the annotations, I am still getting this exception.. Please let me know if there is something wrong with the mappings or the way I am using JSON annotations. Thanks
I've run into this before. But after moving #JsonIgnore from private field to getter of the field, infinite recursion is gone. So my wild guess is that #JsonIgnore might no work on private field. However, javadoc or tutorial of Jackson Java JSON-processor do not mention about this, so I cannot be 100% sure. Just for your information.
The following link says you should annotate the method used by JSON tool to traverse the object graph, to instruct the it to ignore the traversal.
http://jackson.codehaus.org/1.0.1/javadoc/org/codehaus/jackson/annotate/JsonIgnore.html
In my case I have two objects related like this Product <-> ProductImage. So JSON parser went into an infinite loop with out #JsonIgnore annotation on the following to get methods
#JsonIgnore
public Product getImageOfProduct() {
return imageOfProduct;
}
in ProductImage and
#JsonIgnore
public Set<ProductImage> getProductImages() {
return productImages;
}
in Product.
With the annotation, things are working fine.
I know this question isn't specifically about Spring Data REST, but I ran into this exception in the context of Spring Data REST, and wanted to share what the problem was. I had a bidirectional relationship involving an entity with no repository. Creating the repository made the loop disappear.
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.
For more details refer https://stackoverflow.com/a/18288939/286588
Apparently since Jackson 1.6 you can use #JsonManagedReference and #JsonBackReference to effectively solve the infinite recursion problem.
I won't go into the details but this changing you classes to the below format should solve the problem.
public class Person{
#OneToMany(targetEntity=PersonOrganization.class, cascade=CascadeType.ALL, fetch=FetchType.EAGER, mappedBy="person")
#Column(nullable = true)
#JsonManagedReference
private Set<PeopleOrg> organization;
.....
}
public class PersonOrganization{
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="PERSONID")
#JsonBackReference
private Person person;
}
Basically Jackson converts Set<PeopleOrg> organization, the forward part of the reference a to json-like format using the marshalling process, it then looks for Person person, the back part of the reference and does not serialize it.
Credits - Kurt Bourbaki & More info - http://keenformatics.blogspot.co.ke/2013/08/how-to-solve-json-infinite-recursion.html
If A has B & B has A.
This is one to one relationship, but forming a circular relation.
In any of the class, use JustIgnore annotation.
class A
{
B b;
}
class B
{
#JsonIgnore
A a;
}
This applies for other relationships also like one to many.
This might be little old but you can add #JsonIgnore at class level with all properties it should ignore. e.g
#JsonIgnore("productImaes","...")
public class Product{ ...
}
Sometimes the member field may have inner reference to the same class type of itself, which could cause infinite recursion when toJson.
E.g.: you have a member field Klass a, while the class definition of that Klass is as below.
class Klass {
Klass mySibling;
public toString() {
return "something" + mySibling.whateverMethod;
}
}
Solution: refactor the member field, eliminate the inner reference.
This exception is because, your constructor field are not proper, please check your constructors properties once again in your classes, and check mapping give properly or not,
Keep the two Constructors, first is zero construction and second constructor is with fields and both should be contain the super
for me, i have tried #JsonIgnore, #JsonManagedReference/#JsonBackReference but nothing worked, till i read this Exception thrown ["hibernateLazyInitializer"] solution 1 and this Exception thrown ["hibernateLazyInitializer"] solution 2
solution 1 is to change from fetch.LAZY to fetch.EAGER, and solution 2 is using #JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}), and of course use #JsonIgnore in both solutions
Jackson works on Reflection by calling getters. I too had such a situation where I had a getter of the same Object inside its class. Jackson went into infinite recursion eating up stack by repeatedly calling its own getter. Removed getter, then it got fixed.
My Advice :
If you want to use jackson for conversion of an object, never keep getters which references the same object, like in case of singletons.
I have an entity which contains a OneToMany relationship, with eager fetching, with a second entity. This second entity has two OneToOne relationships, with eager fetching also, to a third and fourth class. The OneToOne relationships are unidirectional.
I am calling createQuery() from a DOM4J session sending in "from entity" as the HQL. In the return I get the second entity but it contains only the IDs of the third and fourth entities instead of the complete contents. To me it looks like those third and fourth entities are not being eagerly fetched. I can't reproduce the code exactly but here is the most relevant parts.
#Entity
public class Event extends EventParent {
#OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinColumn(name="eventId")
#org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<Pair> pairs=new HashSet<MarPair>();
}
#Entity
public class Pair extends PairParent {
#OneToOne(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Info info;
#OneToOne(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Results results;
}
#Entity
public class Info {
private String name;
private Date time;
}
#Entity
public class Results {
private String name;
private Date time;
}
Finally here is the code I am using for the query:
public void retrieve() {
String hqlQry = "from Event";
Session session = dom4JSessionFactory.getCurrentSession();
Session dom4jSession = session.getSession(EntityMode.DOM4J);
List results = dom4jSession.createQUery(hqlQuery).list();
}
As I mentioned, from this query I am getting back an integer for the value of info and results which is the key to the info and results table instead of the actual data being retrieved from the info and results table.
Relevant Information:
Spring 2.5.4
Hibernate 3.2.6
Hibernate Annotations 3.3.1.GA
dom4JSessionFactory is of type org.springframework.orm.hibernate3.LocalSessionFactoryBean
The entity "Event" is actually the 7th class down in a class hierarchy (don't know if this matters or not)
I did leave out a lot of information hoping that it was not necessary. If there is something else you would need to venture a guess as to why it isn't working, please let me know.
Turns out this is a bug with the Hibernate version we are using. What I had to do was to change embed-xml to true after Hibernate generated the HBM files. This was done using the "replace" ant function.