Hibernate错误:org.hibernate.NonUniqueObjectException:具有相同标识符值的另一个对象已与会话关联


114

我有两个用户对象,而在尝试使用以下方法保存对象时

session.save(userObj);

我收到以下错误:

Caused by: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:
[com.pojo.rtrequests.User#com.pojo.rtrequests.User@d079b40b]

我正在使用创建会话

BaseHibernateDAO dao = new BaseHibernateDAO();          

rtsession = dao.getSession(userData.getRegion(),
                           BaseHibernateDAO.RTREQUESTS_DATABASE_NAME);

rttrans = rtsession.beginTransaction();
rttrans.begin();

rtsession.save(userObj1);
rtsession.save(userObj2);

rtsession.flush();
rttrans.commit();

rtsession.close(); // in finally block

我还尝试session.clear()过保存之前的操作,但仍然没有运气。

这是第一次,当用户请求到来时,我得到了会话对象,所以我得到了为什么要说该对象存在于会话中的信息。

有什么建议?


这是另一个帮助解决我的问题的精彩线程getj2ee.over-blog.com/…–
Reddymails

Answers:


173

我有很多次此错误,很难追踪...

基本上,休眠是指您有两个对象具有相同的标识符(相同的主键),但它们不是同一对象。

我建议您分解代码,即注释掉位,直到错误消失,然后再放回代码,直到返回为止,您应该找到错误。

它通常是通过级联保存发生的,对象A和对象B之间有一个级联保存,但是对象B已经与该会话相关联,但与实例A不在同一个实例B上。

您正在使用哪个主键生成器?

我问这个错误的原因与您如何告诉休眠状态确定对象的持久状态(即对象是否持久)有关。该错误可能是由于休眠试图持久化一个已经持久的对象而发生的。实际上,如果您使用保存,休眠将尝试并持久化该对象,并且可能已经有一个对象具有与该会话相关联的相同主键。

假设您有一个基于主键组合(表1和列2)的具有10行的表的休眠类对象。现在,您已在某个时间从表中删除了5行。现在,如果您尝试再次添加相同的10行,而hibernate尝试将对象持久化到数据库中,则将添加5条已经删除的行而不会出现错误。现在剩下的5行已经存在,将抛出此异常。

因此,简单的方法是检查您是否已更新/删除了表中属于任何内容的任何值,后来又尝试再次插入相同的对象


4
不错的答案²。主键是我的问题,通过GeneratedValue为postgresql设置了一个序列来解决。
罗德里戈·法拉利

2
我遇到过同样的问题。就我而言,我在代码中搜索了一个对象,而当第一个对象处于休眠状态时,我试图在其他代码段中构建一个具有相同ID的新对象。
dellasavia 2014年

18

这只是休眠带来的问题多于解决的问题之一。在我的情况下,有许多具有相同标识符0的对象,因为它们是新对象,没有一个。数据库生成它们。我读过某处未设置0信号ID。持久保存它们的直观方法是遍历它们并说休眠以保存对象。但是您不能这样做-“当然,您应该知道休眠方式是这样的,因此您必须这样做。”因此,现在我可以尝试将ID更改为Long而不是long,然后看看它是否可以工作。最后,您可以使用一个简单的映射器来轻松完成此操作,因为休眠只是额外的不透明负担。另一个示例:尝试从一个数据库中读取参数并将其持久保存在另一个数据库中,将迫使您手动执行几乎所有工作。


13
我也讨厌休眠...和数据库...在所有问题引起了我之后,我想使用文本文件会更容易(我在开玩笑,但仍然...)。
伊戈尔·波波夫

在我的情况下,有许多具有相同标识符0的对象,因为它们是新对象,没有一个。数据库生成它们。我读过某处未设置0信号ID。持久保存它们的直观方法是遍历它们并说休眠以保存对象。我需要完全做到这一点。你能告诉我这样做的“休眠方式”是什么?
拉姆西斯(Ramses)2014年

在我的情况下,我必须使用session.merge(myobject),因为我有该对象的两个实例。通常在实体被持久化并从会话中分离出来时发生。请求此实体的另一个实例进行休眠。第二个实例保持连接到会话。第一个实例被修改。更多信息,请访问 getj2ee.over-blog.com/…–
Reddymails

它不是让问题冬眠,而是缺乏对它的工作原理的了解
ACV

17

使用方法session.evict(object);的功能evict()用于从会话缓存中删除实例。因此,第一次保存对象时,请session.save(object)在从高速缓存中逐出对象之前通过调用方法保存对象。以相同的方式通过调用evict()session.saveOrUpdate(object)session.update(object)之前更新对象。


11

当您使用相同的会话对象进行读写时,可能会发生这种情况。怎么样?假设您创建了一个会话。您从雇员表中使用主键Emp_id = 101读取了一条记录。现在,您已经用Java修改了该记录。您将把Employee记录保存在数据库中。我们在这里没有关闭会议。由于读取的对象也保留在会话中。它与我们要编写的对象冲突。因此,出现此错误。


9

正如上面已经指出的那样,当我cascade=all处于one-to-many关系的两端时,我遇到了这个问题,所以让我们假设A-> B(A中的一对多,B中的多对一)并且正在更新B在A中,然后调用saveOrUpdate(A),这导致了循环保存请求,即A的保存触发了B的保存,从而触发了A的保存...,并且在第三种情况下,尝试了A的entity()被添加到sessionPersistenceContext中,引发了plicateObject异常。
  我可以通过从一端删除级联来解决它。



5

session.merge(obj)如果要对具有相同标识符持久对象的不同会话进行保存,则可以使用。
它起作用了,我之前也遇到过同样的问题。


4

我也遇到了这个问题,很难找到错误。

我遇到的问题如下:

Dao已使用其他休眠会话读取了该对象。

为避免此异常,只需使用稍后将要保存/更新该对象的dao重新读取该对象。

所以:

class A{      

 readFoo(){
       someDaoA.read(myBadAssObject); //Different Session than in class B
    }

}

class B{



 saveFoo(){
       someDaoB.read(myBadAssObjectAgain); //Different Session than in class A
       [...]
       myBadAssObjectAgain.fooValue = 'bar';
       persist();
    }

}

希望能节省很多人的时间!


4

我通过以下方式遇到了这个问题:

  1. 删除对象(使用HQL)
  2. 立即存储具有相同ID的新对象

我通过删除后刷新结果并在保存新对象之前清除缓存来解决它

String delQuery = "DELETE FROM OasisNode";
session.createQuery( delQuery ).executeUpdate();
session.flush();
session.clear();

4

当我们更新用于从数据库中获取对象的会话的同一对象时,会发生此问题。

您可以使用休眠的合并方法代替更新方法。

例如,首先使用session.get(),然后可以使用session.merge(对象)。此方法不会造成任何问题。我们还可以使用merge()方法更新数据库中的对象。


3

在会话中获取对象,这里是一个示例:

MyObject ob = null;
ob = (MyObject) session.get(MyObject.class, id);

3
很好的尝试,但是返回的对象“ ob”没有更新的数据(考虑到它是在运行时通过应用程序更新的,介于从数据库检索的对象和保存对象之间)。
亚历克斯

2

您的ID映射正确吗?如果数据库负责通过标识符创建ID,则需要将userobject映射到该标识符。


2

我在删除对象时遇到了这个问题,既没有逐出也没有明确的帮助。

/**
 * Deletes the given entity, even if hibernate has an old reference to it.
 * If the entity has already disappeared due to a db cascade then noop.
 */
public void delete(final Object entity) {
  Object merged = null;
  try {
    merged = getSession().merge(entity);
  }
  catch (ObjectNotFoundException e) {
    // disappeared already due to cascade
    return;
  }
  getSession().delete(merged);
}

2

在重复对象开始的位置之前,应先关闭会话,然后再开始新的会话

session.close();      
session = HibernateUtil.getSessionFactory().openSession();

因此,以这种方式,在一个会话中,具有相同标识符的实体不超过一个。


2

参加晚会,但可能对新用户有所帮助-

当我选择使用记录getsession() 并再次使用同一会话更新具有相同标识符的另一条记录时,出现此问题。在下面添加了代码。

Customer existingCustomer=getSession().get(Customer.class,1);
Customer customerFromUi;// This customer details comiong from UI with identifer 1

getSession().update(customerFromUi);// Here the issue comes

永远不要这样做。解决方案是在更新前退出会话或更改业务逻辑。


1

检查是否忘记将@GenerateValue用作@Id列。电影和流派之间的多对多关系也存在相同的问题。程序引发了Hibernate错误:org.hibernate.NonUniqueObjectException:具有相同标识符值的另一个对象已经与会话错误相关联。后来我发现,我只需要确保GenreId get方法具有@GenerateValue。


我如何将您的解决方案应用于我的问题?我如何在Task类中创建一个id列?stackoverflow.com/questions/55732955/…–
sbattou

1

只需检查id是否为null或0,例如

if(offersubformtwo.getId()!=null && offersubformtwo.getId()!=0)

在添加或更新内容从表单设置为Pojo的地方


1

我是NHibernate的新手,我的问题是与保存对象相比,我使用不同的会话查询对象。因此,保存会话不了解该对象。

似乎很明显,但是从阅读以前的答案开始,我到处都在寻找2个对象,而不是2个会话。


1

@GeneratedValue(strategy = GenerationType.IDENTITY),将此注释添加到实体bean的主键属性中应该可以解决此问题。


1

我解决了这个问题。
实际上,这是因为我们忘记了在bean类中实现PK属性的Generator Type的实现。因此,使其像

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;

当我们持久化bean的对象时,每个对象都获得了相同的ID,因此保存了第一个对象,而当另一个要持久化的对象时,HIB FW通过这种Exception: org.hibernate.NonUniqueObjectException:具有相同标识符值的不同对象的类型已经与该会话相关联。


1

发生问题是因为您在同一个休眠会话中尝试保存具有相同标识符的两个对象。有两种解决方案:-

  1. 发生这种情况是因为您没有为id字段正确配置您的mapping.xml文件,如下所示:-

    <id name="id">
      <column name="id" sql-type="bigint" not-null="true"/>
      <generator class="hibernateGeneratorClass"</generator>
    </id>
  2. 重载getsession方法以接受isSessionClear之类的参数,并在返回当前会话之前清除会话,如下所示

    public static Session getSession(boolean isSessionClear) {
        if (session.isOpen() && isSessionClear) {
            session.clear();
            return session;
        } else if (session.isOpen()) {
            return session;
        } else {
            return sessionFactory.openSession();
        }
    }

这将导致现有的会话对象被清除,即使休眠不生成唯一标识符,假设您已经使用Auto_Increment之类的东西为主键正确配置了数据库,它也将为您工作。


1

我有一个类似的问题。就我而言,我忘记increment_by将数据库中的值设置为与cache_sizeand相同allocationSize。(箭头指向提到的属性)

SQL:

CREATED         26.07.16
LAST_DDL_TIME   26.07.16
SEQUENCE_OWNER  MY
SEQUENCE_NAME   MY_ID_SEQ
MIN_VALUE       1
MAX_VALUE       9999999999999999999999999999
INCREMENT_BY    20 <-
CYCLE_FLAG      N
ORDER_FLAG      N
CACHE_SIZE      20 <-
LAST_NUMBER     180

Java:

@SequenceGenerator(name = "mySG", schema = "my", 
sequenceName = "my_id_seq", allocationSize = 20 <-)

1

除了wbdarby所说的以外,它甚至可能在通过将对象的标识符提供给HQL来获取对象时发生。如果尝试在同一会话中修改对象字段并将其保存回数据库(修改可以插入,删除或更新),则会出现此错误。在保存修改后的对象或创建全新的会话之前,请尝试清除休眠会话。

希望我有所帮助;-)


