Hibernate - Remove child elements - java

I am searching for simplest way to remove "child" objects from one-to-many set.
<set cascade="all-delete-orphan" name="publicSitePortfolioWorksToTypeRelations" table="publicSitePortfolioWorksToTypeRelation" inverse="true" lazy="false" fetch="select">
<cache usage="read-write" />
<key>
<column name="workId" />
</key>
<one-to-many class="khartn.my.site.mysite.portfolio.models.PublicSitePortfolioWorksToTypeRelation" />
</set>
Is it possible to delete all child elements from hibernate set, when I am passing empty HashSet ([]) to the main class's method setPublicSitePortfolioWorksToTypeRelations(Set<PublicSitePortfolioWorksToTypeRelation> publicSitePortfolioWorksToTypeRelations) ?

can you prefer to use annotation I think that is simple way to remove child object from one to may set.

Related

Hibernate cascade=save-update, updates all siblings of new child entity

I have a parent-child configuration of hibernate as below
<class name="A" table="TAB_A">
<... columns defined here ...>
<map name="lookup" inverse="true" cascade="all-delete-orphan" lazy="false" fetch="join" access="field" >
<key column="A_FK" />
<map-key type="string" column="key_name" />
<one-to-many class="B" />
</class>
<class name="B" table="TAB_B>
<... columns defined here ...>
<many-to-one name="a" class="A" column="A_FK" not-null="true" cascade="save-update" />
<property name="key" column="key_name" type="string" not-null="true" />
</class>
Recently we upgraded hibernate from 2.x to 4.x.
The problem we are facing after this migration is that the new insert in table B inserts the new records as well as updates all the existing records of TAB_B.
The update statement updates the "key_name" column of TAB_B.
I tried looking into the source code of Hibernate and found that the AbstractCollectionPersister.insertRows method is now overridden in OneToManyPersister.insertRows. This overridden method calls another method writeIndex which invokes the update statements.
So is this intended behavior? If not why the child entries are getting updated?
Are we missing something?
Hanumant

how to set Multiple one to many relation with same column hibernate

There are two entities.
Route (Arrival, ArrivalID and Departure , DepartureID) as Location
Location (Arrivals , Departures ) as Route
Location will have 2 one to many relationship with route table.
I am trying to set.
Route.xml
<many-to-one name="departure" class="com.nakisa.agency.Location" fetch="select" insert="false" update="false">
<column name="locationID" not-null="true" />
</many-to-one>
<many-to-one name="arrival" class="com.nakisa.agency.Location" fetch="select" insert="false" update="false">
<column name="locationID" not-null="true" />
</many-to-one>
Location.xml
<set name="arrivals" table="Routes" inverse="true" lazy="true" fetch="select">
<key>
<column name="arrivalID" not-null="true" />
</key>
<one-to-many class="com.nakisa.agency.Route" />
</set>
<set name="departures" table="Routes" inverse="true" lazy="true" fetch="select">
<key>
<column name="departureID" not-null="true" />
</key>
<one-to-many class="com.nakisa.agency.Route" />
</set>
I'm getting error as departureID is null even i set departureID there in route.
How to correct these mappings in order to work
I think you have problem with fetching:
Lazy="true|false" controls whether an association is loaded eagerly or on demand.
fetch="select|subselect|join|batch" controls how is that entity or collection loaded, when it's required to be loaded.
With lazy="true" and fetch="select" Hibernate will lazy load the collection and will load it with a select. If you set lazy="false", the same select will be executed, the difference will be that it will get executed eagerly. Hope this makes sense.
Try to set lazy="false", and then see if your departureID is still null.

Hibernate unnecessarily update joined tables

Basically, I have 2 classes P & M, and their corresponding tables T_P & T_M, and they are joined in a one-to-many relation, one P has a set of Ms. Something like this:
<class name="P" table="T_P">
<set name="ms" cascade="all" lazy="false" inverse="true">
<key column="P_ID" not-null="true" foreign-key="FK_M_P"/>
<one-to-many class="M"/>
</set>
</class>
<class name="M" table="T_M">
<many-to-one name="p" column="P_ID" foreign-key="FK_M_P" class="P"
update="false" not-null="true" cascade="none"/>
</class>
Now when I make changes to P and ask Hibernate to update DB. It's very likely Hibernate will do a batch of updates, one to T_P, several to T_M, and I know the later part is not necessary as I didn't change those Ms. But I think because my object is detached, hibernate has to update everything.
So my question is, in my case, can I ask Hibernate to only update partially, and not to make excessive DB hits? Or other suggestion to optimize this is also appreciated. But this is a legacy program, and I may not be able to make drastic changes.
As far as inverser="true" mapping is concern, it will only work for bidirectional association.
in your configuration , you have not mentioned mapping for P in M.
<class name="P" table="T_P">
<set name="ms" cascade="all" lazy="false" inverse="true">
<key column="P_ID" not-null="true" foreign-key="FK_M_P"/>
<one-to-many class="M"/>
</set>
</class>
<class name="M" table="T_M">
/*mention mapping for P here, if you want to use inverse property properly*/
</class>
issue might be because of cascade="all" mapping.
try to set cascade="save-update" in mapping for collection.
you go through the following link , which contain simple/basic example of parent-child relationship.
sample mapping for parent-child relation is as follow.
/*parent contain set of child entity*/
<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
/*child has reference of parent*/
<many-to-one name="parent" column="parent_id" not-null="true"/>
I hope this help you to resolve issue.

