Hibernate 错误: 具有相同标识符值的不同对象已经与会话关联

在这个配置中,我实际上有一些对象(实际的数据模型有点复杂) :

  • A has a many-to-many relationship with B. (B has inverse="true")
  • B 与 C 有多对一的关系(我将 cascade设置为 "save-update")
  • C is a kind of type/category table.

另外,我可能应该提到,主键是由保存时的数据库生成的。

对于我的数据,我有时会遇到这样的问题: A 有一组不同的 B 对象,而这些 B 对象引用同一个 C 对象。

When I call session.saveOrUpdate(myAObject), I get a hibernate error saying: "a different object with the same identifier value was already associated with the session: C". I know that hibernate can't insert/update/delete the same object twice in the same session, but is there some way around this? This doesn't seem like it would be that uncommon of a situation.

在我研究这个问题的过程中,我看到有人建议使用 session.merge(),但是当我这样做时,任何“冲突的”对象都作为空对象插入到数据库中,所有值都设置为 null。显然这不是我们想要的。

[Edit] Another thing I forgot to mention is that (for architectural reasons beyond my control), each read or write needs to be done in a separate session.

369150 次浏览

最有可能的原因是 B 对象没有引用同一个 JavaC 对象实例。它们引用数据库中的相同行(即相同的主键) ,但是它们是不同的副本。

因此,管理实体的 Hibernate 会话将跟踪哪个 Java 对象对应于具有相同主键的行。

一种选择是确保引用同一行的对象 B 的实体实际上引用了 C 的同一个对象实例。或者关闭该成员变量的级联。这样,当 B 被持久化时,C 不会被持久化。不过,您必须单独手动保存 C。如果 C 是一个类型/类别表,那么这样做可能是有意义的。

在调用更新查询之前,可能没有设置对象的标识符。

只是在 c # 代码中看到了这个消息。不确定是否相关(但是完全相同的错误消息)。

我使用断点调试代码,并在调试器处于断点时通过私有成员展开一些集合。在不深入挖掘结构的情况下重新运行代码可以消除错误消息。查看私有延迟加载集合的行为似乎使 NHibernate 加载了当时不应该加载的内容(因为它们位于私有成员中)。

代码本身包装在一个相当复杂的事务中,该事务可以更新大量记录和许多依赖项,作为该事务(导入过程)的一部分。

希望能给其他碰到这个问题的人提供线索。

将从 Hibernate 分配对象 ID 的任务传递给数据库,方法是:

<generator class="native"/>

这为我解决了问题。

你只需要做一件事。运行 session_object.clear(),然后保存新对象。这将清除会话(正确命名)并从会话中删除有问题的重复对象。

解决上述问题的一种方法是重写 hashcode()
Also flush the hibernate session before and after save.

getHibernateTemplate().flush();

将分离的对象显式设置为 null也有帮助。

添加注释 @ GeneratedValue 你插入的豆子。

几天前我出现了这个错误,修复这个错误花费了太多时间。

 public boolean save(OrderHeader header) {
Session session = sessionFactory.openSession();




Transaction transaction = session.beginTransaction();


try {
session.save(header);


for (OrderDetail detail : header.getDetails()) {
session.save(detail);
}


transaction.commit();
session.close();


return true;
} catch (HibernateException exception) {


exception.printStackTrace();
transaction.rollback();
return false;
}
}

在我得到这个错误之前,我没有提到 OrderDetil 对象上的 ID 生成类型。当不生成 OrderDetails 的 Id 时,它将每个 OrderDetails 对象的 Id 保持为0。这就是 # jbx 所解释的。是的,这是最好的答案。这个例子是怎么发生的。

把级联设置为 MERGE 就行了。

我同意@Hemant Kumar,非常感谢。根据他的解决方案,我解决了我的问题。

例如:

@Test
public void testSavePerson() {
try (Session session = sessionFactory.openSession()) {
Transaction tx = session.beginTransaction();
Person person1 = new Person();
Person person2 = new Person();
person1.setName("222");
person2.setName("111");
session.save(person1);
session.save(person2);
tx.commit();
}
}

Person.java

public class Person {
private int id;
private String name;


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


public void setId(int id) {
this.id = id;
}


@Basic
@Column(name = "name")
public String getName() {
return name;
}


public void setName(String name) {
this.name = name;
}


}

这段代码在我的应用程序中总是出错: 后来我发现我忘了 到 自动增加我的主键!

我的解决方案是在主键上添加以下代码:

@GeneratedValue(strategy = GenerationType.AUTO)

尝试将查询代码放在。 解决了我的问题。 例如:

query1
query2 - get the error
update

回到这里:

query2
query1
update

在 Hibernate 中找到“ Cascade”属性并删除它。当您设置“级联”可用时,它将调用其他操作(保存、更新和删除)与相关类有关的另一个实体。所以相同的恒等式值会发生。 对我很有效。

