LONG as primary key in Hibernate mapping to MySQL - java

I am trying to implement persistence of some Java objects via Hibernate mapping to a MySQL table. When I commit I get a message saying 'Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1'.
My hypothesis is that the problem is caused from having a long-field in my Java POJO that I want to use as my primary key in the MySQL table. Since I was not able to use datatype LONG as my primary key in MySQL table (ERROR 1170: BLOB/TEXT column 'id' used in key specification without a key length) I concluded from some googling and this post that BIGINT would be the suitable mapping for long. However it is not updating.
My test POJO Personis very simple. It has 3 fields: id (long), firstname (String), lastname (String) with setters and getters, etc.
I do the hibernate mapping in xml (person.hbm.xml) that essentially looks like (minus headings):
<hibernate-mapping>
<class name="hibernatetest.Person" table="hibernatetest">
<id name="id" type="long" column="id" >
<generator class="native"/>
</id>
<property name="firstname">
<column name="firstname" />
</property>
<property name="lastname">
<column name="lastname"/>
</property>
</class>
</hibernate-mapping>
My actual java code snippet that is supposed to save or update the record is simple:
Transaction tr = session.beginTransaction();
Person person = new Person(1,"John","Doe");
session.saveOrUpdate(person);
tr.commit();
And here's that thing, this all works just fine if I change the type of id to an int (Integer) in the Person object and in the MySQL table. However, I do not have that option for the actual objects that I want to persist so the question is; what am I doing wrong or what should I do to get it to work? Thanks.
ADDING Stacktrace:
Hibernate: update hibernatetest set firstname=?, lastname=? where id=?
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:81)
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:73)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:57)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3006)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2908)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3237)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:113)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:273)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:265)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:187)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1082)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:317)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
at com.hibernate.test.TestMain.main(TestMain.java:38)
nested transactions not supported
UPDATE:
OK, I have finally worked it out. I changed the hibernate generator class from 'native' to 'assigned' and now it works as expected. So now the hibernate mapping looks like:
<hibernate-mapping>
<class name="hibernatetest.Person" table="hibernatetest">
<id name="id" type="long" column="id" >
<generator class="assigned"/>
</id>
<property name="firstname">
<column name="firstname" />
</property>
<property name="lastname">
<column name="lastname"/>
</property>
</class>
</hibernate-mapping>
Must admit I did not know the meaning of that parameter (copied from somewhere) and had no idea it could cause this much headache. Found this explanation which was quite useful.
Apparently I do not have enough credentials to answer my own questions so I guess that it will remain open or if someone provides an empty answer, I will accept it. Thanks.

When you use the saveOrUpdate() method hibernate fires the insert query if the id of the object is null and update if it is any other value. I can see the code,
Person person = new Person(1,"John","Doe"); setting the id to 1 and calling the saveOrUpdate() method. I am assuming there are no entries for the id 1 and hence the error is thrown.
To make it work, you need to make the below changes.
Change the Type of id in person to Long from long(The wrapper class so that it can support null).
Write the constructor new Person("John","Doe"); and save that object.
It is not a good Idea to keep the <generator class="assigned"/> for the transactional data. Instead you should be sticking to the native as you were trying first.
I feel this is a cleaner way to solve your initial problem, even though you have found an alternate solution.

Related

How to create NO primary key table with Hibernate?

My goal is to create a no PK table in the database (I will never use it for ORM). I only need to create it, that's all.
I know it is possible to create a no PK table with NHibernate from this stack overflow question Why is NHibernate creating a table without primary key? But I need it to work in Java not .Net
Here is what works. It has PK, but I don't need it
<?xml version="1.0" encoding="UTF-8"?>
<hibernate-mapping>
<class entity-name="testtablenopi">
<id name="id"
type="long"
column="ID">
<generator class="sequence"/>
</id>
<property name='xarxint' column='xarxint' type='int' precision='11' scale='0'/>
<property name='xarxbig_decimal' column='xarxbig_decimal' type='big_decimal' precision='17' scale='2'/>
</class>
When I remove <id/> it doesn't work .
I tried replacing class with bag it didn't work.
How can I create a database table without a PI in hibernate?
It's not possible. Hibernate and all ORMs I know require a PK to be defined for an object.
You'll need to use a plain old JDBC insert statement.

