Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LONG as primary key in Hibernate mapping to MySQL

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.

like image 807
hgus1294 Avatar asked Dec 01 '11 10:12

hgus1294


People also ask

Can embeddable class be used as primary key in Hibernate?

The EJB 3.0 specification allows you to define a primary key class as a @Embeddable and use it as the primary key of your Entity bean. One or more properties can be used as members of the primary key for that particular table.

How can we use string as primary key in Hibernate?

You just need to add an attribute to your entity, make sure that its type and name match the database column, annotate it with @Column and you're done. You can then use the primary key to load the entity, and Hibernate sets the primary key value automatically.

Can we update primary key in Hibernate?

No. Hibernate doesn't allow to change the primary key.

What is the primary key in Hibernate?

Identifiers in Hibernate represent the primary key of an entity. This implies the values are unique so that they can identify a specific entity, that they aren't null and that they won't be modified. Hibernate provides a few different ways to define identifiers.


1 Answers

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.

like image 153
ManuPK Avatar answered Oct 17 '22 00:10

ManuPK