我遇到了这个问题,因为主键生成是错误的,当我插入这样一行时:

public void addTerminal(String typeOfDevice,Map<Byte,Integer> map) {
// TODO Auto-generated method stub
try {
Set<Byte> keySet = map.keySet();
for (Byte byte1 : keySet) {
Device device=new Device();
device.setNumDevice(DeviceCount.map.get(byte1));
device.setTimestamp(System.currentTimeMillis());
device.setTypeDevice(byte1);
this.getHibernateTemplate().save(device);
}
System.out.println("hah");
}catch (Exception e) {
// TODO: handle exception
logger.warn("wrong");
logger.warn(e.getStackTrace()+e.getMessage());
}
}

我将 id 生成器类更改为 Identity

<id name="id" type="int">
<column name="id" />
<generator class="identity"  />
</id>

在我的例子中,只有 rush ()不起作用。

public Object merge(final Object detachedInstance)
{
this.getHibernateTemplate().flush();
this.getHibernateTemplate().clear();
try
{
this.getHibernateTemplate().evict(detachedInstance);
}
}

This means you are trying to save multiple rows in your table with the reference to the same object.

检查实体类的 id 属性。

@Id
private Integer id;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
private Integer id;

如果使用 EntityRepository,则使用 saveAndFlush 代替 save

如果在 IDE 中打开一个表达式选项卡,它正在对引起此异常的对象进行休眠获取调用。我试图删除同一个对象。另外,我在 delete 调用上有一个断点,这似乎是使这个错误发生所必需的。简单地将另一个表达式选项卡作为前面的选项卡或者更改设置,这样 IDE 就不会在断点处停止,从而解决了这个问题。

确保您的实体与所有映射实体具有相同的生成类型

例如: 用户角色

public class UserRole extends AbstractDomain {


@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;


private String longName;


private String shortName;


@Enumerated(EnumType.STRING)
private CommonStatus status;


private String roleCode;


private Long level;


@Column(columnDefinition = "integer default 0")
private Integer subRoleCount;


private String modification;


@ManyToOne(fetch = FetchType.LAZY)
private TypeOfUsers licenseType;

}

单元:

public class Modules implements Serializable {


@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;


private String longName;


private String shortName;

}

具有映射的主实体

public class RoleModules implements Serializable{


@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;


@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private UserRole role;


@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private Modules modules;


@Type(type = "yes_no")
private boolean isPrimaryModule;


public boolean getIsPrimaryModule() {
return isPrimaryModule;
}

}

除了前面的所有答案之外,如果您在类中使用 Value Object 时没有在 VO Transformer 类中设置 id 属性,那么在大型项目中可能会修复这个问题。

just commit current transaction.

currentSession.getTransaction().commit();

现在您可以开始另一个事务并在实体上执行任何操作

另一种情况下,同样的错误消息可以通过生成自定义 allocationSize:

@Id
@Column(name = "idpar")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "paramsSequence")
@SequenceGenerator(name = "paramsSequence", sequenceName = "par_idpar_seq", allocationSize = 20)
private Long id;

没有匹配

alter sequence par_idpar_seq increment 20;

可能导致在插入过程中进行约束验证(这种验证很容易理解) ,或者“具有相同标识符值的不同对象已经与会话相关联”——这种情况不太明显。

这个问题的原因是,在子表中引用同一个原始对象的对象有不同的副本,所以 Spring 试图将对象视为新对象,但是在保存它的同时,标识出有一个具有相同主键的原始对象。所以它给出了上述误差。

这个问题的最佳解决方案是从 DB (您已经知道父对象的主键)加载整个对象(带子实体的父实体) ,然后从新对象(您试图保存的对象)更新从 DB 加载的对象中的值,然后保存从具有新值的 DB 加载的对象。

This will update your values in the DB without giving above error.

PS-不需要更新 id,因为它们已经存在于从 DB 加载的对象中,只需更新需要更改的值

如果你使用的是弹簧数据,另一种解决方法是:

  • 将对 entityManager.persist()的调用替换为对 repository.save()的调用,并且
  • 将对 entityManager.query().getResultList()等的调用替换为对 repository.findBy...的调用

通过这种方式,Spring 数据可以跟踪对象。

而不仅仅是@Id 试试看 @ Id @ GeneratedValue (Strategy = GenerationType.AUTO) it worked for me

在我的例子中,我有一个 OneToOne 关系,在用外键保存一行之后,尝试用相同的外键保存另一行,引发相同的异常。这意味着需求不是 OneToOne 关系,而应该是 ManyToOne 关系。所以我把它改成了 ManyToOne,它开始工作了。

此错误通常发生在试图插入重复数据时违反了唯一类型或主键的列。

确保每个实体的主键不同。

主键必须是唯一的