5

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.

Community
  • 1
  • 1
hgus1294
  • 747
  • 14
  • 26
  • can you post your complete stacktrace, long and BIGINT should work fine, I have used it personally no issues found, please share your stacktrace – mprabhat Dec 01 '11 at 10:40
  • What's your db column type definition? And your id attribute type? Anyway, Bigint for column, and Long for java attribute is allright. – polypiel Dec 01 '11 at 10:45
  • @mprabhat Done. It has a message saying 'nested transactions not supported' that I did not see before. Don't know what it means though? – hgus1294 Dec 01 '11 at 10:48
  • @polypiel Sorry, not sure that I understand the question. The datatype in MySQL is BIGINT(20) and the hibernate config is all that i posted above. – hgus1294 Dec 01 '11 at 10:56
  • Realized that the 'nested transactions not supported' is presumably another and separate exception thrown because I am trying to begin a new transaction when the previous transaction had failed. So it is not relevant in this context. – hgus1294 Dec 01 '11 at 11:13

1 Answers1

5

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.

  1. Change the Type of id in person to Long from long(The wrapper class so that it can support null).

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

ManuPK
  • 11,623
  • 10
  • 57
  • 76
  • Thank you. You are correct in your assumption that there was no previous record with `id`=1 before `SaveOrUpdate`. I tried to change the class type to `Long` according to your suggestion but it reverts back to the same error if I set the generator to `native`. I didn't understand you second point where you omit `id` from the constructor? The `id` is not an autogenerated id so I need to be able to set it to a specific value. – hgus1294 Dec 01 '11 at 14:49
  • I meant that let your DB choose the ID and do not hard code the id to the been while saving. Choose from the options [here](http://docs.jboss.org/hibernate/core/3.3/reference/en/html/mapping.html#mapping-declaration-id) on how can do it. – ManuPK Dec 01 '11 at 16:16
  • OK, got it. In my actual scenario, the `id`'s are provided from an external service and needs to be persisted as such so I cannot let the db decide. If I just change the class type to `Long` and keep a constructor that takes a `Long` I get the original exception. Is that as expected or did I misunderstand something else? You mentioned a risk with generator=`assigned` for transactions. What is the risk? – hgus1294 Dec 01 '11 at 17:15