I have the following relationship: Form --[order]-- Attribute.
One form has n attributes and an attribute can belong to n forms. The relation requires to save an extra property : order.
So I have a class form, a class attribute and a class form_attribute like this:
public class FormAttribute{
Form form;
Attribute attribute;
int attrOrder;
// getters/setters
}
On the Form mapping I am mapping like this:
<set name="attributes" table="form_attribute" inverse="true" cascade="all-delete-orphan" order-by="attrOrder" >
<key column="form" not-null="true"/>
<composite-element class="FormAttribute" >
<many-to-one name="attribute" column="attribute" class="ExamAttribute" not-null="true" cascade="save-update" />
<property name="attrOrder" column="attr_order" type="integer" not-null="true" />
</composite-element>
</set>
when I create a new form and associate attributes I expect when, I save, to see the form_attribute table populated. But only form and attribute table is. Is it normal? Do I have to manually update the form_attribute relationship?
So, finally it was a bit of a stupid issue. If I do not save attribute first and since there is no set on the attribute side to manage the link between form and attribute, the composite element was not saved. Saving the new attributes first, then saving the form fixed my issue.
Related
I have a legacy hibernate application (using hibernate 3.2.6.ga) where we fetch information similar to the structure below. We have a person who contains several other attributes like address details or bank accounts or transactions.
Person
List of Address Details
List of bank accounts
List of transactions
and many more...
With a hibernate mapping file like this:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="myPkg.Person" table="PERSON" schema="WhatEver" lazy="false">
<id name="personId" type="java.lang.Long">
<column name="PERSON_ID" precision="1" />
<generator class="sequence">
<param name="sequence">some_sequence_generator</param>
</generator>
</id>
<!-- lazy init is true as only some methods is interested in addressDetails-->
<bag name="addressDetails" inverse="true" cascade="all" lazy="true">
<meta attribute="propertyType">java.util.LinkedList</meta>
<key>
<column name="PERSON_ID" precision="10" not-null="true" />
</key>
<one-to-many class="myPkg.Address" />
</bag>
...
<!-- lazy init is true as a person can have billion transactions -->
<bag name="transactionDetails" inverse="true" cascade="all" lazy="true">
<meta attribute="propertyType">java.util.LinkedList</meta>
<key>
<column name="PERSON_ID" precision="10" not-null="true" />
</key>
<one-to-many class="myPkg.transaction" />
</bag>
</class>
</hibernate-mapping>
We have several methods to retrieve the persons. Some requires that the child related data be returned as well and some not. So all in the mapping file is set to lazy=”true”.
In cases where we are interested in the children right after we return the data we do a Hibernate.initialize(personObjectJustReturned) to go and fetch the children as well:
Person personInstance = (Person) getHibernateTemplate().get(Person.class, new Long(id));
Hibernate.initialize(personInstance);
But the problem comes in with the transactions list as this can become a very large set of data - where a person can have billions of transactions.
What I would like to do is never return transactions with the person object at all.
So I can do one of 2 things:
Simply make everyting eagerly loaded in my XML file by setting lazy=”false” (apart from my transactions) and don’t do a Hibernate.initialize(personObjectJustReturned). But that would mean that any other method returning data will eagerly fetch the child data as well as all methods in the app is using the same hibernate mapping. Which is not ideal as for example not all methods requires address details. So I will be fetching more data than required on a global scale which is bad practice.
Remove the transactions field relationship from my config file so hibernate will not even know about the transactions field when it retrieving a person. This will work perfectly but in cases where we save a person object and that person has newly generated transactions we will have to save the transactions separately. Aka we cannot simply tell hibernate go and save the person object which automatically saves the newly generated transactions as hibernate don't know about the relationship anymore.
Is there a better way to do this?
The first option you show can lead to memory consumption problems if you have a lot of children per object.
The best option then, is to load the children fetched by the parent object:
SELECT * FROM transaction_detail WHERE person = ?
This way you avoid the Hibernate.initialize(personObjectJustReturned) and fetch the details more precisely (sorted or more filtered).
Then, to save a new transactionDetail, the TransactionDetail object must have the Person object set (as the parent) and when saving (the save will be done at the TransactionDetail), no error should happen (the foreign keys will avoid orphans).
I'm using Hibernate mapping to config stages in my application. It's configured that the necessary class (here called Configurator) is injected with the stages.
I have one list containing Object Score:
private List<Score>dsScore = new ArrayList<Score>(0);
I have a database containing table Score and a list in class Student and student.hbm.xml as:
`<list name ="dsScore" table="SCORE">
<key>
<column name="SCORE_ID" not-null="true"></column>
</key>
<one-to-many class="model.Score"/>
</list>`
into a file config Student containing it, it not work.
Can anybody help me with this?
list hibernate type is collection with persisted ordering. It
requires explicit mapped column in underlying table for ordering, so
your mapping is missing <index>/<list-index> element. OR you
should use bag (maybe with order-by attribute) to get collection
mapped to java List without persisting order. See docs
Use package
attribute on <hibernate-mapping> instead of qualified class name.
I assume that both Student and Score classes are in model package. The mapping then should look like
<hibernate-mapping package="model">
<class name="Student" table="...">
...
<bag name="dsScore" table="Score" order-by="...">
<key column="SCORE_ID" />
<one-to-many class="Score" />
</bag>
</class>
</hibernate-mapping>
There are 3 pojo namely EmployeeMaster (the parent class), Hr and Personal (the child class of EmployeeMaster).
Then I have 3 separate tables for each pojo, constructed as :
create table emp_master(emp_code integer,emp_name text,emp_desig text,
emp_dept text,primary key(emp_code));
create table hr(emp_code integer,salary integer,hra integer,da integer,
taxes integer,grade text,foreign key(emp_code) references emp_master(emp_code));
create table personal(emp_code integer,address text,married bool,
foreign key(emp_code) references emp_master(emp_code));
emp_code is the primary key for emp_master and it is the foreign key for both hr and personal table.
I constructed 3 separate jsp forms to take in the data.
Following is the hibernate mapping file :
<hibernate-mapping>
<class name="pojo.EmployeeMaster" table="emp_master">
<id name="emp_code">
<generator class="assigned" />
</id>
<property name="emp_dept" />
<property name="emp_desig" />
<property name="emp_name" />
<joined-subclass name="pojo.Hr" table="hr">
<key column="emp_code" />
<property name="da" />
<property name="grade" />
<property name="hra" />
<property name="salary" />
<property name="taxes" />
</joined-subclass>
<joined-subclass name="pojo.Personal" table="personal">
<key column="emp_code" />
<property name="address" />
<property name="married" />
</joined-subclass>
</class>
</hibernate-mapping>
Now the problem is,I do not want any null row and want to submit data for the same emp_code into other tables namely hr and personal.
But as I try to submit data into the hr table for emp_code 101, that already exists in the master table I get an error saying Duplicate entry '101' for key 'PRIMARY'.
If I change the generator class to increment in the mapping xml, I get a row with null values,for the data inserted from the child class.What I want is an entry into the three tables with the same employee code. How do I do this ?
Your hibernate mapping suggests that you EmployeeMaster, HR and personal forms a hierarchy, i.e. EmployeeMaster is parent class, while HR and Personal are child classes.
There is something wrong in what you want to achieve and what your mapping suggesting. In above mapping, an ID can be mapped to either HR or to Personal and not both.
You should consider changing the mapping from INHERITANCE to ASSOCIATION.
As per your mapping xml, your java classes should be defined as follows:
public class EmployeeMaster {}
public class HR extends EmployeeMaster{}
public class Personal extends EmployeeMaster{}
Now, you have to device how you can fit your requirements into above model. If that is not the case, then you have no choice but to change the model (xml mapping as well)
Hibernate mapping question where the behavior is ambiguous and/or dangerous. I have a one-to-many relationship that has a cascade-delete-orphan condition AND a where condition to limit the items in the collection. Mapping here -
<hibernate-mapping>
<class name="User" table="user" >
<!-- properties and id ... -->
<set table="email" inverse="true" cascade="all,delete-orphan" where="deleted!=true">
<key column="user_id">
<one-to-many class="Email"/>
</set>
</class>
</hibernate-mapping>
Now suppose that that I have a User object which is associated to one or more Email objects, at least one of which has a 'true' value for the deleted property. Which of the following two will happen when I call session.delete() on the User object?
The User and all the Email objects, including those with deleted=true, are deleted
The User and the Email objects that are deleted!=null are deleted.
On one hand, scenario 1) ignores the where condition, which may not be correct according to the domain model. BUT in scenario 2) if the parent is deleted, and there's a foreign key constraint on the child (email) table's join key, then the delete command will fail. Which happens and why? Is this just another example of how Hibernate's features can be ambiguous?
I didn't test the mapping but in my opinion, the correct (default) behavior should be to ignore the where condition and to delete all the child records (that's the only option to avoid FK constraints violations when deleting the parent). That's maybe not "correct" from a business point of view but the other option is not "correct" either as it just doesn't work.
To sum up, the mapping itself looks incoherent. You should either not cascade the delete operation (and handle the deletion of the child Email manually).
Or, and I think that this might be the most correct behavior, you should implement a soft delete of both the User and associated Email. Something like this:
<hibernate-mapping>
<class name="User" table="user" where="deleted<>'1'">
<!-- properties and id ... -->
<set table="email" inverse="true" cascade="all,delete-orphan" where="deleted<>'1'">
<key column="user_id">
<one-to-many class="Email"/>
</set>
<sql-delete>UPDATE user SET deleted = '1' WHERE id = ?</sql-delete>
</class>
<class name="Email" table="email" where="deleted<>'1'">
<!-- properties and id ... -->
<sql-delete>UPDATE email SET deleted = '1' WHERE id = ?</sql-delete>
</class>
</hibernate-mapping>
What is done here:
We override the default delete using sql-delete to update a flag instead of a real delete (the soft delete).
We filter the entities and the association(s) using the where to only fetch entities that haven't been soft deleted.
This is inspired by Soft deletes using Hibernate annotations. Not tested though.
References
5.1.3. Class
6.2. Collection mappings
16.3. Custom SQL for create, update and delete
I've created a UserObject and RoleObject to represent users in my application. I'm trying to use hibernate for CRUD instead of raw JDBC. I've successfully retrieved the information from the data base, but I can not create new users. I get the following error.
org.springframework.web.util.NestedServletException: Request processing failed; nested
exception is org.springframework.dao.DataIntegrityViolationException: could not insert:
[com.dc.data.UserRole]; nested exception is
org.hibernate.exception.ConstraintViolationException: could not insert:
[com.dc.data.UserRole]
My data base is defined as follows:
Users Table, Authority Table and Authorities table. Authorities table is a join of users and authority.
My hibernate mapping for UserObjec is as follows:
...
<set name="roles" table="authorities" cascade="save-update" lazy="false" >
<key column="userId" />
<one-to-many class="com.dc.data.UserRole"/>
<many-to-many class="com.dc.data.UserRole" column="authId" />
</set>
</class>
...
UserRole is mapped as follows:
<class name="com.dc.data.UserRole" table="authority">
<id name="id" column="authId">
<generator class="native" />
</id>
<property name="roleName">
<column name="authority" length="60" not-null="true" />
</property>
</class>
How do I need to change my mapping or Object structure to be able to persist new users?
You are defining two different relationships inside of your "set" element. What you probably want is just the many-to-many element.
If this still doesn't work, try saving the UserRole itself to see if you can persist it on its own. If you can, then the ConstraintViolationException is being thrown while trying to persist User.
Last tip, you probably don't want to cascade save/update on the "roles" Set. In all likelihood your UserRoles will already be in the DB and simply be attached to the Users as they get created.
The contraint violation on UserRole might be a cause of trying to insert a row with a duplicate key. Maybe experiment with using other types of generators, such as "sequence".