Hibernate Child table updating Parent Object - java

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.

Related

Hibernate: Several tables of one entity -> inherit hibernate-mapping

I have an entity from which i want to have several tables in my sql database.
For Example, I have the Java Class in which i have an collection of another Java Class
#Entity
class SqlEntity{
#Id
#Column(unique = true)
private Date date = null;
#Column
#OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL, CascadeType.PERSIST,
CascadeType.MERGE }, mappedBy = "CollectionData")
private Collection<CollectionData> collectionData = new ArrayList<>();
#Column(name="columNameX")
int attributeX;
#Column(name="columNamey")
int attributeY;
...
}
And i want different Data in different Tables according to where the Data are from:
SQL_ENTITY_GERMANY,
SQL_ENTITY_USA,
SQL_ENTITY_UK,
...
I was able to accomplish this by writing an xml-mapping for every table(before that I had only java annotations to map the entity).
But I had to write an complete mapping for every table like this:
<hibernate-mapping>
<class name="...SqlEntity" table="SQL_ENTITY_GERMANY"
entity-name="SQL_ENTITY_GERMANY">
<id name="date" type="date" column="date">
</id>
<property name="columnNameX" column="attributeX" type="int" />
<property name="columnNameY" column="attributeY" type="int" />
...
<bag name="collectionData" cascade="all">
<key column="date" />
<one-to-many class="COLLECTION_DATA_GERMANY" />
</bag>
</class>
<class name="...collectionData" table="COLLECTION_DATA_GERMANY"
entity-name="COLLECTION_DATA_GERMANY">
<id name="id" type="long" column="id">
<generator class="native" />
</id>
<property name="columnNameX" column="attributeX" type="int" />
<property name="columnNameY" column="attributeY" type="int" />
...
<many-to-one name="collectionData" class="SQL_ENTITY_GERMANY"
fetch="select">
<column name="date" not-null="true" />
</many-to-one>
</class>
</hibernate-mapping>
So if I want to change anything in one entity class (like adding members), i have to change it in every single xml-mapping, too.
So i thought it maybe possible to just inheritate the mapping, so that the annotations of the class are still guilty as long as i dont overwrite it.
I tried to google and search on stackoverflow on this topic, but i found only things about java classes inheritance.
Or is there another solution which could help me with this?
Since your database is non-normalized, the only way of achieving what you want is creating an abstract class and several concrete subclasses with appropriate country codes.
If you are able to alter a schema, I'd suggest adding a column COUNTRY_CODE VARCHAR(2) NULL to your SQL_ENTITY table, insert all data from your SQL_ENTITY_* tables.
You can do so by:
1)Creating a new SQL_ENTITY table
CREATE TABLE NEW_SQL_ENTITY (
id_entity INTEGER NOT NULL AUTO_INCREMENT;
value1 VARCHAR(100) NOT NULL;
value2 VARCHAR(100) NOT NULL;
country_code VARCHAR(2) NULL;
)
2) Inserting the data from the other tables:
INSERT INTO NEW_SQL_ENTITY (value1,value2,country_code) VALUES
(SELECT value1,value2, 'UK' from SQL_ENTITY_UK)
3) Repeating for all SQL_ENTITY tables

Hibernate-Foreign key object Null on Select Query

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();

When does Hibernate create a new entry from a FK to a non-PK column?