1

我有一个相同的错误,就是我用杰克逊(Jackson)的新套件替换了我的套件。

为了解决这个问题,我保留了现有的集合,我使用来从旧集合中删除未知元素到新列表中retainAll。然后,我用添加新的addAll

    this.oldSet.retainAll(newSet);
    this.oldSet.addAll(newSet);

无需进行会话并进行操作。


1

试试这个。下面为我​​工作!

hbm.xml文件中

  1. 我们需要将dynamic-updateclass标签的属性设置为true

    <class dynamic-update="true">
  2. 将generator标记的class属性设置在unique列下identity

    <generator class="identity">

注意:将唯一列设置为,identity而不是assigned


0

对我有用的另一件事是使实例变量Long代替long


我有主键变量long id;将其更改为Long ID;工作了

祝一切顺利


0

您总是可以进行会话刷新。Flush会在会话中同步所有对象的状态(如果我错了,请有人纠正我),也许在某些情况下可以解决您的问题。

实现自己的equals和哈希码可能也对您有所帮助。


0

您可以检查级联设置。您的模型上的Cascade设置可能是导致这种情况的原因。我删除了级联设置(基本上不允许级联插入/更新),这解决了我的问题


0

我也发现了此错误。对我有用的是确保主键(自动生成)不是PDT(即long,int等),而是对象(即Long,Integer等)。

创建对象进行保存时,请确保传递null而不是0。



0

我已经解决了类似的问题:

plan = (FcsRequestPlan) session.load(plan.getClass(), plan.getUUID());
while (plan instanceof HibernateProxy)
    plan = (FcsRequestPlan) ((HibernateProxy) plan).getHibernateLazyInitializer().getImplementation();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.