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

时间:2009-07-02 12:31:08

标签: java hibernate orm

我有两个用户对象,当我尝试使用

保存对象时
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(),但仍然没有运气。

这是第一次我收到用户请求时获取会话对象,所以我得到了为什么说会话中存在该对象。

有什么建议吗?

37 个答案:

答案 0 :(得分:164)

我多次犯过这个错误,跟踪起来很难......

基本上,hibernate所说的是你有两个具有相同标识符的对象(相同的主键),但它们不是同一个对象。

我建议你分解你的代码,即注释掉比特,直到错误消失,然后把代码放回去,直到它回来,你应该找到它的错误。

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

您使用的主键生成器是什么?

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

示例

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

因此,简单的方法是检查您是否更新/删除了表格中的任何值,这是某些内容的一部分,之后您是否尝试再次插入相同的对象

答案 1 :(得分:18)

这只是hibernate解决问题的一个问题。 在我的情况下,有许多具有相同标识符0的对象,因为它们是新的并且没有。 db生成它们。在某处我读过0信号Id未设置。保持它们的直观方式是迭代它们并说hibernate来保存对象。但是你不能这样做 - “当然你应该知道hibernate可以这样做,所以你必须......” 所以现在我可以尝试将Ids更改为Long而不是long,看看它是否有效。 最后,通过自己的简单映射器更容易实现它,因为hibernate只是一个额外的不明显负担。 另一个例子:尝试从一个数据库中读取参数并将其保存在另一个数据库中会强制您手动执行几乎所有工作。但是如果你不得不这样做,那么使用hibernate只是额外的工作。

答案 2 :(得分:13)

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

答案 3 :(得分:8)

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

答案 4 :(得分:7)

正如有人已经指出的那样,当我在cascade=all关系的两端都有one-to-many时遇到了这个问题,所以让我们假设A - > B(来自A的一对多和来自B的多对一)并且在A中更新B的实例然后调用saveOrUpdate(A),它导致循环保存请求,即保存A触发器保存B触发保存A ...而在第三个实例中,当尝试将实体(A)添加到sessionPersistenceContext时,抛出了duplicateObject异常。我可以通过从一端移除级联来解决它。

答案 5 :(得分:4)

我遇到了这个问题:

  1. 删除对象(使用HQL)
  2. 立即存储具有相同ID的新对象
  3. 我通过在删除后刷新结果并在保存新对象之前清除缓存来解决它

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

答案 6 :(得分: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();
    }

}

希望能节省一些人很多时间!

答案 7 :(得分:3)

获取会话内的对象,这是一个例子:

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

答案 8 :(得分:2)

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

答案 9 :(得分: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);
}

答案 10 :(得分:1)

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

  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();
        }
    }
    
  3. 这将导致现有会话对象被清除,即使hibernate没有生成唯一标识符,假设您已使用Auto_Increment之类的东西为主键正确配置了数据库,它应该适合您。

答案 11 :(得分:1)

在重复对象开始的位置之前,您应该关闭会话 然后你应该开始一个新的会议

 [
        {"name":"imageurl","value":"/images/Image1.nii", parts: [
            {"name":"3d","value":"3d0"},
            {"name":"sliceX","value":"sliceX0"},
            {"name":"sliceY","value":"sliceY0"},    
            {"name":"sliceZ","value":"sliceZ0"},
        ]},

        {"name":"imageurl","value":"/images/Image2.nii", parts: [
            {"name":"3d","value":"3d1"},
            {"name":"sliceX","value":"sliceX1"},
            {"name":"sliceY","value":"sliceY1"},
            {"name":"sliceZ","value":"sliceZ1"}
        ]}
    ]

因此,在一个会话中,只有一个实体具有相同的标识符。

答案 12 :(得分:1)

试试这个。以下对我有用!

hbm.xml档案

  1. 我们需要将class tag的dynamic-update属性设置为true

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

    <generator class="identity">
    
  3. 注意:将唯一列设置为identity而不是assigned

答案 13 :(得分:1)

我有类似的问题。就我而言,我忘记了将数据库中的increment_by值设置为与cache_sizeallocationSize使用的值相同。 (箭头指向上述属性)

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

爪哇:

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

答案 14 :(得分:1)

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

希望我帮忙; - )

答案 15 :(得分:1)

我有同样的错误,我用杰克逊换来的新套装取代了我的套装。