suppose I have:
-- table_1 -------------------
|id | property_x | data |
------------------------------
-- table_2 ------------------------
|id | property_x | moar_data |
-----------------------------------
And that table_1 has a FK from property_x to table_2.property_x
(why not table_2.id? IDK, this project was already like this :( )
The HBMs look like this:
<!-- Table 1 -->
<hibernate-mapping package="com.example">
<class name="Table1" table="table_1">
<id column="id" type="integer" name="id">
<generator class="native"/>
</id>
<many-to-one class="Table2" fetch="join" name="table2" not-null="false">
<column name="property_x" />
</many-to-one>
</class>
</hibernate-mapping>
and
<!-- Table 2 -->
<hibernate-mapping package="com.example">
<class name="Table2" table="table_2">
<id column="id" type="integer" name="id">
<generator class="native"/>
</id>
<property column="property_x" name="propertyX" type="string"/>
</class>
</hibernate-mapping>
The thing is that I want to save an object Table1 using session.save(objTable1), without creating a new Table2 entry in the DB, and without loading it either.
I have done this in the past by creating a new object, and only setting the primary key values, leaving everything else blank, and not letting it update the Table2 table, but this is not the PK >_<.
Now, suppose I have in table_2 an entry with id=5, property_x="EX". When I do the following...
// example method
public void saveSomething(Sessions sess) {
Table1 table1 = new Table1();
Table2 table2 = new Table2();
table2.setPropertyX("EX");
table1.setTable2(table2);
sess.save(table1);
}
... it creates a new entry, I'm guessing it's because the PK (id) for Table2 is not set.
My question is, how does hibernate decide to create a new entry in the DB from a FK object?, and is there a way to avoid that on the HBM files?
It's the cascade anotation, if you dont want table 2 to be created in the db, dont set it as a child, but if you want table 2 to be updated, you need the table 2 id.
Alternatively, you can set cascade="none" in the hbm of table 1 or cascade="update", but table 2 still wont be updated without the id.

How to Stop Hibernate From Trying to Update one-to-many Over Join Table

Ok so I'm having bit of a problem with my Hibernate mappings and getting the desired behavior.
Basically what I have is the following Hibernate mapping:
<hibernate-mapping>
<class name="com.package.Person" table="PERSON" schema="MYSCHEMA" lazy="false">
<id name="personId" column="PERSON_ID" type="java.lang.Long">
<generator class="sequence">
<param name="sequence">PERSON_ID_SEQ</param>
</generator>
</id>
<property name="firstName" type="string" column="FIRST_NAME">
<property name="lastName" type="string" column="LAST_NAME">
<property name="age" type="int" column="AGE">
<set name="skills" table="PERSON_SKILL" cascade="all-delete-orphan">
<key>
<column name="PERSON_ID" precision="12" scale="0" not-null="true"/>
</key>
<many-to-many column="SKILL_ID" unique="true" class="com.package.Skill"/>
</set>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="com.package.Skill" table="SKILL" schema="MYSCHEMA">
<id name="skillId" column="SKILL_ID" type="java.lang.Long">
<generator class="sequence">
<param name="sequence">SKILL_ID_SEQ</param>
</generator>
</id>
<property name="description" type="string" column="DESCRIPTION">
</class>
</hibernate-mapping>
So lets assume that I have already populated the Skill table with some skills in it. Now when I create a new Person I want to associate them with a set of skills that already exist in the skill table by just setting the ID of the skill. For example:
Person p = new Person();
p.setFirstName("John");
p.setLastName("Doe");
p.setAge(55);
//Skill with id=2 is already in the skill table
Skill s = new Skill()
s.setSkillId(2L);
p.setSkills(new HashSet<Skill>(Arrays.asList(s)));
PersonDao.saveOrUpdate(p);
If I try to do that however I get an error saying:
WARN (org.slf4j.impl.JCLLoggerAdapter:357) - SQL Error: 1407, SQLState: 72000
ERROR (org.slf4j.impl.JCLLoggerAdapter:454) - ORA-01407: cannot update ("MYSCHEMA"."SKILL"."DESCRIPTION") to NULL
ERROR (org.slf4j.impl.JCLLoggerAdapter:532) - Could not synchronize database state with session
org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
The reason I am getting this error I think is because Hibernate sees that the Skill with Id 2 has 'updated' its description to null (since I never set it) and tries to update it. But I don't want Hibernate to update this. What I want it to do is insert the new Person p and insert a record into the join table, PERSON_SKILL, that matches p with the skill in the SKILL table with id=2 without touching the SKILL table.
Is there anyway to achieve this behavior?
Instead of creating the Skill object yourself:
//Skill with id=2 is already in the skill table
Skill s = new Skill()
s.setSkillId(2L);
p.setSkills(new HashSet<Skill>(Arrays.asList(s)));
You should be retrieving it from the Hibernate Session:
Skill s = (Skill) session.get(Skill.class, 2L);
p.setSkills(new HashSet<Skill>(Arrays.asList(s)));
This way the Session thinks that the skill contained in p.skills is persistent, and not transient.
This may be possible if you don't cascade all-delete-orphan which is explicitely telling hibernate to cascade the changes.
But the right way would be IMO to load load the desired Skill entity from the database and to add it to the set of skills of the Person.

Legacy mapping with hibernate

For my current project I have to map a legacy database using hibernate, but I'm running into some problems.
The database is setup using one 'entity' table, which contains common properties for all domain objects. Properties include (among others) creation date, owner (user), and a primary key which is subsequently used in the tables for the domain objects.
A simple representation of the context is as such:
table entity
- int id
- varchar owner
table account
- int accountid (references entity.id)
table contact
- int contactid (references entity.id)
- int accountid (references account.accountid)
My problem exhibits itself when I try to add a collection mapping to my account mapping, containing all contacts belonging to the account. My attempts boil down to the following:
<hibernate-mapping>
<class name="Contact" table="entity">
<id name="id" column="id">
<generator class="native" />
</id>
<join table="contact">
<key column="contactid"/>
<!-- more stuff -->
</join>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="Account" table="entity">
<id name="id" column="id">
<generator class="native" />
</id>
<bag name="contacts" table="contact">
<key column="accountid" />
<one-to-many class="Contact"/>
</bag>
<join table="account">
<key column="accountid"/>
<!-- more stuff -->
</join>
</class>
</hibernate-mapping>
However, when I try to fetch the account I get an SQL error, stating that the entity table does not contain a column called accountid. I see why this is happening: the mapping tries to find the accountid column in the entity table, when I want it to look in the contact table. Am I missing something obvious here, or should I approach this problem from another direction?
This looks to me like you actually need to be mapping an inheritance, using the Table Per Subclass paradigm.
Something like this:
<class name="entity" table="entity">
<id name="id" column="id">
...
</id>
<joined-subclass name="contact" table="contact">
<key column="contactid"/>
</joined-subclass>
<joined-subclass name="account" table="account">
<key column="accountid"/>
</joined-subclass>
</class>
That's approximate by the way - it's described in detail in section 9.1.2 of the Hibernate documentation (just in case you can't find it, it's called "Table per subclass").
Cheers
Rich

Categories