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是一种类型/类别表
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;