Persisting Users using Hibernate - java

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".

Related

Hibernate don't retrieve children but still persist if present?

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).

Hibernate composite element mapping

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.

Hibernate one-to-many + lazy loading

I have an Account entity and an AccountTransaction entity.
Account 1 <----> n AccountTransaction
In my AccountTransaction.hbm.xml I specify a many-to-one relationship:
<hibernate-mapping>
<class name="com.walshk.accountmanager.domain.AccountTransaction" table="AccountTransaction">
<id name="id" type="long" column="transaction_id">
<generator class="increment"/>
</id>
<property name="date" not-null="true" type="date" column="transaction_date"/>
<property name="description" not-null="true" column="transaction_description" length="500"/>
<property name="amount" column="transaction_amount" not-null="true"/>
<many-to-one name="account" column="account_id" not-null="true" cascade="all" lazy="false"/>
</class>
</hibernate-mapping>
This allows me to lookup AccountTransactions by account using
Criteria criteria = session.createCriteria(AccountTransaction.class)
.add(Restrictions.eq("account", account));
and also allows me to get the Account instance using AccountTransaction#getAccount();
What I want to do now is provide a way to get an account, e.g
Criteria criteria = session.createCriteria(Account.class).add(Restrictions.eq("id", id));
But I also want the Account entity to have a method
List<AccountTransaction> getTransactions();
And I want this to be lazy loaded, since I may not even need to list the transactions.
Since I am already specifying the relationship as many-to-one from the AccountTransaction how do I now specify a one-to-many relationship giving me access from the other direction.
Additionally, what's the best way to handle lazy-loading, do I have to assign a session to each entity and not close the session? I could potentially have too many sessions open though.
Thanks.
If you add a One-to-Many association in your Account class hibernate mapping, you will get:
List<AccountTransaction> getTransactions();
from any ORM creation tool.
One of the parameters of this association is the loading type - I am not familiar with the exact syntax in XML mapping, as we use annotations, but you could probably find it in any reference/documentation page of hibernate XML mapping.
in Order to work with Lazy Loading, You should have Open Session in view enabled.
If you are using Spring integration that you have OpenSesionInViewIntereptor/OpenSessionInViewFilter
If you are using native Hibernate without Spring integration, then you can implement it by yourself.
Please read the following:
http://community.jboss.org/wiki/OpenSessioninView
Hope it helps.

hibernate eagerly load an association that's normally lazy

I have these 2 mappings:
<hibernate-mapping>
<class name="sample.Operator" table="OPERATOR">
<id name="id" >
<generator class="native" />
</id>
<property name="name" not-null="true">
<column name="NAME" />
</property>
<set name="pointCodes" inverse="false" lazy="true" cascade="save-update">
<key>
<column name="OperatorID" />
</key>
<one-to-many class="sample.PointCode" />
</set>
</class>
<hibernate-mapping>
<class name="sample.PointCode" table="POINTCODE">
<id name="id">
<generator class="native" />
</id>
<properties name="pointCodeKey" unique="true">
<property name="pointCode" not-null="true">
</property>
<property name="networkIndicator" not-null="true">
</property>
</properties>
<property name="name" not-null="true">
</property>
</class>
</hibernate-mapping>
Most of the time when featching an Operator I want the pointCodes to be fetched lazily, so I don't want to set lazy="false" in the mappings,
However I have one query, e.g. session.createQuery("from Operator").list() where I do want the pointCodes association to NOT be fetched lazily - how do I do that ?
The hibernate reference manual writes:
A "fetch" join allows associations or
collections of values to be
initialized along with their parent
objects using a single select. This is
particularly useful in the case of a
collection. It effectively overrides
the outer join and lazy declarations
of the mapping file for associations
and collections. See Section 20.1,
“Fetching strategies” for more
information.
from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens
A fetch join does not usually need to
assign an alias, because the
associated objects should not be used
in the where clause (or any other
clause). The associated objects are
also not returned directly in the
query results. Instead, they may be
accessed via the parent object. The
only reason you might need an alias is
if you are recursively join fetching a
further collection:
from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens child
left join fetch child.kittens
The fetch construct cannot be used in
queries called using iterate() (though
scroll() can be used). Fetch should be
used together with setMaxResults() or
setFirstResult(), as these operations
are based on the result rows which
usually contain duplicates for eager
collection fetching, hence, the number
of rows is not what you would expect.
Fetch should also not be used together
with impromptu with condition. It is
possible to create a cartesian product
by join fetching more than one
collection in a query, so take care in
this case. Join fetching multiple
collection roles can produce
unexpected results for bag mappings,
so user discretion is advised when
formulating queries in this case.
Finally, note that full join fetch and
right join fetch are not meaningful.
Just for reference sake, I ran into something like this once, where hibernate was always eagerly loading a set for me.
Turns out I had this setter:
public void setStreams(Set<StreamRef> streams) {
for (StreamRef s : (Set<StreamRef>) streams) {
s.setTape(this); // boo on hibernate, which apparently needs this. whoa!
}
this.streams = streams;
}
which was being called by hibernate (passing it a pointer to a lazy set) but then my for loop was basically calling it to be loaded at that point. If that helps any followers :)
You can test if it's lazy by closing the session, then trying to iterate over the collection. If it's lazy, you'll get org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: XX.streams, no session or session was closed

