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

khbbv19g  于 2021-07-06  发布在  Java
关注(0)|答案(21)|浏览(554)

我基本上在这个配置中有一些对象(实际的数据模型有点复杂):
a与b之间存在多对多关系(b有 inverse="true" )
b与c之间存在多对一关系(我有 cascade 设置为 "save-update" )
c是一种类型/类别表。
另外,我应该提到,主键是由数据库在保存时生成的。
对于我的数据,我有时会遇到这样的问题:a有一组不同的b对象,而这些b对象引用同一个c对象。
当我打电话的时候 session.saveOrUpdate(myAObject) ,我得到一个hibernate错误: "a different object with the same identifier value was already associated with the session: C" . 我知道hibernate不能在同一个会话中插入/更新/删除同一个对象两次,但是有什么方法可以解决这个问题吗?这种情况似乎并不少见。
在我研究这个问题的过程中,我看到有人建议使用 session.merge() ,但当我这样做时,任何“冲突”对象都会作为空对象插入到数据库中,所有值都设置为null。显然这不是我们想要的。
[编辑]另一件我忘记提到的事情是(由于我无法控制的体系结构原因),每次读写都需要在单独的会话中完成。

vnzz0bqm

vnzz0bqm1#

刚看到这条信息,但用的是c代码。不确定它是否相关(尽管与错误消息完全相同)。
我用断点调试代码,并在调试器处于断点时通过私有成员扩展了一些集合。在没有挖掘结构的情况下重新运行代码,错误消息就消失了。看起来,查看私有延迟加载集合的行为使得nhibernate加载了当时不应该加载的东西(因为它们在私有成员中)。
代码本身被 Package 在一个相当复杂的事务中,该事务可以作为该事务(导入过程)的一部分更新大量记录和许多依赖项。
希望能给其他遇到这个问题的人一个线索。

y53ybaqx

y53ybaqx2#

除了前面的所有答案之外,如果您对类使用value对象时没有在vo transformer类中设置id属性,那么在大型项目中可能会解决这个问题。

2ledvvac

2ledvvac3#

确保您的实体与所有Map实体具有相同的生成类型
例如:用户角色

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;

}
带Map的主实体

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

}

nbnkbykc

nbnkbykc4#

只要将cascade设置为merge,就可以了。

f45qwnt8

f45qwnt85#

在hibernate中找到“cascade”atribute并将其删除。当您将“cascade”设置为可用时,它将调用其他与相关类有关系的实体的操作(保存、更新和删除)。所以同样的情况也会发生。它对我起了作用。

yfjy0ee7

yfjy0ee76#

我同意@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();
    }
}

个人.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;
    }

}

此代码在我的应用程序中总是出错: A different object with the same identifier value was already associated with the session ,后来我发现我忘了自动增加主键!
我的解决方案是在主键上添加以下代码:

@GeneratedValue(strategy = GenerationType.AUTO)
0x6upsns

0x6upsns7#

这意味着您正试图保存表中引用同一对象的多行。
检查实体类的id属性。

@Id
private Integer id;

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

ou6hu8tu8#

很可能是因为b对象引用的不是同一个javac对象示例。它们引用的是数据库中的同一行(即相同的主键),但它们是数据库的不同副本。
因此,管理实体的hibernate会话将跟踪哪个java对象对应于具有相同主键的行。
一种方法是确保引用同一行的对象b的实体实际上引用的是c的同一个对象示例。或者关闭该成员变量的级联。这样,当b是持久的时,c不是。不过,您必须分别手动保存c。如果c是一个类型/类别表,那么这样做可能是有意义的。

jfgube3f

jfgube3f9#

另一种情况是可以生成相同的错误消息,自定义 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;

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

j1dl9f46

j1dl9f4610#

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

zzzyeukh

zzzyeukh11#

如果让ide中的一个表达式选项卡处于打开状态,该选项卡正在对导致此异常的对象进行hibernate get调用。我想删除同一个对象。另外,delete调用上有一个断点,这似乎是发生此错误所必需的。只需将另一个表达式选项卡设为前选项卡或更改设置,使ide不会在断点处停止,就可以解决此问题。

7xzttuei

7xzttuei12#

将注解@generatedvalue添加到要插入的bean中。

uxh89sit

uxh89sit13#

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

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

k97glaaz14#

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

weylhg0b

weylhg0b15#

在我的例子中,只有flush()不起作用。我必须在flush()之后使用clear()。

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

相关问题