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.
Related
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
I have two entities - Category and Attribute. Category can have multiple related attributes, and Attribute can be related to any number of categories. Association should be available only on Category side - Attribute objects are not aware of categories they are related to.
So I model this association as unidirectional many-to-many:
Category.hbm.xml
<class name="Category" table="category" proxy="ICategory" entity-name="category">
<id name="id" column="id" unsaved-value="null"><generator class="identity" /></id>
...some properties...
<bag name="relatedAttributes" table="category_attribute" fetch="select">
<key column="id_category" />
<many-to-many column="id_attribute" entity-name="attribute" />
</bag>
</class>
and Attribute.hbm.xml
<class name="Attribute" table="attribute" proxy="IAttribute" entity-name="attribute">
<id name="id" column="id" unsaved-value="null" ><generator class="identity" /></id>
...some properties...
</class>
Mapping works perfectly with current data until it needs an update. I just want to do things as simple as these:
ICategory c = (ICategory) session.get("category", 1);
c.getRelatedAttributes().add((IAttribute) session.get("attribute", 2));
session.update("category", c);
How can i make this association updateable?
Finally done. Changes that affect behavior:
<bag name="relatedAttributes" table="category_attribute" fetch="select" inverse="false" cascade="save-update">
...
</bag>
and do not forget to call session.flush() after operations.
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.
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.
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