Hibernate Save strange behaviour

I have a user object that has a one-to-many relationship with String types. I believe they are simple mappings. The types table hold the associated user_id and variable type names, with a primary key 'id' that is basically a counter.
<class name="Users" table="users">
<id column="id" name="id" />
...
<set name="types" table="types" cascade="save-update">
<key column="id" />
<one-to-many class="Types" />
</set>
</class>
<class name="Types" table="types">
<id column="id" name="id" />
<property column="user_id" name="user_id" type="integer" />
<property column="type" name="type" type="string" />
</class>
This is the java I used for adding to the database:
User u = new User();
u.setId(user_id);
...
Collection<Types> t = new HashSet<Types>();
t.add(new Type(auto_incremented_id, user_id, type_name));
u.setTypes(t);
getHibernateTemplate().saveOrUpdate(u);
When I run it, it gives this error:
61468 [http-8080-3] WARN org.hibernate.util.JDBCExceptionReporter - SQL Error: 1062, SQLState: 23000
61468 [http-8080-3] ERROR org.hibernate.util.JDBCExceptionReporter - Duplicate entry '6' for key 'PRIMARY'
61468 [http-8080-3] ERROR org.hibernate.event.def.AbstractFlushingEventListener - Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
When I check the sql, it shows:
Hibernate: insert into users (name, id) values (?, ?)
Hibernate: insert into types (user_id, type, id) values (?, ?, ?)
Hibernate: update types set id=? where id=?
Why does Hibernate try to update the types' id?
The error says: Duplicate entry '6' for key 'PRIMARY', but there really isn't? I made sure the ids are incremented each time. And the users and types are added into the database correctly.
I logged the information going in, and the types added has an id of 7 and a user id of 6. Could it be that Hibernate takes the user_id of 6 and tried to update types and set id=6 where id=7? Therefore the duplicate primary key error?
But why would it do something so strange? Is there a way to stop it from updating?
Should I set the id manually? If not, then how should I add the types? It gives other errors when I add a type object that only has a type string in it and no ids.
Thanks guys. Been mulling over it for days...
Your biggest problem is incorrect column in the <key> mapping - it should be "user_id", not "id". That said, your whole mapping seems a bit strange to me.
First of all, if you want IDs auto generated you should really let Hibernate take care of that by specifying appropriate generator:
<id column="id" name="id">
<generator class="native"/>
</id>
Read Hibernate Documentation on generators for various available options.
Secondly, if all you need is a set of string types, consider re-mapping them into a collection of elements rather than one-to-many relationship:
<set name="types" table="types">
<key column="user_id"/>
<element column="type" type="string"/>
</set>
That way you won't need explicit "Types" class or mapping for it. Even if you do want to have additional attributes on "Types", you can still map it as component rather than entity.
Finally, if "Types" must be an entity due to some requirement you have not described, the relationship between "Users" and "Types" is bi-directional and needs to be mapped as such:
<set name="types" table="types" inverse="true">
<key column="user_id"/>
<one-to-many class="Types"/>
</set>
...
in Types mapping:
<many-to-one name="user" column="user_id" not-null="true"/>
In the latter case "Types" would have to have a "user" property of type "Users".
Here is a detailed example.
The solution that worked for me was to separately save the two parts (without adding type to user):
getHibernateTemplate().save(user);
getHibernateTemplate().save(new Type(user.id, type_name));
And with the < generator class="native" /> on only the types id.
Bad practice?
Had mapped it as a collection of elements, but it was somewhat wrongly adding the user_id to the types id column which causes duplicate error sometimes; since types id is the only primary key column. Weird! I sort of remember there was some other column error, but forgot about it, because I immediately reverted back to one-to-many. Couldn't grasp the strange workings of Hibernate...
I will try the bi-directional solution sometime... Thanks a lot for all the help :)

Categories