在Hibernate映射到MySQL中,将LONG作为主键

5
我正在尝试通过将Hibernate映射到MySQL表来实现一些Java对象的持久性。当我提交时,会收到一条消息,显示“批处理更新返回了意外的行计数 [0]; 实际行计数:0; 预期:1”。
我的假设是,在我的Java POJO中有一个long字段,我想将其用作MySQL表中的主键,这可能导致问题。由于我无法在MySQL表中使用LONG数据类型作为主键(错误1170:BLOB/TEXT列'id'在没有键长度的键规范中使用),因此我从一些搜索和这个post中得出结论,BIGINT将是适合long的映射。然而它没有更新。
我的测试POJO Person非常简单。它有3个字段:id(long)、firstname(String)、lastname(String)以及setter和getter等。
我在xml中进行hibernate映射(person.hbm.xml),基本上看起来像(减去标题):
<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>

我实际的Java代码片段,旨在保存或更新记录,非常简单:

Transaction tr = session.beginTransaction();            
Person person = new Person(1,"John","Doe");
session.saveOrUpdate(person);
tr.commit();

这里是问题,如果我将Person对象和MySQL表中的id类型更改为int(Integer),那么一切都可以正常工作。但是,我无法对要持久化的实际对象进行此更改,所以问题是:我做错了什么或者我应该怎么做才能使其正常工作?谢谢。

添加堆栈跟踪:

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

更新: 好的,我终于解决了。我将Hibernate生成器类从“native”更改为“assigned”,现在它按预期工作。因此,现在Hibernate映射如下:

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

我必须承认我不知道那个参数的意义(从某处复制),也不知道它会引起这么多的头疼。发现了这个解释,非常有用。

显然我没有足够的凭据来回答自己的问题,所以我想它将保持开放状态,或者如果有人提供一个空答案,我将接受它。谢谢。


你能否发布完整的堆栈跟踪信息?long和BIGINT应该可以正常工作,我个人使用过没有发现任何问题,请分享你的堆栈跟踪信息。 - mprabhat
你的数据库列类型定义是什么?还有你的id属性类型呢?无论如何,对于列来说使用Bigint,对于Java属性使用Long都可以。 - polypiel
@mprabhat完成。它有一个消息,说“不支持嵌套事务”,这是我之前没有看到的。但我不知道它是什么意思? - hgus1294
@polypiel 抱歉,我不确定我理解你的问题。MySQL中的数据类型是BIGINT(20),而Hibernate配置就是我上面发布的所有内容。 - hgus1294
意识到“不支持嵌套事务”的异常可能是另一个单独的异常,因为我正在尝试在上一个事务失败时开始新的事务。因此,在这种情况下它并不相关。 - hgus1294
1个回答

5
当您使用saveOrUpdate()方法时,如果对象的id为null,则Hibernate会触发插入查询,如果id是任何其他值,则触发update我可以看到代码中Person person = new Person(1,"John","Doe");id设置为1并调用saveOrUpdate()方法。我假设没有id为1的条目,因此会抛出错误。
为使其正常工作,您需要进行以下更改。
  1. 将person中的id类型从long更改为Long(包装类以支持null)。

  2. 编写构造函数new Person("John","Doe");并保存该对象。

在事务数据中保留<generator class="assigned"/>不是一个好主意。相反,您应该坚持一开始尝试的native
我认为这是解决您的初始问题的更清洁的方法,即使您已经找到了替代方案。

谢谢。您的假设是正确的,在SaveOrUpdate之前没有id=1的先前记录。我尝试按照您的建议将类类型更改为Long,但如果我将生成器设置为native,它会恢复到相同的错误。我不理解您的第二点,即从构造函数中省略idid不是自动生成的id,因此我需要能够将其设置为特定值。 - hgus1294
我的意思是让你的数据库选择ID,而不是在保存时将ID硬编码到bean中。从这里的选项中选择如何做到这一点:http://docs.jboss.org/hibernate/core/3.3/reference/en/html/mapping.html#mapping-declaration-id - ManuPK
好的,我明白了。在我的实际场景中,id是由外部服务提供的,并且需要保持不变,因此我不能让数据库决定。如果我只是将类类型更改为Long并保留一个接受Long的构造函数,则会得到原始异常。这是否符合预期或者我误解了其他内容?您提到了使用assigned生成器进行事务存在风险。什么是风险? - hgus1294

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接