I have 2 POJO, Event and OrganizerProfile. Their relationship are many to one.
If I retrieve the Event from database, the OrganizerProfile is showing as empty from the debugger instead of what it should be. Also, I have to leave the hibernate session open to call the event.getOrganizerProfile.
If I get the Event, close the hibernate session, then the OrganizerProfile in the Event cannot be retrieved.
new EventDTO(this.getEvtByDateAddress(_event.getDate(), _event.getAddress(), /*dont close sess*/false));
Can you please explain this?
Thanks
<hibernate-mapping package="com.example.client.serializable">
<class name="Event" table="event">
<id name="oid" type="long" column="oid">
<generator class="increment">
<param name="initial_value">1</param>
</generator>
</id>
<property name="evtName">
<column name="evtName"/>
</property>
<property name="address">
<column name="address"/>
</property>
<property name="date" type="date">
<column name="date"/>
</property>
<many-to-one name="organizerProfile" cascade="all"></many-to-one>
</class>
</hibernate-mapping>
<hibernate-mapping package="com.example.client.serializable">
<class name="OrganizerProfile" table="organizerprofile">
<id column="oid" name="oid" type="long">
<generator class="increment">
<param name="initial_value">1</param>
</generator>
</id>
<property generated="never" lazy="false" name="acctOid">
<column name="acctOid"/>
</property>
<property generated="never" lazy="false" name="email">
<column name="email"/>
</property>
<property generated="never" lazy="false" name="name">
<column name="name"/>
</property>
<property generated="never" lazy="false" name="contact">
<column length="5120" name="contact"/>
</property>
<property name="profilePicName">
<column name="profilePicName"/>
</property>
</class>
</hibernate-mapping>
public Event getEvtByDateAddress(Date _date, String _address, boolean _closeSess)
{
try
{
if(!session.isOpen())
{
session = HibernateUtil.getSessionFactory().openSession();
}
session.beginTransaction();
Criteria criteria = session.createCriteria(Event.class);
criteria.add(Restrictions.eq("date", _date));
criteria.add(Restrictions.eq("address", _address));
Event evt = (Event)criteria.uniqueResult();
if(_closeSess)
{
session.close();
}
if (evt==null)
{
LogUtils.logInfo("The event does not exist: " + _date + " " + _address);
return null;
}
else
{
return evt;
}
}
catch(Exception e)
{
LogUtils.logInfo(e.toString());
if(_closeSess)
{
session.close();
}
return null;
}
}
public EventDTO(Event _event)
{
this.oid=_event.getOid();
this.evtName=_event.getEvtName();
this.address=_event.getAddress();
this.date=_event.getDate();
this.evtPicName=_event.getEvtPicName();
this.organizerProfile=new OrganizerProfileDTO(_event.getOrganizerProfile());
}
<many-to-one name="organizerProfile" cascade="all"></many-to-one>
Since you did not specify the property lazy in your many-to-one mapping, the associated entities will be proxied (see hibernate mapping documentation point 12) thus your related entity is not fetched and cannot be accessed outside of the session, inside of the session you can access it, because hibernate will automatically fetch it when you try to access it.
If you want to access the related entity outside of the session you have to fetch it manually, let hibernate initialize it - both within the session - or you can set your fetch type to eager ( <many-to-one name="organizerProfile" cascade="all" lazy="false"></many-to-one>) which I would NOT recommend (N+1 selects Problem).
Example of manual fetching with your code:
Criteria criteria = session.createCriteria(Event.class);
criteria.setFetchMode("organizerProfile", FetchMode.JOIN);
criteria.add(Restrictions.eq("date", _date));
criteria.add(Restrictions.eq("address", _address));
Also possibly helpful to read: Hibernate 4.3 docs #20: Performance fetching
Related
I'm getting an Exception^
Exception occurred in target VM: failed to lazily initialize a collection, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed
I do not understand why I have problems. I do not use lazy loading. This is my Role.hbm.xml:
<hibernate-mapping>
<class name="ru.itlect.bzdur.security.Role" table="Roles" >
<meta attribute="class-description">
</meta>
<id column="id" name="id" type="int">
<generator class="native"/>
</id>
<property column="displayName" name="displayName" not-null="true" type="string"/>
<property column="removed" name="removed" not-null="true" type="boolean"/>
<property column="hidden" name="hidden" not-null="true" type="boolean"/>
<property column="system" name="system" not-null="true" type="boolean"/>
<set name="permitionParams" table="Permitions" inverse="true" lazy="false" fetch="select" cascade="all" >
<key>
<column name="RoleID" not-null="true" />
</key>
<one-to-many class="ru.itlect.bzdur.security.PermitionParam" />
</set>
</class>
</hibernate-mapping>
The more strange the place where I loose Hibernate Session. Role.java:
public class Role {
private int id;
private Set<PermitionParam> permitionParams;
private final Map<String, PermitionParam> permitionMap = new HashMap<>();
...
public void setPermitionParams(Set<PermitionParam> permitionParams) {
permitionMap.clear();
if(permitionParams == null){
this.permitionParams = new HashSet<>();
return;
}
this.permitionParams = permitionParams;
for(PermitionParam param : permitionParams){
permitionMap.put(param.getPermition().getName(), param);
}
}
...
}
The Exception is thrown on the line: for(PermitionParam param : permitionParams){, whene I call save() methode of the CrudRepository. Hibernate creates the new copy of the Role object and calls its setters. I do not understand how I can loose the Session in that moment.
Please help me.
The fix is you need to initialize the lazy objects with in the transaction before passing to service layer. look here more here or here
I'm trying to achieve lazy load on the following.
User.java
public class User {
private int id;
private String userName;
private String password;
private Employee employee;
//getter and setters
}
User.hbm.xml
<hibernate-mapping>
<class name="com.site.dto.User" table="user">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="identity" />
</id>
<property name="userName" type="string" update="false">
<column name="user_name" length="50" not-null="true" unique="true" />
</property>
<property name="password" type="string">
<column name="password" length="50" not-null="true" unique="true" />
</property>
<one-to-one name="employee" class="com.site.dto.Employee" fetch="select" cascade="save-update" />
</class>
</hibernate-mapping>
Employee.java
public class Employee implements Serializable{
private int id;
private String name;
private String email;
private User user;
// getter and setters
}
Employee.hbm.xml
<hibernate-mapping>
<class name="com.site.dto.Employee" table="employee">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="identity" />
</id>
<property name="name" type="string">
<column name="name" length="50" not-null="true" unique="true" />
</property>
<property name="email" type="string" update="false">
<column name="email" length="50" not-null="true" unique="true" />
</property>
// enforcing many-to-one to one-to-one by putting unique="true"
<many-to-one name="user" column="user_id" class="com.site.dto.User" unique="true" not-null="true" fetch="select" cascade="save-update" />
</class>
</hibernate-mapping>
First I'm getting the User Object based on username. Now I'm trying to load the employee object which gives me null pointer exception. So after digging on some debug, it seems to be using a select statement with wrong where clause. Here is the hibernate debug
select employee0_.id as id1_1_0_, employee0_.name as name2_1_0_, employee0_.email as email3_1_0_,employee0_.user_id as user_id25_1_0_, from employee employee0_ where employee0_.id=?
Why is the where clause is based on employee.id and not employee.user.id ? I think this is due to the reason on how one-to-one mapping works in hbm.xml configuration where one-to-one will be linked to child table's primary key id but not user_id. I'm forcing the many-to-one to one-to-one in employee by using unique="true". I can fetch the employee in Hibernate annotation's one-to-one by defining #Join-column but I can't figure out how to map the one-to-one in hbm.xml which should refer the child's user_id.
Figured out the solution a while back, but forget to post it.
The above problem is coz, by default one-to-one mapping will be implemented for a child table which have the parent's primary key as the Child's primary key. So if we're going to eliminate that default property and use one-to-one with many-to-one (unique=true), we should define property-ref
I've added property-ref in one-to-one mapping in User.hbm.xml and now it works fine.
<one-to-one name="employee" property-ref="user" class="com.site.dto.Employee" fetch="select" cascade="save-update" />
I know there are loads of entries about this topic. I have already read all of them searching for the problem I am facing.
I have a class/table with a self-reference. This is the Class
public class Comment {
private Integer id;
private Comment parent;
private Issue issue;
private User author;
private String body;
private Date created;
private Date updated;
private Set<Comment> childs;
// All setters and getters
}
And here you can see the hbm.xml file:
<hibernate-mapping>
<class name="Comment" table="COMMENTS">
<id name="id" type="java.lang.Integer">
<column name="ID"/>
<generator class="native"/>
</id>
<many-to-one cascade="all"
class="Comment" fetch="join" name="parent">
<column name="PARENT" not-null="false"/>
</many-to-one>
<many-to-one class="Issue"
fetch="join" name="issue">
<column name="ISSUE" not-null="true"/>
</many-to-one>
<many-to-one class="User"
fetch="join" name="author">
<column name="AUTHOR" not-null="true"/>
</many-to-one>
<property generated="never" lazy="false" name="body" type="java.lang.String">
<column name="BODY" not-null="true"/>
</property>
<property generated="never" lazy="false" name="created" type="java.util.Date">
<column name="CREATED"/>
</property>
<property generated="never" lazy="false" name="updated" type="java.util.Date">
<column name="UPDATED"/>
</property>
<set cascade="delete" fetch="select" inverse="true" lazy="true"
name="childs" sort="unsorted" table="COMMENTS">
<key>
<column name="ID" not-null="true"/>
</key>
<one-to-many class="Comment"/>
</set>
</class>
</hibernate-mapping>
So far everything is fine. But I must have an error somewhere because, when I am running this unit test
Session session = sessionFactory.getCurrentSession();
User user = new User("loginName", "password", "firstName", "lastName", "eMail");
session.save(user);
session.flush();
session.clear();
Issue issue = new Issue();
session.save(issue);
session.flush();
session.clear();
Comment parent = new Comment(issue, user, "body_parent");
session.save(parent);
Comment child = new Comment(issue, user, "body_child_1");
child.setParent(parent);
parent.getChilds().add(child);
session.save(child);
session.flush();
session.clear();
parent = (Comment) session.createQuery("from Comment comment where comment.body='body_parent'").uniqueResult();
System.out.println(parent);
System.out.println(parent.getChilds().iterator().next());
I added the last two lines in order to show you the problem I am facing right now:
COMMENT = [id=1, parentId=<root>, issueId=1, authorId=1, body=body_parent, created=2014-03-08 19:28:54.832, updated=2014-03-08 19:28:54.832, numChilds=1]
COMMENT = [id=1, parentId=<root>, issueId=1, authorId=1, body=body_parent, created=2014-03-08 19:28:54.832, updated=2014-03-08 19:28:54.832, numChilds=1]
Both parent and child are the same! I don't really understand the problem here. When I retrieve the child through a query the result is correct but when I get the child through getChild() there's not query to retrieve the its childs.
Have you any idea? Any clue? I do not see the light at the end of the tunnel here :-/
Thanks a lot in advance!
I finally have realized what the problem was! Well, there was couple of problems.
First, there was no parent_id field in neither the DB nor the class.
Second, there was no exception because I activated inverse property to the set.
The combination of both issues was the problem.
Below, I post the working solution.
Comment.java
public class Comment {
private Integer id;
private Integer parentId; // Parent ID was missing
private Issue issue;
private User author;
private String body;
private Date created;
private Date updated;
private Comment parent; // Reference to parent though parentId
private Set<Comment> childs = new HashSet<Comment>(0);
protected Comment () {
}
// All setters and getters
}
Comment.hbm.xml
<hibernate-mapping>
<class name="es.kazbeel.geckobugtracker.model.Comment" table="COMMENTS">
<id name="id" type="java.lang.Integer">
<column name="ID"/>
<generator class="native"/>
</id>
<property name="parentId" type="java.lang.Integer" update="false" insert="false" column="PARENT_ID" />
<many-to-one class="es.kazbeel.geckobugtracker.model.Issue"
fetch="join" name="issue">
<column name="ISSUE"/>
</many-to-one>
<many-to-one class="es.kazbeel.geckobugtracker.model.User"
fetch="join" name="author">
<column name="AUTHOR"/>
</many-to-one>
<property generated="never" lazy="false" name="body" type="java.lang.String">
<column name="BODY"/>
</property>
<property generated="never" lazy="false" name="created" type="java.util.Date">
<column name="CREATED"/>
</property>
<property generated="never" lazy="false" name="updated" type="java.util.Date">
<column name="UPDATED"/>
</property>
<many-to-one name="parent" class="es.kazbeel.geckobugtracker.model.Comment" column="PARENT_ID" not-null="false" />
<set name="childs" table="COMMENTS" lazy="false" cascade="all-delete-orphan" inverse="false">
<key column="PARENT_ID" />
<one-to-many class="es.kazbeel.geckobugtracker.model.Comment"/>
</set>
</class>
</hibernate-mapping>
I hope this is helpful for someone in the future. This is the first time I post a self-solution :)
Take a shot using the code below:
Comment parent = new Comment(issue, user, "body_parent");
Comment child = new Comment(issue, user, "body_child_1");
child.setParent(parent);
parent.getChilds().add(child);
session.save(parent);
session.flush();
session.clear();
from this
your issue will be resolved by properly defining cascading depedencies
or by saving the referenced entities before saving the entity that
references. Defining cascading is really tricky to get right because
of all the subtle variations in how they are used.
cascade="delete"
check the options all|none|save-update|delete|all-delete-orphan
session.save(child); instead of this try cascade="all" and session.save(parent) so that all child objects will be saved with parent.
Hello Stackeroverflowers,
i'd like to ask how i can solve the following problem:
I have 3 Tables:
Hardware
PC
Software
They have a Many-to-Many Relation. So N Hardware Entries can have M Hardware Entries.
When im Calling my Hibernate stuff then i get all Pc's with the chosen Software. In Software i have a Mappign on Hardware to get the specified Hardware of a Pc.
So far so good.
The Problem im facing is that i have to make this compatible from the other side to allow to get all Pc's with the specified Hardware and then from the Pc's the software.
When i have a mapping that links from Software over Pc to Hardware its ok. When i put a mapping into Hardware to get Pc's. Im getting a Stackoverflow because Hibernate tries to create everytime i initialize a Hardware to initialize a Pc and Pc tries then to initialize a Hardware so i get a Loop that never ends.
Can someone give me a hint to Solve this problem ?
I heard of that the attribute inverse can solve this but i dont know where ro place it and how it works.
I'm thankful for every Comment.
Hardware.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 04.11.2013 17:30:12 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
<class name="de.test.database.pojo.Hardware" table="object" schema="XXX">
<id name="id" type="int">
<column name="id" />
<generator class="assigned" />
</id>
<property name="fkserialId" type="int">
<column name="fk_serial_id" not-null="true" />
</property>
<property name="name" type="string">
<column name="name_id" not-null="true" />
</property>
<set name="linkHardwareToSoftware" table="pc_link" inverse="true" lazy="true" fetch="select">
<key foreign-key="none">
<column name="fk_serial_id" not-null="true" />
</key>
<one-to-many class="de.test.database.pojo.PC" />
</set>
</class>
</hibernate-mapping>
PC.hbm.xml
<hibernate-mapping>
<class name="de.test.database.pojo.pc" table="pc_link" schema="xxx">
<id name="id" type="int">
<column name="id" />
<generator class="assigned" />
</id>
<property name="sort" type="int">
<column name="sort" not-null="true" />
</property>
<property name="owner" type="string">
<column name="owner" not-null="true" />
</property>
<many-to-one name="hardware" class="de.test.database.pojo.hardware" fetch="select">
<column name="fk_serial_id" not-null="true" />
</many-to-one>
<many-to-one name="software" class="de.test.database.pojo.Software" fetch="select">
<column name="fk_sw_id" not-null="true" />
</many-to-one>
</class>
</hibernate-mapping>
Software.hbm.xml
<hibernate-mapping>
<class name="de.test.database.pojo.Software" table="object" schema="xxx">
<id name="id" type="int">
<column name="id" />
<generator class="assigned" />
</id>
<property name="fkswId" type="int">
<column name="fk_sw_id" not-null="true" />
</property>
<property name="name" type="java.lang.Integer">
<column name="name" />
</property>
<property name="company" type="java.lang.Integer">
<column name="company" />
</property>
<set name="linkSWToHardware" table="pc_link" inverse="true" lazy="true" fetch="select">
<key foreign-key="none">
<column name="fk_sw_id" not-null="true" />
</key>
<one-to-many class="de.test.database.pojo.pc" />
</set>
</class>
</hibernate-mapping>
HibernateCode.java
try
{
String obj =" AND t.fkHWtypeId =:otid";
if(objectType==0)
obj="";
Session ses = getSession();
Query query = ses.createQuery(
" FROM hardware t"+
" WHERE t.deleted = 0 AND t.Id =:pid"+obj
);
query.setParameter("pid", HardwareId);
if(objectType!=0){
System.out.println("Reading HWtypeid...");
query.setParameter("HWtypeid", HardwareType);
}
List<Tree> list = query.list();
return list;
} catch (HibernateException e)
{
return null;
}
Stacktrace:
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: de.test.database.pojo.Pc["hardware"]->de.test.database.pojo.Hardware_$$_javassist_109["linkHardwareToSoftware"]->org.hibernate.collection.internal.PersistentSet[0]->de.test.database.pojo.pc["object"]->de.test.database.pojo.Hardware_$$_javassist_109["linkHardwareToSoftware"]
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:164)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
at org.codehaus.jackson.map.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:72)
at org.codehaus.jackson.map.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:23)
at org.codehaus.jackson.map.ser.std.AsArraySerializerBase.serialize(AsArraySerializerBase.java:86)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:446)
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:150)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:446)
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:150)
As the stack trace indicates, the problem has nothing to do with the mapping and with loading data from the database with Hibernate. The problem happens when you're serializing your beans with Jackson, because you have bidirectional associations and thus cyclic references which cause Jackson to loop endlessly.
So, you should choose how you want to serialize your objects, and use Jackson annotations or use DTOs to serialize them and break the cycles between objects. You could, for example, add a #JsonIgnore on the linkHardwareToSoftware field, so that the collection of software is not serialized when serializing a Hardware instance.
I'm wondering why hibernate generates 1 delete per entity on a child table
instead of using one delete on the foreign key
Here's the hibernate.cfg.xml (No i's not the next SO :-t
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.url">jdbc:hsqldb:file:testdb;shutdown=true</property>
<property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.connection.pool_size">0</property>
<property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hbm2ddl.auto">auto</property>
<mapping file="entities/Question.hbm.xml"/>
<mapping file="entities/Answer.hbm.xml"/>
</session-factory>
Question.hbm.xml
<hibernate-mapping>
<class name="entities.Question">
<id name="id">
<generator class="native" />
</id>
<property name="title" not-null="true">
</property>
<property name="question" type="text" not-null="true">
</property>
<bag name="answers" inverse="true" cascade="all,delete-orphan" >
<key>
<column name="questionId" index="answer_questionId_idx" not-null="true"/>
</key>
<one-to-many class="entities.Answer" />
</bag>
<property name="created" update="false" >
<column name="created" not-null="true" index="answer_created_idx"></column>
</property>
<property name="lastUpdated">
<column name="lastUpdated" not-null="true" index="answer_lastUpdated_idx"></column>
</property>
</class>
</hibernate-mapping>
Answer.hbm.xml
<hibernate-mapping>
<class name="entities.Answer">
<id name="id">
<generator class="native" />
</id>
<property name="answer" type="text" not-null="true">
</property>
<property name="created" update="false" >
<column not-null="true" name="created" index="question_created_idx"></column>
</property>
<property name="lastUpdated" >
<column name="lastUpdated" not-null="true" index="question_lastUpdated_idx"></column>
</property>
<many-to-one name="question" column="questionId" not-null="true" update="false">
</many-to-one>
</class>
</hibernate-mapping>
There's 1 Question and 2 answers in my database, this test code:
Session session = factory.openSession();
Transaction t = session.beginTransaction();
Question q = (Question) session.load(Question.class,1);
session.delete(q);
t.commit();
session.close();
I would expect it to generate SQL like,
select .... from Questions where id = 1;
delete from Answers where questionId=1;
delete from Question where id=1;
I.e., just issue one delete to do the cascading delete on Answers,
instead it's loading all the answers and issuing one delete per answer, like:
select
question0_.id as id0_0_,
question0_.title as title0_0_,
question0_.question as question0_0_,
question0_.created as created0_0_,
question0_.lastUpdated as lastUpda5_0_0_
from
Question question0_
where
question0_.id=?
select
answers0_.questionId as questionId0_1_,
answers0_.id as id1_,
answers0_.id as id1_0_,
answers0_.answer as answer1_0_,
answers0_.created as created1_0_,
answers0_.lastUpdated as lastUpda4_1_0_,
answers0_.questionId as questionId1_0_
from
Answer answers0_
where
answers0_.questionId=?
delete from Answer where id=?
delete from Answer where id=?
delete from Question where id=?
How come, and is there anything I can do about it ?
Edit, in response to Nate Zaugg, I can get the db to do the cascading delete by setting on-delete="cascade" on the one-to-many key mapping, i'm more wondering why hibernate does what it does and not does one delete on the Answers table, and wheter threre's something wrong with my mappings.
Can you not configure your DMBS to do cascading deletes on relationships? It's really easy to do.
Edit: Try this <one-to-many class="entities.Answer" lazy="false" cascade="all" />