gpt4 book ai didi

java - 解释使用 Hibernate 映射自动递增复合 id 序列中的行为

转载 作者:IT老高 更新时间:2023-10-28 13:47:19 35 4
gpt4 key购买 nike

我有一张 table

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 在 id 上使用 @GeneratedValue@Id 并在 subid 上使用 **NO @Id 注释和 entityManager.persist 在更新** 异常 -- 传递给持久化的分离实体

案例 3 在 id 上使用 @GeneratedValue@Id 并在 subid 上使用 **NO @Id 注释和 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 session 尚未关闭的情况下会出现分离实体异常?为什么在案例 5 中会出现 IllegalArgumentException?

我正在使用 hibernate 3.6 mysql 5 和 Spring 4还请提出一种实现这种增量 id 和 subid 的方法。(使用自定义 SelectGenerator ,带有演示实现或任何其他方式,无需进行列连接)

最佳答案

由于 id 字段已经是唯一的并且会自动递增,因此在这种情况下您不需要复合 id,因此您的实体可以如下所示:

@Id
@Column(name = "id")
public long getId() {
return id;
}

@Column(name = "subid")
public int getSubid() {
return subid;
}

可以使用实体管理器通过 id 获取实体:

entityManager.find(MyEntity.class, entityId); 

或者您可以使用同时获取 idsubid 的查询来获取实体:

MyEntity myEntity = entityManager.createTypeQuery("select me from MyEntity where id = :id and subid = :subid", MyEntity.class)
.setParameter("id", entityId)
.setParameter("subid", entitySubId)
.getSingleResult();

Hibernate 也有一个 SelectGenerator它可以从数据库列中获取 id,这在数据库使用触发器生成 id 时很有用。

不幸的是,它不适用于复合 id,因此您编写了自己的扩展 SelectGenerator 或使用单个字符串 id_sub_id 列组合了 id 和 sub- id 到单个 VARCHAR 列中:

'1-0'
'1-1'
'2-0'
'2-1'

您必须编写一个数据库触发器来使用特定于数据库的存储过程更新两列,并将这两列聚合到 VARCHAR 中。然后,您使用标准 SelectGenerator 将聚合列映射到 String 字段:

@Id
@Column(name = "id_sub_id")
@GeneratedValue( strategy = "trigger" )
@GenericGenerator(
name="trigger", strategy="org.hibernate.id.SelectGenerator",
parameters = {
@Parameter( name="keys", value="id_sub_id" )
}
)
public String getId() {
return id;
}

关于java - 解释使用 Hibernate 映射自动递增复合 id 序列中的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31362100/

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com