Trying to insert a new Entity using hibernate and it is throwing me this exception:
a different object with the same identifier value was already associated with the session
I understand that this error is coming because hibernate finds a similar object in the memory. But I am creating a new object every time before inserting. Does it have anything to do with sequence?
hbm
<class name="MyObject" table="My_Object">
<id column="object_id" name="id" type="long">
<generator class="sequence">
<param name="sequence">OBJ_SEQ</param>
</generator>
</id>
<property name="column1" column="column_1" type="string" not-null="true"/>
<property name="column2" column="column_2" type="string" not-null="true"/>
<property name="column3" column="column_3" not-null="true" type="string"/>
</class>
The problem is when you are trying to persist an entity, a entity with the same id is already present in the database. Reason for this can be, you are manually assigning a value to the id, or you have reset the OBJ_SEQ in database.
Related
There is something in the inner hibernate's behavior I don't understand.
I have got a normal hibernate configuration of a class having an other one in reference.
My principal class below
<hibernate-mapping package="com.my.package">
<class name="MyClass" table="MY_TABLE">
<id name="id" column="ID">
<generator class="sequence">
<param name="sequence">SEQ_MY_TABLE</param>
</generator>
</id>
<!-- reference -->
<many-to-one class="MyReferenceClass" fetch="select" name="myReference">
<column name="ID" not-null="true"/>
</many-to-one>
<!-- some other properties ... -->
.
.
</class>
</hibernate-mapping>
And the reference class below
<hibernate-mapping package="com.my.package">
<class name="MyReferenceClass" table="MY_REFERENCE_CLASS">
<id name="id" type="long">
<column name="ID" precision="22" scale="0" />
<generator class="sequence" />
</id>
</class>
<!-- Some propeties -->
.
.
</hibernate-mapping>
I have in this table of reference some lines:
id , property1, property2
0 val1x val2x
1 val1y val2y
2 val1z val2z
.
.
My problem is that when I try to insert a new instance of MyClass having a reference to MyReferenceClass with id 1,2,..., I have no problem. But when I try to insert one with the id = 0, I have got the exception below:
org.hibernate.PropertyValueException: not-null property references a null or transient value: com.my.package.MyClass.myReference
at org.hibernate.engine.Nullability.checkNullability(Nullability.java:72)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:290)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:181)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:187)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:172)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:94)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
I resolved the problematic by changing the generator class from sequence to assigned in the MyReferenceClass.hbm.xml :
<hibernate-mapping package="com.my.package">
<class name="MyReferenceClass" table="MY_REFERENCE_CLASS">
<id name="id" type="long">
<column name="ID" precision="22" scale="0" />
<generator class="assigned" />
</id>
</class>
<!-- Some propeties -->
.
.
</hibernate-mapping>
The problem is that I have no clue why it doesn't work only when my id = 0 and not every time.
Which mecanism do I not understand in this part of the Hibernate Framework ?
PS: I know that the sequence in my first file is false as we have no sequence here, that's actually an old legacy code I'm making evolve, but I don't understand why it worked before.
It looks like the unsaved-value attribute of the id mapping for the MyReferenceClass is defaulting to 0, and cascading updates aren't enabled for MyClass.
When you add an object with an id other than the unsaved-value Hibernate assumes you're referencing an existing row. When you add an object with an id matching the unsaved-value then Hibernate assumes the entity is transient and needs to be persisted in order to get a new value from the sequence. And because your collection isn't cascading, Hibernate assumes the references are to existing, non-transient entities.
Changing the generator to "assigned" means you assume full responsibility for populating the id and Hibernate won't have to worry about it.
To make this work with the sequence generator you could save the objects before you add them to the collection, or set up a cascade on the mapping for the collection.
I'm new to Hibernate and I'm trying to dynamically join more table. I have built an example with three tables: Employee, Address, Country. I want to retrieve all employees of a certain country. Here my config file:
<class entity-name="Employee">
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="firstName" column="first_name" type="string"/>
<property name="lastName" column="last_name" type="string"/>
<property name="salary" column="salary" type="int"/>
<many-to-one name="address" column="address" unique="true"
class="Address" not-null="true"/>
</class>
<class entity-name="Address">
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="street" column="street_name" type="string"/>
<property name="city" column="city_name" type="string"/>
<property name="state" column="state_name" type="string"/>
<property name="zipcode" column="zipcode" type="string"/>
<many-to-one name="country" column="country" unique="true"
class="Country" not-null="true"/>
</class>
<class entity-name="Country">
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="name" column="name" type="string"/>
<property name="code" column="code" type="string"/>
</class>
If I do
List employees = session.createCriteria("Employee")
.createCriteria("address")
.createCriteria("country")
.add(Restrictions.eq("code","IT"))
.list();
I get the right result back but my goal is not to manually specify the whole path between start table and filter table: I want that hibernate makes the job for me. I would like to write something like this:
List employees = session.createCriteria("Employee")
.createCriteria("country")
.add(Restrictions.eq("code","IT"))
.list();
but I get the error message
org.hibernate.QueryException: could not resolve property: country of: Employee
Hibernate is just a wrapper or abstraction layer to hide away SQL expressions.
In your working example Hibernate builds a SQL select statement creating a double JOIN and returning the desired output. You are feeding Hibernate with the "knowledge" of how to filter the associated table. The same as you would write your own SQL statement.
But in you second example you expect Hibernate to guess a second level association from the first table. Hibernate simply looks up if table Employee has a column/foreign key country and it fails to find it. So the error is: could not resolve property: country of: Employee
You can not expect from Hibernate to do this kind of magic as it would produce or sorts of false queries.
Just to illustrate on your example.
In case your Employee would have two fields of type Address:
<class entity-name="Employee">
<id name="id" type="int" column="id">
<generator class="native"/>
</id>
<property name="firstName" column="first_name" type="string"/>
<property name="lastName" column="last_name" type="string"/>
<property name="salary" column="salary" type="int"/>
<many-to-one name="bussiness_address" column="bussiness_address" unique="true"
class="Address" not-null="true"/>
<many-to-one name="home_address" column="home_address" unique="true"
class="Address" not-null="true"/>
</class>
Both would have a Country linked ... what should in case of this query hibernate do?
List employees = session.createCriteria("Employee")
.createCriteria("country")
.add(Restrictions.eq("code","IT"))
.list();
Filter country by the home or the business address or both?
Or just imagine a case where you do filtering by an id field present in every table.
So to wrap things up: Hibernate can't do magic it is only here to hide away raw SQL.
If I translate your query to Java language, what you are trying to do is getting value of coutry directly out of employee. In Java you would get a compile error though. Such thing automatically does not work neither in Java, nor in Hibernate or plain SQL. There is no direct link between employee and country, but there is a sequence of links employee -> address -> country.
To fix this in Java way, one could maitain a secondary reference to country directly from employee. Then calling employee.getAddress().getCountry() and employee.getCountry() would be equal.
To achieve this in hibernate, you would need to add new field country to Employee and map it as a non-modifiable many-to-many relationship with entity Country, using entity Address to map the relationship. This would only reduce 2 hibernate joins to one, it could not be applied recurrently to more than 2 joins.
If this solution is OK for you, I can provide you with example syntax.
I wanna set lazy to "false" but only to one method at in runtime.
Can I do?
this.getSession().createSQLQuery("select * from customers....")....
Attention: im using createSQLQuery not createCriteria.
CustomerMapping.xml here:
<hibernate-mapping>
<class name="com.example.entities.customers.Customer"
table="CUSTOMERS">
<id name="id" type="long">
<column name="ID" />
<generator class="sequence">
<param name="sequence">seq_customers</param>
</generator>
</id>
<property name="name" type="String">
<column name="NAME_C" />
</property>
<many-to-one name="address"
class="com.example.entities.Address" fetch="select"
cascade="all">
<column name="ADDRESS_ID" />
</many-to-one>
</class>
</hibernate-mapping>
I wanna set lazy to false for Address.
I have to do this because this method return a list of customers (with address) and when I iterate this list and print its very slow cause the lazy is setted true (by default).
Is there a reason you are using SQL instead of HQL? I would stay away from SQL statements when using hibernate when possible.
I would implement it like this in HQL:
from Customer c
join fetch c.address
the join fetch makes Customers address no longer lazy.
I have a Node which is associated with NodeDatas. These NodeDatas have revisions, and Node has a column latestRevision which tells it which is the last revision.
I'm trying to figure out the mapping I need to get the set below filtered by the latestRevision in Node.
<class name="Node" table="RatNodes">
<id name="id">
<generator class="native"/>
</id>
<set fetch="join" name="nodeDatas" table="RatNodeData" inverse="true" access="field" cascade="all-delete-orphan" order-by="dataOrder asc">
<key column="nodeID" on-delete="cascade"/>
<one-to-many class="NodeData"/>
<filter name="revisionFilter" condition="revision = latestRevision"/>
</set>
<property name="questionNumber" access="field"/>
<property name="type" access="field"/>
</class>
<class name="NodeData" table="RatNodeData">
<composite-id>
<key-property name="nodeID" access="field"/>
<key-property name="key" access="field" column="dataKey"/>
<key-property name="order" access="field" column="dataOrder"/>
<key-property name="revision" access="field"/>
</composite-id>
<property name="value" access="field" type="EncodedStringUserType" column="dataValue"/>
</class>
The result of this mapping when I enable the filter is an error:
Caused by: com.jnetdirect.jsql.JSQLException: Invalid column name 'latestRevision'.
The query it spits out is:
select nodedatas0_.nodeID as nodeID1_, nodedatas0_.dataKey as dataKey1_, nodedatas0_.dataOrder as dataOrder1_, nodedatas0_.revision as revision1_, nodedatas0_.nodeID as nodeID46_0_, nodedatas0_.dataKey as dataKey46_0_, nodedatas0_.dataOrder as dataOrder46_0_, nodedatas0_.revision as revision46_0_, nodedatas0_.dataValue as dataValue46_0_ from RatNodeData nodedatas0_ where nodedatas0_.revision = nodedatas0_.latestRevision and nodedatas0_.nodeID=? order by nodedatas0_.dataOrder asc
I can tell this is wrong as it's looking for latestRevision on NodeData but I'm not sure how to tell it to look on Node instead. Is this possible in hibernate? Seems like a pretty simple query if I were just using SQL.
Ended up doing this instead of trying to do it in the mapping file:
List datas = session.createCriteria(NodeData.class)
.add(Restrictions.eq("nodeID", this.id))
.add(Restrictions.eq("revision", this.latestRevision))
.list();
I'm not sure if this is the right way to do this or not. Would love to hear about a mapping file solution too.
You have not defined any filter or 'latestRevision' as filter-param. So it will not come to know that what actually the entity means.
Try using following.
For further help have a look at the link.
I have one entity called ProductTemplate with the following hibernate mapping
<hibernate-mapping default-cascade="none">
<class name="com.stackoverflow.ProductTemplateImpl" table="PRODUCT_TEMPLATE" dynamic-insert="false" dynamic-update="false">
<composite-id name="productTemplatePk" class="com.stackoverflow.product.ProductTemplatePK">
<key-property name="templateType" type="java.lang.String">
<column name="TEMPLATE_ID" sql-type="VARCHAR2(255)" not-null="true"/>
</key-property>
<key-many-to-one name="product" class="com.stackoverflow.ProductImpl" >
<column name="PROD_ID"/>
</key-many-to-one>
</composite-id>
</class>
</hibernate-mapping>
where ProductTemplatePK is a normal java Primary key class.
and another entity called Product with the following hibernate mapping:
<hibernate-mapping default-cascade="none">
<class name="com.stackoverflow.ProductImpl" table="PRODUCT" dynamic-insert="false" dynamic-update="false">
<id name="id" type="java.lang.String" unsaved-value="null">
<column name="PROD_ID" sql-type="VARCHAR2(255)"/>
<generator class="assigned">
</generator>
</id>
<property name="state" type="java.lang.String">
<column name="PROD_STATE" not-null="true" unique="false"/>
</property>
<property name="nameEn" type="java.lang.String">
<column name="PROD_NAME_EN" not-null="true" unique="false"/>
</property>
</class>
</hibernate-mapping>
Now if I tried to retrieve all productTemplates based on the productId I can do it using the following hibernate criteria:
Criteria productTemplateCriteria = this.getSession().createCriteria(ProductTemplate.class);
productTemplateCriteria.add(Restrictions.in("productTemplatePk.product.id", "1"));
but I don't know how to retrieve those templates based on the product.nameEn as the following code:
Criteria productTemplateCriteria = this.getSession().createCriteria(ProductTemplate.class);
productTemplateCriteria.add(Restrictions.in("productTemplatePk.product.nameEn", "Ali"));
generates the following error:
Caused by: org.hibernate.QueryException: could not resolve property: productTemplatePk.product.nameEn of: com.stackoverflow.ProductTemplateImpl.
so how can I query an entity property that is mapped as part of a composite primary key ?
I manged to solve this by simple HQL query as I didn't find any way to do it using hibernate API
this is the HQL query I used:
from com.stackoverflow.ProductTemplateImpl productTemplate
where productTemplate.productTemplatePk.product.state not in (:productStates)
But I'm still interested to know if there is any Hibernate API combination that can do it.
I was not able to get deeper than 2 levels except for ids when specifying property names in criteria. Try using explicit join with alias:
Criteria crit = this.getSession().createCriteria(ProductTemplate.class);
crit.addAlias("productTemplatePk.product","p")
.add(Restrictions.in("p.nameEn", "Ali"));