我在这个配置中基本上有一些对象(真实的数据模型有点复杂):
A 与 B 具有多对多关系。(B 具有 inverse="true")
B 与 C 具有多对一的关系。(我已将级联设置为“保存更新”)
C 是一种类型/类别表。
另外,我可能应该提到主键是由数据库在保存时生成的。
对于我的数据,我有时会遇到 A 有一组不同的 B 对象的问题,而这些 B 对象引用同一个 C 对象。
当我调用 session.saveOrUpdate(myAObject)
时,我收到一条休眠错误消息:"a different object with the same identifier value was already associated with the session: C"
。我知道hibernate不能在同一个会话中两次插入/更新/删除同一个对象,但是有没有办法解决这个问题?这似乎并不罕见。
在我研究这个问题的过程中,我看到人们建议使用 session.merge()
,但是当我这样做时,任何“冲突”的对象都会作为空白对象插入到数据库中,并且所有值都设置为 null。显然这不是我们想要的。
[编辑] 我忘了提到的另一件事是(出于我无法控制的架构原因),每次读取或写入都需要在单独的会话中完成。
很可能是因为 B 对象没有引用同一个 Java C 对象实例。它们指的是数据库中的同一行(即相同的主键),但它们是它的不同副本。
因此,正在发生的事情是管理实体的 Hibernate 会话将跟踪哪个 Java 对象对应于具有相同主键的行。
一种选择是确保引用同一行的对象 B 的实体实际上是引用 C 的同一对象实例。或者关闭该成员变量的级联。这种方式当 B 被持久化时 C 不是。不过,您必须单独手动保存 C。如果 C 是一个类型/类别表,那么这样做可能是有意义的。
只需将级联设置为 MERGE,就可以了。
你只需要做一件事。运行 session_object.clear()
,然后保存新对象。这将清除会话(恰如其名)并从会话中删除有问题的重复对象。
clear()
方法的 session_object
?
session.clear()
还会从会话中删除所有其他(非违规)对象。所以这不是一个好的选择。
我同意@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)
@EmbeddedId
,我应该设置这些策略中的任何一个吗?
这意味着您正在尝试使用对同一对象的引用在表中保存多行。
检查您的实体类的 id 属性。
@Id
private Integer id;
至
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
private Integer id;
使用以下命令将分配对象 ID 的任务从 Hibernate 转移到数据库:
<generator class="native"/>
这为我解决了这个问题。
解决上述问题的一种方法是覆盖 hashcode()
。
同时在保存前后刷新休眠会话。
getHibernateTemplate().flush();
将分离的对象显式设置为 null
也有帮助。
将注解 @GeneratedValue 添加到您要插入的 bean 中。
刚刚遇到此消息,但在 c# 代码中。不确定它是否相关(但完全相同的错误消息)。
我正在用断点调试代码,并在调试器处于断点时通过私有成员扩展了一些集合。在不挖掘结构的情况下重新运行代码会使错误消息消失。似乎查看私有延迟加载集合的行为使 NHibernate 加载了当时不应该加载的东西(因为它们在私有成员中)。
代码本身包含在一个相当复杂的事务中,该事务可以更新大量记录和许多依赖项,作为该事务(导入过程)的一部分。
希望为遇到此问题的其他人提供线索。
在 Hibernate 中找到“Cascade”属性并删除它。当您设置“级联”可用时,它将在与相关类有关系的另一个实体上调用其他操作(保存、更新和删除)。所以会发生相同的身份值。它对我有用。
几天前我遇到了这个错误,我花了太多时间来修复这个错误。
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 解释的。是的,这是最好的答案。这个例子是如何发生的。
尝试将您的查询代码放在之前。这解决了我的问题。例如改变这个:
query1
query2 - get the error
update
对此:
query2
query1
update
在我的情况下,只有 flush() 不起作用。我必须在flush()之后使用clear()。
public Object merge(final Object detachedInstance)
{
this.getHibernateTemplate().flush();
this.getHibernateTemplate().clear();
try
{
this.getHibernateTemplate().evict(detachedInstance);
}
}
可以生成相同错误消息的另一种情况,自定义 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;
可能会在插入期间导致约束验证(这很容易理解)或“具有相同标识符值的不同对象已经与会话相关联” - 这种情况不太明显。
在调用更新查询之前,您可能没有设置对象的标识符。
由于主键生成错误,我遇到了这个问题,当我插入这样的一行时:
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 生成器类更改为身份
<id name="id" type="int">
<column name="id" />
<generator class="identity" />
</id>
如果您使用 EntityRepository 则使用 saveAndFlush 而不是 save
如果在我的 IDE 中打开了一个表达式选项卡,它正在对导致此异常的对象进行休眠 get 调用。我试图删除同一个对象。此外,我在删除调用上有一个断点,这似乎是发生此错误所必需的。只需将另一个表达式选项卡设置为前选项卡或更改设置以使 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;
}
}
除了所有先前的答案之外,如果您为您的类使用值对象未在 VO Transformer 类中设置 id 属性,则可能在大型项目中解决此问题。
出现此问题的原因是您的子表中有不同的对象副本引用相同的原始对象,因此 spring 尝试将您的对象视为新对象,但在保存它时发现存在具有相同主键的原始对象。所以它给出了上述错误。
此问题的最佳解决方案是从 DB 加载整个对象(带有子实体的父实体)(您已经知道父对象的主键),然后从新对象(您正在尝试)更新从 DB 加载的对象中的值保存),然后保存从具有新值的数据库加载的对象。
这将更新您在数据库中的值,而不会出现上述错误。
PS-不需要更新 id,因为它们已经存在于从 DB 加载的对象中,只更新需要更改的值
如果您使用弹簧数据,另一种解决此问题的方法:
用对 repository.save() 的调用替换对 entityManager.persist() 的调用,以及
用对 repository.findBy 的调用替换对 entityManager.query().getResultList() 等的调用...
这样,弹簧数据就可以跟踪对象。它支持多个 get 和 persist 调用。
而不仅仅是 @Id 尝试 @Id @GeneratedValue(strategy = GenerationType.AUTO) 它对我有用
就我而言,我有 OneToOne 关系,在用外键保存一行后,尝试用相同的外键保存另一行会抛出相同的异常。这意味着需求不是OneToOne关系,而应该是ManyToOne。因此,我将其更改为 ManyToOne 并开始工作。
此错误通常是因为您在尝试插入重复数据时违反了唯一类型或主键的列。
确保每个实体的主键都不同。
主键必须是唯一的
只需提交当前事务。
currentSession.getTransaction().commit();
现在您可以开始另一个事务并对实体执行任何操作
不定期副业成功案例分享