I have 3 Entities named, Discipline, DisciplineMembership, DisciplineManagement:
one Discipline has many DisciplineMemberships
one Discipline has many DisciplineManagements
a DisciplineManagement has one to one relation with DisciplineMembership
the related part of associated hbm file for Discipline looks like this:
Discipline.hbm:
<class name="Discipline" table="DISCIPLINES">
<id name="id" type="java.lang.Long">
<generator class="increment" />
</id>
<set name="memberships" inverse="false" cascade="all-delete-orphan" >
<key column="disciplineId" not-null="true" />
<one-to-many class="DisciplineMembership" />
</set>
<set name="managements" inverse="false" cascade="all-delete-orphan">
<key column="disciplineId" not-null="true" />
<one-to-many class="DisciplineManagement" />
</set>
</class>
in a function called A, I want to iterate over DisciplineMemberships and DiscipilineManagements of a Discipline:
public void A(){
Discipline discipline = this.getObject();
for(DisciplineMembership mem: discipline.getMembers()){
System.out.print(mem);
}
for(DisciplineManagement man: discipline.getManagements()){
System.out.print(man);
}
}
first for statement runs correctly, but on second one, I catch LazyInitializationException as follows:
13-04-27 13:54:26,115 [hibernate.LazyInitializationException (<init>:42)] ERROR: failed to lazily initialize a collection of role: Discipline.managements, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: Discipline.managements, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:368)
at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:111)
at org.hibernate.collection.PersistentSet.iterator(PersistentSet.java:186)
at ObjectManagementPart_Edit.A(ObjectManagementPart_Edit.java:6)
I am confused how can this be possible? the Discipline Session is open, I can iterate through its memberships, but when I want to Iterate through its managements, hibernate can not lazyInitialize them.
what should I do?
UPDaTE:
I tried to use Hibernate.initialize() function, so the function A is now like this:
public void A(){
Discipline discipline = this.getObject();
Hibernate.initialize(discipline.getMembeships());
for(DisciplineMembership mem: discipline.getMemberships()){
System.out.print(mem);
}
Hibernate.initialize(discipline.getManagements());
for(DisciplineManagement man: discipline.getManagements()){
System.out.print(man);
}
}
the first use of Hibernate.initialize() for Membeships has no problem,
but in second use for Managements I get this exception:
org.hibernate.HibernateException: collection is not associated with any session
at org.hibernate.collection.AbstractPersistentCollection.forceInitialization(AbstractPersistentCollection.java:474)
at org.hibernate.Hibernate.initialize(Hibernate.java:431)
at discipline.ObjectManagementPart_Edit.A(ObjectManagementPart_Edit.java:6)
Now I am more Confused :/
Just because you can iterate over memberships does not tell you that it actually just fetched them lazy. If the memberships where loaded before (where the session was open) they are keept. Hibernate.initialize(...) will not do anything if the list was already initialised.
For a more precise answer, you need to describe your session management strategy. Most problems with hibernate are rooted in the fact that the session management is not good.
Related
I have an EMF containment hierarchy: An House contains a list of Rooms, represented using an ecore file.
Before persisting, when I initially create my house and populate it with rooms, the eContainer value for a Room object correctly show the containing house. I then use Hibernate (no Teneo involved) to save the house (which cascade-saves the rooms).
When I reload the House object, I get references to all the Rooms. However, the eContainer value for a Room is now null. This is relevant because I use EcoreUtil.delete(bathroom) to delete a Room from its containing house, and that uses EObject.eContainer().
Relevant Hibernate Mapping:
<class name="House" table="house" lazy="false">
<bag name="rooms" lazy="true" cascade="save-update, delete">
<key update="true">
<column name="house_id" unique="false"/>
</key>
<one-to-many class="Room"/>
</bag>
</class>
<class name="Room" table="room" lazy="false">
</class>
What should I do to delete EMF objects? Should I call something other than EcoreUtil.delete(obj)? Should I manually add the eContainer references on load? Should I change my hibernate mapping?
Hibernate requires every property to have a getter and setter. In EMF (not using Teneo at least), there is a getter for Rooms but no setter.
public EList<Room> getRooms() {
if (rooms == null) {
rooms = new EObjectContainmentEList<Room>(Room.class, this, housePackage.HOUSE__ROOM);
}
return rooms;
}
I provided my own setter below.
public void setRooms(List<Room> roomList) {
this.rooms = new BasicEList<>();
this.rooms.addAll(roomList);
}
This was incorrect because a BasicEList does not do any provide EMF notification, so the eContainer was not getting set. I changed to the following, which in the underlying implementation uses a EObjectContainmentEList and so far it seems to work.
public void setRooms(List<Room> roomList) {
ECollections.setEList(getRooms(), roomList);
}
I encounter strange behaviour on Hibernate's merge() if I use an entity with a composite-id and a version number.
This is my hibernate mapping file:
<class name="ArticleTurnover" table="T_ARTICLETURNOVER">
<composite-id>
<key-property name="mainArticleId" type="java.lang.Integer" column="ARTICLETURNOVER_ID"/>
<key-property name="locationId" type="java.lang.Integer" column="ARTICLETURNOVER_LOCATIONID"/>
</composite-id>
<version name="version" column="ARTICLETURNOVER_VERSION" />
... some properties
</class>
And this is the code (it fails...):
ArticleTurnover at = new ArticleTurnover();
at.setMainArticleId(1);
at.setLocationId(1);
ArticleTurnover savedAt = em.merge(at);
assertNotNull(savedAt.getMainArticleId());
assertNotNull(savedAt.getLocationId());
After calling merge the fields mainArticleId and locationId are both null. The code above is just a test, but if I would commit a transaction, hibernate would insert null into the composite-id fields, and fail!
If I change em.merge(at) to em.persist(at) everything works. And if I manually set at.setVersion(0) (!!) it also works.
I ended up in adding a component type ArticleTurnoverId for my composite-id like mentioned here: http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/mapping.html#d0e4819.
Can anybody tell me what I am doing wrong, or point out why this happens?
I have the following class diagram and I want to map it to a database (note that Person has a list with objects of class Vehicle).
Also my database looks like:
All tables in the database that represent a subclass of the Vehicle class have all the fields of the superclass Vehicle. Also, all the relations show a one-to-many relationship from Person to Vehicle, Car and Motorcycle.
My hibernate mapping files are the following:
Person.hbm.xml
<hibernate-mapping package="....">
<class name="Person" table="Persons">
<id name="key" column="Person_ID">
<generator class="native"/>
</id>
<list name="ownedVehicles" inverse="false" cascade="all">
<key column="Person_ID" not-null="true" />
<list-index column="idx"/>
<one-to-many class="Vehicle"/>
</list>
</class>
</hibernate-mapping>
Vehicle.hbm.xml
<hibernate-mapping package="...">
<class name="Vehicle" table="Vehicles" polymorphism="implicit">
<id name="id" type="int" column="Vehicle_ID">
<generator class="increment"/>
</id>
<property name="numOfSeats"/>
<union-subclass name="Car" table="Cars"></union-subclass>
<union-subclass name="Motorcycle" table="Motorcycles"></union-subclass>
</class>
</hibernate-mapping>
The problem (error I get) is the following:
Hibernate: insert into Persons (Person_ID) values (default)
2013-06-26 15:41:52 WARN JdbcCoordinatorImpl:424 - HHH000386: ResultSet had no statement associated with it, but was not yet registered
Hibernate: update Car set numOfSeats=? where Vehicle_ID=?
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
I get this error when I run:
Car car = new Car();
car.setNumOfSeats(5);
Person person = new Person();
person.getOwnedVehicles().add(car);
ManagePerson managePerson = new ManagePerson();
Integer personID = managePerson.store(person);
The store() function of ManagePerson actually creates a session and a transaction and then uses the save() method provided by Hibernate to persist the objects into the database.
As far as I understand Hibernate usually will do insert into Persons, then insert into Cars and finally update Cars (the update is done to save the foreign keys on Cars table that will reference the Person that owns the cars). However, here this is not the case and the insert into Cars seems to be getting skipped. I understood how Hibernate works here by trying person.getOwnedVehicles().add(vehicle); instead of person.getOwnedVehicles().add(car); on the code given above.
As you might understand, I am trying to see if Hibernate actually understands in which "subclass" table a record should go, depending on the class of the object contained in the ownedVehicle list of the Person class. For example, if the ownedVehicles has an object of class Car and one of class Motorcycle, then each of these should go to Cars and Motorcycle tables respectively.
Note: I am using Hibernate 4.2.2 and HSQLDB 2.2.9.
I would appreciate any help with this.
Thanks.
I think it is just a matter of incorrect use of the implicit polymorphism of Hibernate.
Implicit polymorphism for your case can only work by changing your list to have
inverse="true". This can be done of course if your Vehicle class also 'knows' about the relationship with the Person class (e.g. by adding an 'Owner' property and the corresponding mapping).
(Have a look at this table and the case of "table per concrete-class (union-subclass)" and one-to-many associations.
If you enable logging and raise the log level to DEBUG you would see that currently Hibernate tries to update the Vehicles table with the Person_ID instead of the Car table like you meant it to. This is because of the inverse="true" and the limitations of the combination of the Table-per-concrete-class mapping strategy and implicit polymorphism (have a look at the documentation).
So, by having the Vehicle class know about its Owner and using inverse="true" you should be able to succeed in what you are trying to do. Either this or try one of the other inheritance mapping strategies (again have a look at the documentation).
If the managePerson.store(...) method doesn't have a recursive call to the objects in "getOwnedVehicles()" such that it can then call their "store" methods then you shouldn't expect that the created "car" object would be inserted into the table.
You are in fact calling "managePerson.store" not "manageCar.store", I'd have to see the code in the .store(...) method to be sure though but I would expect that it is not doing an iteration of the Vehicles and is not doing an insert for any discovered ones (why should it unless you built it explicitly to do that?).
Consider the following:
User.java
class User{
...
private Set<Community> communities = new HashSet<Community>();
...
}
Community.java
class Community{
...
private Set<User> users = new HashSet<User>();
...
}
User.hbm.xml
<set name="communities" cascade="save-update" lazy="false" table="tbl_user_community">
<key column="user_id" />
<many-to-many class="Community" column="community_id"/>
</set>
Community.hbm.xml
<set name="users" lazy="false" cascade="save-update" table="tbl_user_community" inverse="true">
<key column="community_id" />
<many-to-many class="User" column="user_id"/>
</set>
both has many-to-many relationship.
Code for adding community to user:
HibernateTemplate template = null;
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
template = new HibernateTemplate(sessionFactory);
User user = template.get(User.class, "100");
Community community = template.get(Community.class,1);
user.getCommunities().add(community);
template.saveOrUpdate(user);
Scenario:
community1 is assigned to user1 (inserted into database)
community2 is assigned to user1 (inserted into database)
community1 is assigned to user2 (inserted into database)
community2 is assigned to user2 (not inserted into database, throws NonUniqueObjectException)
if i use template.merge(user) instead of template.saveOrUpdate(user).. it works .. why???
saveOrUpdate() and update() take a detached object and attach this object instance to the session. So if an entity with the same type and ID is already attached to the session, Hibernate can't attach another instance: it would break the guarantee that only one entity with a given ID exists in a session. So it throws this exception.
merge() is different. It takes an detached entity, loads the entity with the same ID in the session if not already loaded, and copies the state (i.e. the fields) of the detached entity to the attached one. The detached entity stays deteached and unmodified.
So, in general, merge() should be preferred. BTW, the other methods don't exist in the JPA API. If you use saveOrUpdate(), you should generally call it as the first instruction of the transaction, to avoid being in the situation causing the NonUniqueException.
I have two classes A and B. Many B's can have association with a single A, hence a many-to-one relationship from B to A. I've mapped the relationship like:
<class name="A" table="tbl_A">
<property name="propA" column="colA"/>
</class>
<class name="B" table="tbl_B">
<property name="propB" column="colB"/>
<many-to-one name="a" class="A" column="col1" cascade="delete"/>
</class>
A has nothing mapped to B. Keeping this in mind we intend to delete B when it's associated A is deleted. This could have been possible if I could define an inverse="true" on the many-to-one association in B but hibernate does not allow that.
Can anyone help with this? We do not want to write anything in A for this.
Hibernate only cascades along the defined associations. If A knows nothing about Bs, nothing you do with A will affect Bs.
Pascal's suggestion is, therefore, the easiest way to do what you want:
<class name="A" table="tbl_A">
...
<set name="myBs" inverse="true" cascade="all,delete-orphan">
<key column="col1"/>
<one-to-many class="B"/>
</set>
</class>
<class name="B" table="tbl_B">
...
<many-to-one name="a" class="A" column="col1" not-null="true"/>
</class>
Note that setting cascade="delete" on B as you have it in your original code will NOT do what you want - it tells Hibernate to "delete A if B is deleted" which is likely to result in constraint violation (if there are any other Bs linked to that A).
If you absolutely cannot add a collection of Bs to A (though I can't really think of the circumstances where that'd be the case), your only other alternative is to define cascade delete from A to B at the foreign key level; your Bs will then be deleted when your A is deleted.
This is a rather ugly solution, however, because you have to be extremely careful of how you delete A in Hibernate:
Session must be flushed prior to deleting A (having pending updates to B may result in an error or A and some Bs being re-inserted behind the scenes)
All Bs linked to your A (and since you're not maintaining the relationship from A side that means all Bs) must be evicted from all active sessions and 2nd level cache.
I think you need to cascade="all,delete-orphan" from A to B's with a one-to-many association.