Hibernate Adding where clause on one-to-many mapping - java

I have this set on my .hbm file.
<set name="subTopicsTb" table="subtopics_tb" inverse="true" lazy="false" fetch="select">
<key>
<column name="topic_id" />
</key>
<one-to-many class="com.topics.model.SubTopics" />
</set>
now, the default is that, hibernate get's all subtopics where the topic_id is the id.
i want to filter the subtopics. adding something like where subTopics.date is not null thanks

This actually works, being date the column name in your DB. where attribute in the set appends raw sql to your query, so you have to specify as it is in your database and NOT HQL:
<set name="subTopicsTb" table="subtopics_tb" inverse="true" lazy="false"
fetch="select" where="date is not null">
<key>
<column name="topic_id" />
</key>
<one-to-many class="com.topics.model.SubTopics" />
</set>

Add a where clause? I don't know how you set that in an XML config .. but you can check out the annotation version here.
I found something at stackoverflow on how to add the where to your XML.

Are you using HQL to retrieve your SubTopics? If so, you can include the filter in your selection. For example:
String query = "FROM SubTopic subtopic WHERE subtopic.date != null"

Related

Map hibernate many-to-one with the foreign key in subclass joined table

I'm mapping some entities using Hibernate 3 for my project and simply explained I've got kind of this:
Student entity (tstudent table)
UniversityStudent entity (tuniversitystudent table)
University entity (tuniversity table)
UniversityStudent extends from Student and has its own attributes, like the university itself, which is a foreign key into the tuniversitystudent table. It is also mapped like a subclass into the Student class, using a discriminator field:
<class name="mycompany.Student" table="tstudent" discriminator-value="BASIC">
<id name="id" column="id" type="integer">
<generator class="native" />
</id>
<discriminator column="type" />
<property name="name" column="name" />
<property name="surname" column="surname" />
<property name="phoneNumber" column="phone_number" />
<subclass discriminator-value="UNIVERSITY"
name="mycompany.UniversityStudent">
<join table="tuniversitystudent">
<key column="id_student" />
<many-to-one name="university" class="mycompany.University">
<column name="id_university" />
</many-to-one>
</join>
</subclass>
</class>
Well, now I want to have a Set collection with the UniversityStudent entities for each University. So I map it like that:
<class name="mycompany.University" table="tuniversity">
<id name="id" column="id" type="integer">
<generator class="native" />
</id>
<property name="name" column="name" />
<set name="universityStudents" table="tuniversitystudent">
<key>
<column name="id_university" />
</key>
<one-to-many class="mycompany.UniversityStudent" />
</set>
</class>
My problem comes when I want to load a University object, Hibernate complains that id_university doesn't exist in tstudent table. I checked the generated SQL query and it really tries to load it from tstudent.
Unknown column 'student0_.id_university' in 'field list'
It seems that it's recognizing that it is a subclass of the basic Student and tries to join the collection using a field in the parent table, but however the field is actually in the child table, because only university students can have a University assigned.
I tried another workaround which seems to work but it's not valid for me, that's mapping the UniversityStudent as a joined-subclass instead of a subclass with a join inside:
<joined-subclass name="mycompany.UniversityStudent" table="tuniversitystudent">
<key column="id_student" />
<many-to-one name="university" class="mycompany.University">
<column name="id_university" />
</many-to-one>
</joined-subclass>
However, I'm interested in keeping it as a subclass with a discriminator value. Any idea?
I checked out some resources and finally got into this bug: https://hibernate.atlassian.net/browse/HHH-1015, which looks absolutely compatible with your case. Checkout this old question as well, again very similar to your case.
I firstly read the definition of table per sublass given by Hibernate (I know, it is for version 3.3 but I couldn't find the same source for Hibernate 4): joined-subclass seems (to me) to be a custom implementation of subclass using a discriminator provided by Hibernate and that is a good reason to stay away from its usage. However, from what I know, the mappings table per sublass and table per subclass using a discriminator should be equivalent, that's why I believe the bug I pointed you out is really still open.
If you have time and will, you can try to use another JPA provider and check if you still run in the same issue. JPA 2.0 specifications is a thing, provider implementation is another! I recently run into another bug (related to #IdClass) which forced me to try EclipseLink and the configuration which was not working with Hibernate was right with Eclipse Link
Seems you can use Custom SQL (or HQL) for loading. Haven't tried it myself, but looks like, hmm, at least as a last resort, it provides a decent solution.
Define the query in your HBM:
<sql-query name="universityStudents">
<load-collection alias="unistu" role="University.universityStudents"/>
SELECT unistu.*, student.*
FROM tuniversitystudent unistu
JOIN tstudent student
ON unistu.id_student = student.id
WHERE unistu.id_university = :id
</sql-query>
And then use it inside University:
<set name="universityStudents" inverse="true">
<key/>
<one-to-many class="mycompany.UniversityStudent"/>
<loader query-ref="universityStudents"/>
</set>

Hibernate Mapping of Join Table with Meta Data

I'm trying to figure out how to map the relationship between two tables through a join table that has some meta data in it. In short, the three tables represent the page of a form, and each page can contain any number of elements (questions.) For some reason, the original developer decided that elements could be used on multiple forms. This means that the weight column, used to order the elements on the page, is in the join table.
How the heck do I map this in XML? (Annotations aren't an option.)
For the join table, I guess it's like this:
<class name="com.foo.bar.model.db.ApplicationPageElements"
table="APPLICATION_PAGE_ELEMENTS">
<composite-id name="id" class="com.foo.bar.model.db.ApplicationPageElementsKey">
<key-property name="pageId" column="page_id"/>
<key-property name="elementId" column="element_id"/>
</composite-id>
<property name="weight" type="java.lang.Long">
<column name="WEIGHT" precision="0" />
</property>
</class>
My instincts have me wanting to do something like this from the ApplicationPage side of things:
<set name="applicationElements" table="applicationPageElement">
<key column="page_id"/>
<many-to-many column="element_id" unique="true"
class="com.foo.bar.model.db.ApplicationElements" />
</set>
And that's where I get all slack-jawed, stare at the screen, and sob.
We're using .hbm.xml files to map our database. We also made the decision to not change our database.
Any ideas on how to map this in XML?
Instead of thinking of the relationship between application_page and application_element as many to many, think of it as a one to many relationship from application_page to ApplicationPageElements and a one to many relationship from application_element to ApplicationPageElements.
In your application_page xml mapping add this:
<set name="applicationElements" inverse="true">
<key column="page_id"/>
<one-to-many class="ApplicationPageElements"/>
</set>
page_id forms a part of the primary key of the join table. So, mark the collection as inverse.
Your mapping for the join table is correct. But, with the above change current mapping of your join table you can navigate from application_page to ApplicationPageElements. To navigate from application_page to application_element (via ApplicationPageElements) add a many to one relationship in join table mapping.
<class name="com.foo.bar.model.db.ApplicationPageElements"
table="APPLICATION_PAGE_ELEMENTS">
<composite-id name="id" class="com.foo.bar.model.db.ApplicationPageElementsKey">
<key-property name="pageId" column="page_id"/>
<key-property name="elementId" column="element_id"/>
</composite-id>
<property name="weight" type="java.lang.Long">
<column name="WEIGHT" precision="0" />
</property>
<many-to-one name="elements" class="ApplicationElements"
column="element_id" not-null="true" insert="false" update="false"/>
<many-to-one name="page" class="ApplicationPage"
column="page_id" not-null="true" insert="false" update="false"/>
</class>
Note that in the above many-to-one mapping, insert and update attributes are set to false. This is necessary because the columns are mapped twice, once in the composite key (which is responsible for insertion of the values) and again for the many-to-one associations.
The above use case is mentioned in detail in the book: Java Persistence with Hibernate.

Get entries in intersection table based on key

I have three tables:
offers; offer_groups; offer_group_members.
The offers and offer_groups tables are mapped with hibernate (see below).
In offer_group_members, I store to which group the offers belong (offer primary key, offer group primary key).
I am kinda new to hibernate so my question is: How can I get all the OfferGroups from the OFFER_GROUP_MEMBERS table based on the Offer key?
I tried something like this:
Criteria crit;
crit = getSession().createCriteria(Offer.class);
crit = crit.createCriteria("offerGroups");
crit.add(eq("key", offerKey));
Here are the mappings:
for offer:
<composite-id name="comp_id"
class="com.infonova.psm.hibernate.prodsrv.OfferPK">
<key-property name="key" column="KEY"
type="java.lang.String" length="128">
</key-property>
</composite-id>
for offer_group_key:
<id name="key" type="java.lang.String" column="KEY" length="128">
<generator class="assigned"/>
</id>`
for offer_group_key:
<set name="offers" table="OFFER_GROUP_MEMBERS" lazy="true" inverse="false"
cascade="none">
<key>
<column name="OFFER_GROUP_KEY"/>
</key>
<many-to-many class="Offer">
<column name="OFFER_KEY"/>
</many-to-many>
</set>
for offer:
<set name="offerGroups" table="OFFER_GROUP_MEMBERS"
inverse="true" lazy="true" cascade="none">
<key>
<column name="OFFER_KEY" />
</key>
<many-to-many
class="OfferGroup">
<column name="OFFER_GROUP_KEY" />
</many-to-many>
</set>
It would be easier if you showed us the entities, since it's on them that HQL and criteria queries work.
Anyway, in HQL:
select og from Offer o
inner join o.offerGroups og
where o.key = :key
And in Criteria, unfortunately, IIRC, all you can do is select the root entity or scalars, so it's hard to do this without having a bidirectionall association. If you had a bidirectional association, you could do
Criteria c = session.createCriteria(OfferGroup.class, "og");
c.createAlias("og.offers", "o");
c.add(Restrictions.eq("o.key", key));
Since you don't have the bidirectional association, the only way that I know of is to do this:
Criteria c = session.createCriteria(OfferGroup.class, "og");
DetachedCriteria dc = DetachedCriteria.forClass(Offer.class, "o");
dc.createAlias("o.offerGroups", "og2");
dc.add(Restrictions.eq("o.key", key));
dc.setProjection(Projections.property("og2.id"));
c.add(Subqueries.propertyIn("og.id", dc));
which corresponds to this ugly HQL query:
select og from OggerGroup og
where og.id in (select og2.id from Offer o
inner join o.offerGroups og2
where o.key = :key)
For such simple static queries, I don't see any reason to go with Criteria rather than HQL.

hibernate many to many relations cascade

Newbie to hibernate i have two tables A and B that have many to many relations defined by a table AB(A_ID and B_ID) with foreign key reference to A.A_ID and B.B_ID and cascade on delete and update defined.
I have mapped
a.hbm.xml has
<set name="bSet" table="AB" inverse="true" lazy="false" fetch="select" cascade="all">
<key>
<column name="A_ID" not-null="true" />
</key>
<many-to-many class="objectB" >
<column name="B_ID" not-null="true" />
</many-to-many>
</set>
b.hbm.xml has
<set name="aSet" table="AB" inverse="false" lazy="false" fetch="select" cascade="all">
<key>
<column name="B_ID" not-null="true" />
</key>
<many-to-many class="objectA">
<column name="A_ID" not-null="true" />
</many-to-many>
</set>
//ObjectA.java has
private Set<ObjectB> bSet = new HashSet<objectB>(0);
//ObjectB.java has
private Set<ObjectA> aSet = new HashSet<objectA>(0);
From the front end sends the A object as a json with set of B's the table A is getting updated correctly while the AB is untouched.
Can someone point out where am I going wrong?
Here is the JSON
{
"a_field1": "value1",
"a_field2": "value2",
"aId": 1,
"bSet": [
{
"bId": 100
},
{
"bId": 200
}
],
"a_field3": "value3"
}
initially the db is set up with 3 records in AB table
(1,100)
(1,200)
(1,300)
The final results in the db should have been
(1,100)
(1,200)
the last row (1,300) should have been deleted.
Any help is greatly appreciated.
Shah
My best guess (you don't provide any example of the server code that handles the request) is that you're only updating one side of the bidirectional association. In other words you're just deserializing that A instance and doing a merge. If you get a new A you still need to merge the A instance but you also need to load up all the B's that A no longer references and remove the A instance from their list and also look up all the B's that are newly referenced by A and add A to their list. It's one of the hazards of a bidirectional relationship in code.

Hibernate Cross Reference many-to-many Parent/Child Mapping Of Same Class

I'm having a major problem getting a parent/child relationship working for a hierarchy of a single class. Basically I have to represent a server tree like thus:
Server A
Server B
Server C
Server D
Server E
Server C
Server D
Server F
Server G
Note that the children of Servers B & E are the same. My original mapping was something like this, which was fine until I needed to have the objects for server C & D being the same instance, so having a single column for PARENT_ID got filled by the last relationship and only one of servers B or E would show the children:
<hibernate-mapping ...>
<class name="Server" ...>
...
<set name="children" cascade="all-delete-orphan" lazy="false">
<key column="PARENT_ID" />
<one-to-many class="Server" />
</set>
</class>
</hibernate-mapping>
I know I need to do some sort of cross reference table to map the fact that a server can have multiple parents, but all the examples I've found on-line contain a separate parent and child class.
Can anyone tell me how to do a cross reference parent/child mapping for the same class...? I.e. something like:
<hibernate-mapping ...>
<class name="Server" ...>
...
<set name="children" cascade="all-delete-orphan" lazy="false">
<key>
<column name="PARENT_ID" />
<column name="CHILD_ID" />
</key>
<many-to-many class="Server">
<column name="???" />
<formula>???</formula>
</many-to-many>
</set>
</class>
</hibernate-mapping>
Thanks,
Bob.
In your many-to-many mapping, set the column name to be CHILD_ID.
<many-to-many class="Server">
<column name="CHILD_ID" />
</many-to-many>
This will cause the relationship to view the child id as the id representing itself. While the one-to-many relationship will use the parent_id as the id representing itself. Should work, I haven't ran it, but I have done a similar thing before.
Doing the following:
...
<set name="children" table="SERVER_XREF" cascade="all-delete-orphan" lazy="false">
<key column="PARENT_ID" />
<many-to-many class="Server" column="CHILD_ID" />
</set>
...
appears to have resulted in the server hierarchy I was after being returned.
Cheers,

Categories