为了解决这个问题,我保留了现有的集合,我从旧集合中删除了未知的元素到class Cruise(models.Model): code = models.CharField(max_length=10) destination = models.CharField(max_length=60) url = models.URLField ship = models.ForeignKey(Ship) duration = models.IntegerField start = models.DateField end = models.DateField added = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) port = models.CharField(max_length=30) 的新列表中。 然后我用retainAll添加新的。

addAll

无需召开会议并对其进行操作。

答案 16 :(得分:1)

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

答案 17 :(得分:1)

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

答案 18 :(得分:1)

我是NHibernate的新手,我的问题是我使用不同的会话来查询我的对象,而不是保存它。因此保存会话不知道该对象。

这似乎是显而易见的,但从阅读之前的答案,我到处寻找2个对象,而不是2个会话。

答案 19 :(得分:1)

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

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

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

答案 20 :(得分:1)

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

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

添加或更新内容从表单到Pojo的位置

答案 21 :(得分:1)

默认情况下使用身份策略,但我通过添加对其进行了修复

@ID
@GeneratedValue(strategy = GenerationType.IDENTITY)

答案 22 :(得分:1)

我只是遇到了同样的问题。我通过添加以下行来解决它:

@GeneratedValue(strategy=GenerationType.IDENTITY)

enter image description here

答案 23 :(得分:1)

检查是否忘记将@GenerateValue放入@Id列。 电影和流派之间有很多很多关系,我也有同样的问题。该计划投了 Hibernate错误:org.hibernate.NonUniqueObjectException:具有相同标识符值的另一个对象已与会话关联 错误。 后来我发现我必须确保你有@GenerateValue到GenreId get方法。

答案 24 :(得分:0)

晚会,但可能会为即将到来的用户提供帮助 -

当我使用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

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

答案 25 :(得分:0)

我们正在使用旧版本的hibernate(3.2.6),对我们来说,问题在于Hibernate期望结果集中的第一列是生成的主键。花了我很长时间才弄清楚这一点。

解决方案:确保在DDL中生成的主键始终是第一列。 解决方案2:更新休眠状态

答案 26 :(得分:0)

解决此问题的一种解决方法是尝试在进行任何更新之前从hibernate cache / db中读取对象,然后继续。

示例:

            OrderHeader oh = orderHeaderDAO.get(orderHeaderId);
            oh.setShipFrom(facilityForOrder);
            orderHeaderDAO.persist(oh);

注意:请记住,这不能解决根本原因,但可以解决问题。

答案 27 :(得分:0)

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

plan = (FcsRequestPlan) session.load(plan.getClass(), plan.getUUID());
while (plan instanceof HibernateProxy)
    plan = (FcsRequestPlan) ((HibernateProxy) plan).getHibernateLazyInitializer().getImplementation();

答案 28 :(得分:0)

我解决了这个问题。
实际上这是因为我们忘记了在bean类中实现了Generator属性的PK属性。所以把它变成像

这样的任何类型
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;

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

答案 29 :(得分:0)

这有帮助吗?

User userObj1 = new User();
User userObj2 = userObj1;
.
.
.
rtsession.save(userObj1);
rtsession.save(userObj2); 

答案 30 :(得分:0)

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

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

答案 31 :(得分:0)

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

答案 32 :(得分:0)

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

实现自己的equals和hashcode也可以帮到你。

答案 33 :(得分:0)

另一件对我有用的事情是将实例变量Long替换为long


我的主键变长ID; 将其改为Long id;工作

一切顺利

答案 34 :(得分:-1)

在我的运行中,我遵循

1-切换到实体中的Lazy密钥 2-从Maven下载最新的

- http://mvnrepository.com/artifact/org.javassist/javassist/3.19.0-GA

答案 35 :(得分:-1)

在我的模型对象类中,我定义了这样的注释

@Entity
@Table(name = "user_details")
public class UserDetails {  
    @GeneratedValue
    private int userId;
    private String userName;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }   

    @Id
    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }
}

当我将@Id和@GenerateValue注释同时写在@变量声明时,问题得到了解决。

@Entity
@Table(name = "user_details")
public class UserDetails {
    @Id
    @GeneratedValue
    private int userId;
    private String userName;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }   

    public int getUserId() {
        return userId;
    }
...
}

希望这是有帮助的

答案 36 :(得分:-1)

这是因为您打开会话可能是为了获取数据,然后您忘记关闭它。 当您删除再次打开会话时,它将变为错误。

解: 每个功能都应该打开和关闭会话

session.getTransaction.begin(); /* your operation */ session.close()