Hibernate-Foreign key object Null on Select Query - java

I have two tables ex:User and Role. with many-to-one relation between User and Role (one User can contain many Roles)
I used SQL query to insert the data in to the User table with the role_id(assume pk of Role table) as foreign key.
Now,when I tried to fetch the records of User with a particular role.
i am using following code to fetch the User object.
User user=(User)session.get('User.class',user_id);
Role role=user.getRole();
On executing the above lines,some times I'm getting the User Object,some times not.
The relation mapping between the objects is as below.
<many-to-one name="role" class="com.example.Role" fetch="select">
<column name="role_id" precision="14" scale="0" />
</many-to-one>
<set name="user" cascade="delete" table="User" >
<key>
<column name="role_id" not-null="true" />
</key>
<one-to-many class="com.example.User" />
</set>
Is there any way to prevent it occuring?
Is this possible that some times the select query will give me output and sometimes null.

There seem to be a fundamental mistake in your design. You stated that one User can contain Many Roles. This means foreign key (which points the PK of User) should be in Roles. But you seem to have put the foreign key in User.

Aside from DUKE answer who clearly pointed that your mapping says that a Role has many users as opposed to your requirement, there are still some issues with your code:
First you need to add inverse="true" to your one-to-many side. Since you have a bi-directional association, only one side can own the relationship:
<set name="user" cascade="delete" table="User" inverse="true">
<key>
<column name="role_id" not-null="true" />
</key>
<one-to-many class="com.example.User" />
</set>
And then it's much more efficient to fetch the Role in the same query with the User:
User user = (User)
session.createQuery(
"select u from User left join fetch u.role where u.id = :user_id')
.setParameter("user_id", user_id)
.uniqueResult();

Related

Cascade=delete doesn't delete related entities

I do have 2 entities:
1. User
2. Limits
User has a set of limits, so one-to-many relation is present. I do have a cascade type set to delete, so when User is deleted, all corresponding limits should be deleted as well.
<bag name="limits" cascade="delete">
<key column="USER_ID"/>
<one-to-many class="com.mac.kom.modules.limits.models.LimitsModel" />
</bag>
When I try to delete the User I do get an error
Caused by: java.sql.SQLException: ORA-01407: cannot update ("DB"."LIMITS"."USER_ID") to NULL
Any clues?
You should cascade delete-orphan.
delete tries to break the link between the both entities by clearing the FK field.
delete-orphan won't authorize having some LimitsModel without User and thus will delete the related LimitsModel record.
try this
<bag name="limits">
<key column="USER_ID" on-delete="cascade"/>
<one-to-many class="com.mac.kom.modules.limits.models.LimitsModel" />
</bag>

Hibernate Child table updating Parent Object

I have table like below,
Role:
Role_Id Role_Name
201 Manager
202 Supervisor
User:
User_ID User_Name Role_Id
1 John 201
2 Peter 202
3 Raj 202
I have created POJO and hibernate mapping file for both tables.
Mapping File:
//RoleVO
<class name="RoleVO" table="tbl_Role">
<id name="roleId" column="role_Id" >
<generator class="identity"/>
</id>
<property name="roleName" column="Role_Name" />
</class>
//UserVO
<class name="UserVO" table="tbl_User">
<id name="userId" column="user_Id" >
<generator class="identity"/>
</id>
<property name="userName" column="User_Name" />
<many-to-one name="Role" class="RoleVO" column="Role_Id"/>
</class>
When I insert user detail in User Table through hibernate, should I get the Role Ref and set it with User object or directly can I add "Role Id" in the User table.
For eg.,
//First approach:
UserVO usr = new UserVO();
usr.serUserName("Kumar");
usr.setRoleId(202);
session.save(usr);
or
//Second Approach:
UserVO usr = new UserVO();
usr.serUserName("Kumar");
RoleVO role = session.get(RoleVO.class, 202);
usr.setRole(role);
session.save(usr);
Which approach is best? First or Second?
I assume that the User-Role association is Many-to-One and unidirectional association.
Set RoleVO object in UserVO instance is the only approach to use for inserting UserVo object in your database using hibernate as a mapping file indicate:
<many-to-one name="Role" class="RoleVO" column="Role_Id"/>
So to insert user detail object in your database, you may write:
UserVO user = new UserVO();
//Set user info
...
RoleVO role = (RoleVO) session.get(RoleVO.class, 202);
user.setRole(role);
session.save(user);
I suggest that you check this hibernate tutorial for further information.

Hibernate performs delete cascade in wrong order

My Db is throwing a Constraing violation (FK) because Hibernate performs a cascade delete in the wrong order.
Details:
I delete a Member, that has a Wallet with Wallet transactions (value-type), and the wallet transaction has an association to a product just like the Member contains collection of products (see the Hibernate mapping below).
I delete a Member instance, and want Hibernate to remove both the products and Wallet transactions. It seems that it first removes the product instances (through cascading), such that a FK violation is thrown by the DB, as it's still referenced by a Wallet transaction, that wasn't removed yet (through cascade)
I played around with the cascade setting, like all-delete-orphan (on the products), etc..., but no luck
I also emptied the wallet transactions and flushed the hibernate session in the same removal transaction, but also the same error.
Please some understanding and help to get the order of cascade deletion correct?
The hibernate mapping (I left out the none-important parts like PK and properties):
<class name="Member" table="mem" >
<component name="wallet" class="Wallet">
<set name="transactions" table="wallet_tx" cascade="all">
<cache usage="read-write" />
<key column="idTaxer" not-null="true" />
<composite-element class="WalletTransaction">
<property name="amount" type="big_decimal" column="amount" />
<many-to-one name="product" column="idPrdInst" class="Product" cascade="none" />
</composite-element>
</set>
</component>
<set name="products" cascade="delete" inverse="true">
<key column="idTaxer" not-null="true" />
<one-to-many class="Product" />
</set>
</class>
<class name="Product" table="prd" >
...
<many-to-one name="member" column="idMember" class="Member" cascade="none" />
</class>
The DB error:
ERROR: update or delete on table "prd" violates foreign key constraint "fk_1umej7" on table "wallet_tx"
DETAIL: Key (id)=(75bef42fc4544) is still referenced from table "wallet_tx".
If you empty the wallet transaction and expect it to be deleted you must set delete-orphan on the transactions relationship.
If product is still deleted 1st, you can flush after removing the wallet tx from the transaction set, it will work but it's surely not the state-of-art way.
Otherwise you could try to map the product - transaction oneToMany relationship and set a delete cascade on it (with inverse), the product deletion will so first trigger the transaction deletion.
Instead of:
<set name="transactions" table="wallet_tx" cascade="all">
you should have:
<set name="transactions" table="wallet_tx" cascade="all-delete-orphan">
And simply clear the transactions Set prior to deleting the Member parent entity.

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.

Categories