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

Java 休眠错误:具有相同标识符值的不同对象已与会话关联,java,hibernate,Java,Hibernate,我在这个配置中基本上有一些对象(实际数据模型稍微复杂一些): A与B有多对多关系。(B有inverse=“true”) B与C之间存在多对一关系(我将级联设置为“保存更新”) C是一种类型/类别表 另外,我可能应该提到,主键是由数据库在保存时生成的 对于我的数据,我有时会遇到这样的问题:A有一组不同的B对象,而这些B对象引用同一个C对象 当我调用session.saveOrUpdate(myAObject)时,我得到一个hibernate错误,它说:“具有相同标识符值的另一个对象已经与会话关

我在这个配置中基本上有一些对象(实际数据模型稍微复杂一些):

  • A与B有多对多关系。(B有
    inverse=“true”
  • B与C之间存在多对一关系(我将
    级联
    设置为
    “保存更新”
  • C是一种类型/类别表
另外,我可能应该提到,主键是由数据库在保存时生成的

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

当我调用
session.saveOrUpdate(myAObject)
时,我得到一个hibernate错误,它说:
“具有相同标识符值的另一个对象已经与会话关联:C”
。我知道hibernate不能在同一个会话中插入/更新/删除同一个对象两次,但是有办法解决这个问题吗?这种情况似乎并不罕见

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


[编辑]另一件我忘记提及的事情是(出于我无法控制的架构原因),每次读写都需要在单独的会话中完成。

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

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


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

在调用update query之前,您可能没有设置对象的标识符。

只是在C代码中遇到了此消息。不确定它是否相关(尽管错误消息完全相同)

我用断点调试代码,并在调试器处于断点时通过私有成员扩展了一些集合。在没有挖掘结构的情况下重新运行代码使错误消息消失。看起来,查看私有延迟加载集合的行为使得NHibernate加载了当时不应该加载的东西(因为它们位于私有成员中)

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


希望能为其他遇到此问题的人提供线索。

使用以下方法将对象ID分配任务从Hibernate转移到数据库:



这为我解决了问题。

你只需要做一件事。运行
session\u object.clear()
,然后保存新对象。这将清除会话(名称恰当),并从会话中删除有问题的重复对象。

解决上述问题的一种方法是覆盖
hashcode()

在保存之前和之后刷新hibernate会话

getHibernateTemplate().flush();
将分离的对象显式设置为
null
也有帮助。

添加注释 @生成值
添加到您正在插入的bean。

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

 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,它会将每个OrderDetail对象的id保持为0。这是jbx解释的。是的,这是最好的答案。这是它如何发生的一个例子

只要将cascade设置为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)

请尝试将查询的代码放在前面。 这解决了我的问题。 e、 g.改变这一点:

query1 
query2 - get the error 
update
为此:

query2
query1
update
在Hibernate中找到“Cascade”atribute并将其删除。当您将“级联”设置为可用时,它将调用与相关类有关系的其他实体上的其他操作(保存、更新和删除)。所以相同的身份值也会发生。
它对我起了作用。

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

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());
        }
}
public void addTerminal(字符串类型的设备,映射){
//TODO自动生成的方法存根
试一试{
Set keySet=map.keySet();
for(字节字节1:键集){
设备=新设备();
device.setNumDevice(DeviceCount.map.get(字节1));
device.setTimestamp(System.currentTimeMillis());
device.setTypeDevice(字节1);
此.getHibernateTemplate().save(设备);
}
System.out.println(“hah”);
}捕获(例外e){
//TODO:处理异常
记录器。警告(“错误”);
logger.warn(e.getStackTrace()+e.getMessage());
}
}
我将id生成器类更改为identity

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

I
public Object merge(final Object detachedInstance)
    {
        this.getHibernateTemplate().flush();
        this.getHibernateTemplate().clear();
        try
        {
            this.getHibernateTemplate().evict(detachedInstance);
        }
}
@Id
private Integer id;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
private Integer id;
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;
}
currentSession.getTransaction().commit();
@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;