【发布时间】:2015-09-30 11:05:18
【问题描述】:
我有一张桌子
CREATE TABLE `SomeEntity` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`subid` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`,`subid`),
我有一个实体类,其中有一个自动增量字段。我想在它被持久化时读取分配给它的自动增量 id
getter 的注解如下
private long id;
private int subid;
@Id
@GeneratedValue **//How do i correct this to have multiple rows with same id and different subid**
@Column(name = "id")
public long getId() {
return id;
}
@Id
@Column(name = "subid")
public int getSubid() {
return subid;
}
我想拥有实体
id 1 subid 0
id 1 subid 1
id 1 subid 2
id 2 subid 0
subid 在数据库中默认为 0,我在更新该行时以编程方式递增它。 我尝试了this SO post中的解决方案 JPA - Returning an auto generated id after persist()
@Transactional
@Override
public void daoSaveEntity(SomeEntity entity) {
entityManager.persist(entity);
}
现在在此事务之外,我正在尝试获取分配的自动增量 ID
@Override
public long serviceSaveEntity(SomeEntity entity) {
dao.daoSaveEntity(entity);
return entity.getId();
}
我从网络服务调用它
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response createEntity(SomeEntity entity) {
更新方法如下
@Transactional
public void updateReportJob(SomeEntity someEntity) {
Query query =
entityManager
.createQuery("UPDATE SomeEntity SET state=:newState WHERE id = :id");
query.setParameter("newState","PASSIVE");
query.setParameter("id", id);
query.executeUpdate();
double rand = Math.random();
int i = (int) (rand * 300);
try {
Thread.sleep(i); //only to simulate concurrency issues
} catch (InterruptedException e) {
e.printStackTrace();
}
List<Integer> resList =
entityManager.createQuery("select max(subid) from SomeEntity WHERE id = :id")
.setParameter("id", jobId).getResultList();
// Increment old subid by 1
int subid = resList.get(0);
SomeEntity.setsubid(subid + 1);
SomeEntity.setState("ACTIVE");
// entityManager.merge(SomeEntity);
entityManager.persist(SomeEntity);
}
我从 N 个线程发送 N 个并发更新,用于 ID 为 1 和以下几个其他属性的实体
SomeEnity 实体 = 新的 SomeEntity();
entity.setId(1);
long num = Thread.currentThread().getId();
entity.setFieldOne("FieldOne" + num);
entity.setFieldTwo("" + i);
entity.setFieldThree("" + j);
i++;
j++;
案例 1,id 上带有 @Id,subid 上带有 @Id 注释,更新中带有 `entityManager.persist'
当我运行 300 个线程时,一些因连接异常“连接过多”而失败,数据库状态为
id 1 subid 0
id 1 subid 1
id 1 subid 2
.. ....
id 1 subid 150
subid 始终是递增的,竞争条件只是因为竞争条件而未定义的 ACTIVE 条件
案例 2,id 上带有 @Id,subid 上带有 @Id 注释,并且 `entityManager.merge' 在更新中
id 1 subid 0 ID 1 子 ID 0 ID 2 子 ID 0 ………… id 151 subid 0 (也许只是巧合,比案例 1 多一个线程成功?)
案例 3 使用 @GeneratedValue 和 @Id 在 id 和 **NO @Id 在 subid 上注释和 entityManager.persist 在更新**
异常 -- 传递给持久化的分离实体
案例 3 使用 @GeneratedValue 和 @Id 在 id 和 **NO @Id 在 subid 和 entityManager.merge 上的注释更新**
如果按顺序运行更新,则数据库状态为
id 1 subid 0
下次更新后
id 1 subid 1
每次更新后更新同一行(导致一次只更新一行)
id 1 subid 2
案例 4 与案例 3 相同,同时更新 如果同时运行(有 300 个线程),我会得到以下异常
org.hibernate.HibernateException: More than one row with the given identifier was found: 1
数据库状态是 id 1 subid 2(只有一个线程会成功,但由于竞争条件将 subid 从 0 更新为 2 )
案例 5 在 id 上使用 @GeneratedValue 和 @Id,在 subid 上使用 @Id 注释
创建也因 subid org.hibernate.PropertyAccessException 失败:调用 SomeEntity.id 的 setter 时发生 IllegalArgumentException
请解释原因。从我知道的方法的 javadoc 中
persist - 使实例受管理和持久化。
merge - 将给定实体的状态合并到当前持久化上下文中。
我的问题更多是关于 hibernate 如何管理注释。 为什么在情况 3 会话尚未关闭的情况下会出现分离实体异常? 为什么在案例 5 中会出现 IllegalArgumentException?
我正在使用休眠 3.6 mysql 5 和 Spring 4 另外请提出一种实现这种增量 id 和 subid 的方法。(使用自定义 SelectGenerator ,带有演示实现或任何其他方式,无需进行列连接)
【问题讨论】:
-
更新了问题。id 是一个自动增量列,并且在没有 @GeneratedValue 的情况下与 hibernate 3.6.5 一起工作很好,只是在持久化 id 后它不会反映在对象中
-
它可能不会在持久化后立即发生,但它在 @Transactional 方法中(如链接帖子中所述),因此该值将反映在事务之外。经过几次尝试后,我发现它是由于复合主键而发生
-
因为 id 是自动递增的,所以不能有多个具有相同 id 的行,数据库将不允许您为该字段设置值。我认为问题不完整或没有正确提及。
-
见案例 1 。如果没有 GeneratedValue 注释,它允许有多个具有相同 ID 的行,即使它在数据库中是自动递增的,因为主键是 (id,subid)
标签: java mysql spring hibernate jpa