Java_Hibernate 3_CLOB_ORA-01461

I added a new CLOB Column in a table and modified my hbm.xml - file to use this new column (the old column was varchar2 and therefore too small):
(I used random-names for demonstration purposes)
<?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="test.com.TestDO" table="TESTTABLE">
<id name="id" type="java.lang.Integer">
<column name="ID"/>
<generator class="sequence">
<param name="sequence">SEQ_SEQUENCENAME</param>
</generator>
</id>
</property>
<property name="columnName1" type="java.util.Date">
<column name="COLUMN_NAME1"/>
</property>
<property name="columnName2" type="java.lang.String">
<column name="COLUMN_NAME2"/>
</property>
<property name="columnName3" type="java.lang.String">
<column name="COLUMN_CLOB"/>
</property>
</class>
</hibernate-mapping>
As you can see, my clob-column (COLUMN_CLOB) is defined as last, because first I got this exception:
ORA-24816: Expanded non LONG bind data supplied after actual LONG or LOB column
I searched for this error and the solution was to place the clob binding at the last in the insert-statement, so I defined the clob-column in the hbm.xml file to be last, as you can see above.
Now I am not getting the ORA-24816 exception, but I am getting this exception:
ORA-01461: can bind a LONG value only for insert into a LONG column
The generated Insert-Statement looks like this:
insert into TESTTABLE(COLUMN_NAME1, COLUMN_NAME2, COLUMN_CLOB, ID) values (?, ?, ?, ?)];
As you may have noticed, the COLUMN_CLOB - column does not appear last in the generated Insert-Statement. I don't know if the order in the hbm-xml -file affects the order of the columns in the generated insert-statement?
But anyway, I am not getting the ORA-24816 anymore just ORA-01461. I don't know how to solve this issue.
I am using Hibernate 3 and Oracle Version: Enterprise Edition Release 11.2.0.4.0 - 64bit Production
EDIT:
I have the exact same issue as described here:
http://newtechnobuzzz.blogspot.ch/2014/07/ora-24816-expanded-non-long-bind-data.html#.Wcy-QdFpHRY
I have tried the following solutions:
Chaning the order of getter and setter methods does not work
Changing the order of the declared property in the hbm.xml -file does
not work
The problem described on the link and on other sites, state that this excpetion
occurs if you try to insert data in both the varchar2 and the clob columns (2 strings with a length of 4000). However I am only inserting data into the clob-column which is bigger then 4000. I am not using the old varchar2 - column anymore.
Now, I am trying to solve this by using this solution:
It can solved by writing two update queries. Firstly, save/update the entity by an Update query and then write another query to update LOB columns in that entity.
However, I dont quite understand, how I should modify my code. My method looks like this:
#Override
#Transactional(readOnly=false, propagation=Propagation.MANDATORY)
public TestDO saveTest(TestDO test) {
getHibernateTemplate().saveOrUpdate(test);
return test;
}
From what I can see, the property type has not been declared correctly for the CLOB column, try that:
<property name="columnName3" length="100000" type="StringClob">
<column name="COLUMN_CLOB"/>
</property>

Hibernate (hbm) - refreshing set with where clause after merging the container object