Override lazy loading setting for related entities in a hibernate mapping file

I have a mapping file which sets lazy=false for some related entities. This makes sense for a lot of use cases but there are some exceptions. The problem is that I dont want to fetch the related associations at query time for these cases, which are very expensive time-wise.
Example of the mapping for the entity.
<class name="Category" table="category">
<id name="id" type="string">
<column length="50" name="id"/>
<generator class="uuid"/>
</id>
<property name="name" type="string">
<column length="100" name="name" not-null="true"/>
</property>
<set inverse="true" lazy="false" name="categorySourcesList">
<key>
<column length="50" name="categoryid" not-null="true"/>
</key>
<one-to-many class="CategorySource"/>
</set>
</class>
My question is, is there a way to override the lazy value which is set in the mapping file, either in the sql-query I custom write or enabling lazy load as one of the parameters in the DAO? or through some annotations?
Yes, you can override the annotated or xml mapped association fetching strategy.
Hibernate Documentation
Criteria criteria = session().createCriteria(MyClass.class);
criteria.add(Restrictions.eq("id", "1234"));
criteria.setFetchMode("children",FetchMode.EAGER);
return (MyClass) criteria.uniqueResult();
This will return you an instance of MyClass with its children eagerly fetched.
AFAIK you can't override EAGER loading with LAZY but only the other way round.
Thus, you'd need to define the association the be LAZY and override that in the queries using joins. There might be other ways to do that but I'm afraid that's all I know right now.

Hibernate many-to-many mapping and cascade=delete

I have a mapping (only important parts):
<class name="xyz.Role" table="ROLE" lazy="true">
<id name="id" type="java.lang.Integer">
<column name="ROLE_ID"/>
<generator class="increment"/>
</id>
<set name="assignments" lazy="true" table="PERSON_ROLE" cascade="delete"
inverse="true">
<key column="ROLE_ID" />
<many-to-many class="xyz.Person" column="PERSON_ID" />
</set>
</class>
and
<class name="xyz.Person" table="PERSON" lazy="true">
<id name="Id" type="java.lang.Integer">
<column name="TPP_ID"/>
<generator class="increment"/>
</id>
<set name="roles" lazy="true" table="PERSON_ROLE" cascade="save-update">
<key column="PERSON_ID" />
<many-to-many class="xyz.Role" column="ROLE_ID" />
</set>
</class>
With this mapping, when I delete a role, very person with this role is also deleted. What I would like to achieve, is delete the association (row from PERSON_ROLE table) when I delete Role. Is there any way to achieve this?
Cascade works on the level of entities. Since Person_Role is not mapped as entity, cascade can not help you AFAIK.
You might use a database-level "on cascade delete" on the foreign key from Person_Role to Role.
Or you can - as sfussenegger points out - remove the association programatically. Note that since you mapped the association on both entities, every row in Person_Role will appear twice in your object model. In such cases, it is recommended to remove the relevant entries from both collections in order not to corrupt your object model. Hibernate however will only look at the end of the association that is not mapped with inverse="true" when persisting changes. That is, to delete from the association with your current mapping, you must delete from Person.roles, not Role.assignments:
for (Person p : role.assignments) {
person.roles.remove(role)
}
Or you might wish to replace the many-to-many mapping with an association entity, in which case you could simply use cascade. This would allow you to add more information to assignments more easily. For instance, if you had to express "Joe works 30% on QA and 70% as requirements engineer", you could simply add that field to the association.
Why not simply call role.getAssignments().clear() before deleting the role?
for (Person p : role.getAssignments()) {
person.getRoles.remove(role)
}
role.getAssignments.clear();
session.delete(role);
I'm not a big fan of cascade="delete". I get this weird gut feeling any time I think about a snippet of XML being able to delete entire tables of valuable data :)
Don't put a mapping on the Role's side at all. If you eventually need this information, get it with an HQL query. I.e. get rid of this:
<set name="assignments" lazy="true" table="PERSON_ROLE" cascade="delete"
inverse="true">
<key column="ROLE_ID" />
<many-to-many class="xyz.Person" column="PERSON_ID" />
</set>
And whenever you need the persons for a role (which I think should be rare) get it by:
SELECT p FROM Person p JOIN p.roles WHERE role=:role

Categories

Resources