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

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

Related

Hibernate Filter - how to join child with parent to get only one child - can't pass the parameters

I have the below configuration between my Java Classes A, B with their associated tables being A1, B1.
Table A1 has a one-many relationship with B1, defined in the database through the id column.
B1 has a composite key.
I am trying to define a filter in hibernate, such that after enabling the filter, when i query for A, in the result, i retrieve only one record of B matching the id and source fields/columns of A, and not the whole collection of B.
I was looking up solutions for even without defining a filter. Couldn't define a one-to-one in hibernate-mappings as database has a one-to-many relation defined between these tables.
B1 table has to support multiple source records for an id, so we have the id/source in B1 defined as a composite key.
But functionally, only one id-source combination of B is a valid record to go with A, and that's what I am trying achieve through this mapping.
I've been trying to see if in the filter, I could somehow join the table B1 with table A1 on the id/source of A1, so that I could pull that 1 record from B1.
But I've not been able to figure out how.
I cannot pass in the id/source of A1 at runtime by using setParameter() on the filter, as A is a child of another Class C, having one-many relation between C and A. In the application, I query for C by its primary-key id1 to pull the whole-tree (object-tree: C -> A -> B) or (tables: C1 -> A1 -> B1).
getCurrentSession().enableFilter("exportedInvoiceFilter");
I know these 2 lines in the below snippet of code doesn't work, but I was trying to see if Hibernate-Filter could refine the results in this way, where i could filter/join by the 2 columns.
condition=":id = A1.d, :source= A1.source" />
The whole mapping is as below:
<hibernate-mapping>
<class name="C" table="C1">
<id column="bId" name="bId" />
<set name="a" lazy="false" fetch="join">
<key>
<column name="bId" />
</key>
<one-to-many class="A" />
</set>
</class>
<class name="A" table="A1">
<id column="id" name="id" />
<property name="source" column="source"></property>
<set name="b" lazy="false" fetch="join" table="B1" inverse="true">
<key>
<column name="id"></column>
</key>
<one-to-many class="B" />
<filter name="filter1"
condition=":id = A1.id, :source= A1.source" />
</set>
</class>
<class name="B" table="B1">
<composite-id name="key"
class="D">
<key-property name="id">
<column name="id" sql-type="INT" />
</key-property>
<key-property name="source">
<column name="source" />
</key-property>
</composite-id>
</class>
<filter-def name="filter1">
<!--<filter-param name="id" type="INT"/>
<filter-param name="source" type="String"/>-->
</filter-def>
</hibernate-mapping>

what type of mapping is this?

With the hibernate mapping file as shown :
<hibernate-mapping>
<class name="pojo.Ghazal" table="ghazal">
<id name="s_no">
<generator class="increment" />
</id>
<property name="poem" />
<property name="poet" />
<map name="map" table="linked">
<key column="s_no" />
<index column="key_" type="string" />
<element column="val_" type="string" />
</map>
</class>
</hibernate-mapping>
what type of mapping it is ?
The pojo named ghazal has the following properties :
s_no
poem
poet
map
I have heard many types of mapping like many to one, one to one, etc etc.
This is an association done with collection(Map named "map" in your case) of values, relationship is Many to many, you can find the detailed docs here

hibernate: how to create a composite foreign key in xml?

I have a class Event containing a composite primary key (start date and end date).
A EventPlanning class holds a Set of such Event objects and has to persist them using hibernate with XML.
I can do this for classes with a common primary key:
<!-- EventPlanning xml -->
....
<id name="id" column="id">
<generator class="native" />
</id>
<property name="name" column="name" type="string" update="false" />
<set name="events" table="events" cascade="all">
<key column="event_id"> // ###### here! ######
</key>
<one-to-many class="myPackage.Event" />
</set>
...
but I can't find out how this works with a composite key..
replacing the <key column="event_id"> with the following code doesn't work:
<key>
<property column="start_date" />
<property column="end_date" />
</key>
I'd be glad if somebody can show me the right syntax! :)
the Event xml looks like this:
<class name="myPackage.Even" table="events">
<composite-id>
<key-property name="startDate" column="start_date" type="date" />
<key-property name="endDate" column="end_date" type="date" />
</composite-id>
<property name="signinDeadline" column="signin_deadline"
type="date" />
<property name="confirmationDeadline" column="confirmation_deadline"
type="date" />
<set name="participants" table="participants" cascade="all">
<key column="event_id">
</key>
<one-to-many class="myPackage.Participants" />
</set>
</class>
thanks in advance! :)
Something like this works for me:
<class name="YourClass" table="your_table" ...>
<composite-id name="compositeId" class="DoubleDate">
<key-property name="start_date" column="start_date"/>
<key-property name="end_date" column="end_date"/>
</composite-id>
...
</class>
public class DoubleDate implements Serializable {
private Date start_date, end_date;
public DoubleDate() {
}
// setters, getters
}
public class YourClass {
private DoubleDate compositeId;
// public no args ctr, getters, setters, etc
}
After having now worked longer with JPA and Hibernate, I'd just say that you simply should not use composite primary keys. Caches use ids as keys that point to cached values, data retrieving methods like get and load expect the id as parameter etc.
The advantages gained by having an id field pay off against the additional space it needs.