I'm working with Hibernate 4.3.5, Java 1.6 and Spring 4.0.3.
I've mapped the entities through hbm, and I want my application works with logical deletion.
So, in each mapped entity, I've added a property named 'deleted', which indicates if an entity is deleted or not.
Because I don't want to load the deleted entities (the ones having true the deleted property), I've used the where clause in the mapped classes, so I only get the entities aren't logically deleted.
And also, I've added the same where clause to every one-to-many relationship.
In one particular case, I've got a Report entity that has a one-to-many relationship with the Document entity.
So, when I mark a Document as deleted, and I save the Report entity (with merge), I expect than the Report entity doesn't keep the Document marked as deleted. But this doesn't occur.
This is the hbm for the Report entity:
<hibernate-mapping>
<class
name="es.entities.Report"
table="reports"
dynamic-insert="false"
dynamic-update="false"
where="deleted = 0">
<id name="id">
<generator class="identity"/>
</id>
<property name="title"></property>
<property name="deleted"></property>
<set
name="documents"
table="documents"
cascade="all"
lazy="false"
where="deleted=0">
<key column="id_report"/>
<one-to-many class="es.entities.Document"/>
</set>
</class>
</hibernate-mapping>
Here it is the hbm for the Document entity:
<hibernate-mapping>
<class
name="es.entities.Document"
table="documents"
dynamic-insert="false"
dynamic-update="false"
where="deleted = 0">
<id name="id">
<generator class="identity"/>
</id>
<property name="name"></property>
<property name="type"></property>
<property name="size"></property>
<property name="deleted"></property>
</class>
</hibernate-mapping>
I use a Service (ReportService) to open a Spring transaction. The method is:
#Autowired
private ReportDao reportDao;
#Transactional
public Report save(Report report) {
this.reportDao.save(report);
}
And this is the DAO (ReportDao) method I use to save the Report entity:
public Report save(Report report) {
return (Report) this.currentSession().merge(report);
}
I put an example:
The parameter I send to the service contains a Report object, with two Document objects, one of them deleted and the other not.
The DAO method returns the same information, but I'd like this method returns only the documents are not deleted.
Note: if I use another method with another transaction, I obtain the report only with the document is not deleted, but I'd like to do this in the same transaction.
Can anybody help me or show me an alternate to this? It is possible to use other Session method than merge?
Thanks a lot.
Merge method create a copy from the passed entity object and return it. Try re-fetching the report entity post merge.

org.hibernate.MappingException: Repeated column in mapping for entity

I am using Hibernate 3.2.5. I am getting the above exception while using many-to-one mapping. The training table is having a many to one relation with Department table, i.e. One Depatement is capable of taking more than one training.
The exception is asking me to add insert="false" update="false" in my hbm file. If I add this bit in hbm file, then the code works fine.
Here is the hbm file:
<?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="com.infy.model.Training" table="training">
<id name="Id" type="integer" column="ID">
<generator class="assigned"></generator>
</id>
<property name="trainerName">
<column name="TRAINER_NAME"></column>
</property>
<property name="deptId">
<column name="DEPT_ID"></column>
</property>
<property name="trainingSubject">
<column name="TRAINING_SUBJECT"></column>
</property>
<many-to-one name="departmentDetails" column="DEPT_ID"></many-to-one>
</class>
</hibernate-mapping>
If I change this line to:
<many-to-one name="departmentDetails" column="DEPT_ID" insert="false" update="false"></many-to-one>
Then the code works. I want to know what is the exact reason for adding this.
Regards,
You have mapped the DEPT_ID column twice, here:
<property name="deptId">
<column name="DEPT_ID"></column>
</property>
And here:
<many-to-one name="departmentDetails" column="DEPT_ID"></many-to-one>
When executing a select statement, Hibernate will be fine populating two properties of your object from the same column, however when doing an insert or an update it cannot decide which property to persist in the database.
Why do you need two properties mapped to the same column in the first place? If you need access to the deptId, you can probably remove the deptId property and instead do
training.getDepartmentDetails().getId()
The error message for this scenario is quite clear (you haven't put it here, but I've seen it a few times). The problem is that you've mapped the column DEPT_ID to two different fields in your class.
First, you've mapped it to the property deptId and then to departmentDetails. As you found out, hibernate allows to do this only if one of the mappings is configured to be insert="false" update="false".
The reason is quite simple. If you would change deptId to another id, hibernate would need to change the class that is mapped in departmentDetails, which is quite complicated.
if you need to get the deptId, you can add a getDeptId method on Training that returns departmentDetails.getId(). And don't provide a setDeptId.
If you are using the same column name twice in your mapping file. might be you get mapping Exception
Initial SessionFactory creation failed.org.hibernate.MappingException:
Also if u mark insert=flase and update=false .
if u try to update or insert in records in table or another legacy system try to update these column value. it wouldn't update or insert that filed.
Please check the below link .it will help to find your solutions.
http://www.techienjoy.com/hibernate-insert-update-control.php
Thanks
Sandeep G.

Hibernate Reference column in table to indicate how to unmarshall an attribute in a different column in the same table

I have an entity that I want to persist through Hibernate (3.2)
The EntityBean has a column that indicates how another value of the entity bean should be unmarshalled:
<class name="ServiceAttributeValue" table="service_attribute_value">
<cache usage="nonstrict-read-write"/>
<id name="id" column="id" type="int-long">
<generator class="native"/>
</id>
<property name="serviceAttribute" type="service-attribute" column="service_attribute" not-null="true" />
<!-- order is important here -->
<property name="value" type="attribute-value" not-null="true">
<column name="service_attribute" />
<column name="id_value"/>
<column name="enum_value"/>
<column name="string_value"/>
<column name="int_value"/>
<column name="boolean_value"/>
<column name="double_value"/>
</property>
</class>
The "service_attribute" column indicates which of the columns for the "value" property to look at when it unmarshalls the value and, more importantly, exactly what Type the value should be, for example the class of the Enum if the enum_value is to be read, or the type of Bean if the the id_value is to be read.
The value property uses a custom CompositeUserType to do the unmarshalling and within this I wish to reference the service_attribute column (although not write to it), however when I try to do this I get the following error:
org.hibernate.MappingException: Repeated column in mapping for entity: com.precurse.apps.rank.model.service.ServiceAttributeValue column: service_attribute (should be mapped with insert="false" update="false")
However within the definition of the composite property these xml attributes are not defined (only within a normal property).
Does anyone know of a way of overcoming this, or if there is a better solution to this propblem.
If you need any more information please let me know,
Cheers
Simon
I had a similar problem and changing the case of one column solved the problem. Could give a try!
e.g., one column could be service_attribute other Service_Attribute.
You can try this. Instead of mapping both values as property on the same table, map one of the property using join to itself and keep the other property as the way it is. This case you will be able to access the same property in both places. Just remember to name the property as different name.
<join table="service_attribute_value">
<key column = "id" />
<property name="serviceAttribute" type="service-attribute" column="service_attribute" not-null="true" />
</join>
<!-- order is important here -->
<property name="value" type="attribute-value" not-null="true">
<column name="service_attribute" />
<column name="id_value"/>
<column name="enum_value"/>
<column name="string_value"/>
<column name="int_value"/>
<column name="boolean_value"/>
<column name="double_value"/>
</property>
based on your description, it seems like what you want to do is creating different subclasses based on the service_attribute. Instead of trying to achieve repeated column mapping which is not allow in hibernate, you can take a look hibernate inheritance mapping.
I Think I found a solution albeit not a very elegant one.
in the
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
method of the CompositeUserType the "owner" argument passed to the method contains the id of the object who's service_attribute I want to access.
Annoyingly the actual serviceAttribute of the owner is not accessable or has not been set at this stage (I played around with the ordering of the elements in the hbm.xml config, in case this was an ordering thing, but unfortunatly still no joy), so I can't simply access it.
Anyway the id of the owner object is set, so I then used the session argument to run a HQL query based on the id to access the serviceAttribute which I then used to correctly unmarshall the value property.
The drawback of this solution is that it requires a HQL query as an overhead to the unmarshalling process, although its within the same session, its still not optimal.
If anyone has any ideas for a better solution I'd be very grateful.
Cheers

Categories

Resources