Hibernate - Set filtered on column in parent and child tables

I have a Node which is associated with NodeDatas. These NodeDatas have revisions, and Node has a column latestRevision which tells it which is the last revision.
I'm trying to figure out the mapping I need to get the set below filtered by the latestRevision in Node.
<class name="Node" table="RatNodes">
<id name="id">
<generator class="native"/>
</id>
<set fetch="join" name="nodeDatas" table="RatNodeData" inverse="true" access="field" cascade="all-delete-orphan" order-by="dataOrder asc">
<key column="nodeID" on-delete="cascade"/>
<one-to-many class="NodeData"/>
<filter name="revisionFilter" condition="revision = latestRevision"/>
</set>
<property name="questionNumber" access="field"/>
<property name="type" access="field"/>
</class>
<class name="NodeData" table="RatNodeData">
<composite-id>
<key-property name="nodeID" access="field"/>
<key-property name="key" access="field" column="dataKey"/>
<key-property name="order" access="field" column="dataOrder"/>
<key-property name="revision" access="field"/>
</composite-id>
<property name="value" access="field" type="EncodedStringUserType" column="dataValue"/>
</class>
The result of this mapping when I enable the filter is an error:
Caused by: com.jnetdirect.jsql.JSQLException: Invalid column name 'latestRevision'.
The query it spits out is:
select nodedatas0_.nodeID as nodeID1_, nodedatas0_.dataKey as dataKey1_, nodedatas0_.dataOrder as dataOrder1_, nodedatas0_.revision as revision1_, nodedatas0_.nodeID as nodeID46_0_, nodedatas0_.dataKey as dataKey46_0_, nodedatas0_.dataOrder as dataOrder46_0_, nodedatas0_.revision as revision46_0_, nodedatas0_.dataValue as dataValue46_0_ from RatNodeData nodedatas0_ where nodedatas0_.revision = nodedatas0_.latestRevision and nodedatas0_.nodeID=? order by nodedatas0_.dataOrder asc
I can tell this is wrong as it's looking for latestRevision on NodeData but I'm not sure how to tell it to look on Node instead. Is this possible in hibernate? Seems like a pretty simple query if I were just using SQL.
Ended up doing this instead of trying to do it in the mapping file:
List datas = session.createCriteria(NodeData.class)
.add(Restrictions.eq("nodeID", this.id))
.add(Restrictions.eq("revision", this.latestRevision))
.list();
I'm not sure if this is the right way to do this or not. Would love to hear about a mapping file solution too.
You have not defined any filter or 'latestRevision' as filter-param. So it will not come to know that what actually the entity means.
Try using following.
For further help have a look at the link.

Making join with Criteria

I'm terribly new to Hibernate. I've googled for two hours but I still can't figure out, how to make JOIN without using HQL, only by criteria. I have tables Clients(cID, name) and Visits(vID, vcID, date). The relation is one to many (one client can visit multiple times). I would also like to do it without setFetchMode. Just Criteria. Do I have to change the mappping xml?
UPDATE:
this is part of my mapping xml:
<class name="Client" table="Clients">
<id name="cID" column="cID"><generator class="native"/></id>
<property name="name" length="10" not-null="true"/>
</class>
<class name="Visit" table="Visits">
<id name="vID" column="vID"><generator class="native"/></id>
<property name="vcID" length="10" not-null="true"/>
<property name="date" length="25" not-null="true"/>
</class>
Having a class Client with a list-attribute "visits" that's mapping to your Visit-Entity:
Criteria criteria = session.createCriteria(Client.class);
criteria.addCriteria("visits");
This would create an inner join between your client-table and your visits-table.
Update:
Here you'll find some good examples: http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/querycriteria.html#querycriteria-associations
Mapping Example
I hardly ever use hibernate mapping xml, however it should read similiar to:
<class name="Client" table="Clients">
<id name="cID" column="cID"><generator class="native"/></id>
<property name="name" length="10" not-null="true"/>
<bag name="visits">
<key column="vcId"/>
<one-to-many class="Visit"/>
</bag>
</class>
Tell Hibernate that there is a property "visits" which represents a one-to-many relationship.
You need to update you mapping:
<class name="Client" table="Clients">
<id name="cID" column="cID"><generator class="native"/></id>
<property name="name" length="10" not-null="true"/>
<!-- Declare Set<Visit> visits in the Client class-->
<set name="visits" lazy="false" cascade="all">
<key column="vcID"/>
<one-to-many class="your.package.Visit"/>
</set>
</class>
<class name="Visit" table="Visits">
<id name="vID" column="vID"><generator class="native"/></id>
<!-- and add "Client client" property to your Visit class -->
<many-to-one name="client" column="vcID" lazy="false"/>
<property name="date" length="25" not-null="true"/>
</class>
Then:
Criteria criteria = session.createCriteria(Visit.class).addCriteria("client")
.add(Restriction.eq(...));
or
Criteria criteria = session.createCriteria(Client.class).addCriteria("visits")
.add(Restriction.eq(...));
And Hibernate will join them automatically.

Categories

Resources