You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hibernate 5.6克隆列时触发EntityExistsException异常求助

Hibernate 5.6克隆Column关联属性时EntityExistsException问题解决

问题场景

使用Hibernate 5.6,数据结构为Profile -> Groups -> Columns -> ColumnProperties,需要克隆某个Profile的Column并附加到指定列组中,执行时出现如下错误:

10:58:39,793 ERROR
[com.myApp.core.rmgt.profile.service.RmgtProfileServiceImpl] (default
task-2) javax.persistence.EntityExistsException: A different object
with the same identifier value was already associated with the session
:
[com.myapp.core.rmgt.profile.business.object.RmgtColumnPropertyBVOImpl#124320]

映射文件

<hibernate-mapping>
    <class name="com.myapp.core.rmgt.profile.business.object.RmgtProfileBVOImpl" table="rmgt_t_profile" proxy="com.myapp.core.rmgt.profile.common.business.object.RmgtProfileBVO">
        <id name="rowguid" column="rowguid" type="java.lang.Long">
            <generator class="sequence">
                <param name="sequence_name">rmgt_t_profile_rowguid_seq</param>
            </generator>
        </id>  
        ...
         <set name="groups" table="rmgt_t_group" lazy="false" cascade="all-delete-orphan" order-by="rmgtg_group_order asc">
            <key column="rmgtg_fk_profile_id" not-null="true"/>     
            <one-to-many class="com.myapp.core.rmgt.profile.business.object.RmgtGroupBVOImpl"/>
        </set>
    </class>
</hibernate-mapping>

<hibernate-mapping>
    <class name="com.myapp.core.rmgt.profile.business.object.RmgtGroupBVOImpl" table="rmgt_t_group" proxy="com.myapp.core.rmgt.profile.common.business.object.RmgtGroupBVO">
        <id name="rowguid" column="rowguid" type="java.lang.Long">
            <generator class="sequence">
                <param name="sequence_name">rmgt_t_group_rowguid_seq</param>
            </generator>
        </id>               
       ...
        <set name="columns" table="rmgt_t_column" lazy="false" cascade="all-delete-orphan" order-by="rmgtc_column_order asc">
            <key column="rmgtc_fk_group_id" not-null="true"/>       
            <one-to-many class="com.myapp.core.rmgt.profile.business.object.RmgtColumnBVOImpl"/>
        </set>                            
    </class>    
</hibernate-mapping>

<hibernate-mapping>
    <class name="com.myapp.core.rmgt.profile.business.object.RmgtColumnBVOImpl" table="rmgt_t_column" proxy="com.myapp.core.rmgt.profile.common.business.object.RmgtColumnBVO">
        <id name="rowguid" column="rowguid" type="java.lang.Long">
            <generator class="sequence">
                <param name="sequence_name">rmgt_t_column_rowguid_seq</param>
            </generator>
        </id> 
        ...
        <set name="columnProperties" table="rmgt_t_column_property" lazy="false" cascade="all-delete-orphan" order-by="rmgtcp_order asc">
            <key column="rmgtcp_fk_column_id" not-null="true"/>
            <one-to-many class="com.myapp.core.rmgt.profile.business.object.RmgtColumnPropertyBVOImpl"/>
        </set>
    </class>    
</hibernate-mapping>

<hibernate-mapping>
    <class name="com.myapp.core.rmgt.profile.business.object.RmgtColumnPropertyBVOImpl" table="rmgt_t_column_property" proxy="com.myapp.core.rmgt.profile.common.business.object.RmgtColumnPropertyBVO">
        <id name="rowguid" column="rowguid" type="java.lang.Long">
            <generator class="sequence">
                <param name="sequence_name">rmgt_t_column_property_rowguid_seq</param>
            </generator>
        </id> 
                                      
        <property name="key" type="java.lang.String" column="rmgtcp_key" not-null="true" /> 
        <property name="value" type="java.lang.String" column="rmgtcp_value" not-null="true" /> 
        <property name="order" type="java.lang.Integer" column="rmgtcp_order" not-null="true" />
        
    </class>    
</hibernate-mapping>

原操作步骤

  1. 获取目标Column并evict其所有ColumnProperties,克隆Column并清空rowguid:
RmgtColumnBVO column = columnService.findLazyById(columnId);
// Evict all properties to force hibernate write new instances to DB
column.getColumnProperties().stream().forEach(x -> profileDAO.getSession().evict(x));
RmgtColumnBVO columnClone = new RmgtColumnBVOImpl();
BeanUtils.copyProperties(column, columnClone);

columnClone.setRowguid(null);
columnClone.setProfileId(profileId);
columnClone.setGroupId(groupId);
  1. 克隆ColumnProperties并清空其rowguid:
// clone properties
Set<RmgtColumnPropertyBVO> propertys = column.getColumnProperties();
Set<RmgtColumnPropertyBVO> propertysClone = new HashSet<RmgtColumnPropertyBVO>();

for (RmgtColumnPropertyBVO property : propertys) {
    RmgtColumnPropertyBVO propertyClone = new RmgtColumnPropertyBVOImpl(); 
    BeanUtils.copyProperties(property, propertyClone);
    propertyClone.setRowguid(null);
    propertysClone.add(propertyClone);
}

columnClone.setColumnProperties(propertysClone);
  1. 获取Profile,附加克隆的Column并保存:
RmgtProfileBVO profile = profileService.findById(column.getProfileId());
profile.getGroup(groupId).getColumns().add(columnClone);
profileService.saveOrUpdate(profile, object.getCreationUser());

问题原因

当前Hibernate Session中仍存在一个ID为124320的RmgtColumnPropertyBVOImpl对象,即便evict了原属性对象,但原Column对象仍在Session管理范围内,BeanUtils复制对象时可能附带了Hibernate内部的代理状态或引用,导致克隆对象和Session中的原对象产生ID冲突。

解决方法

1. 让原Column完全脱离Session管理

获取原Column后,直接evict整个对象,彻底断开和Session的关联,避免后续操作时Session持有原对象的引用:

RmgtColumnBVO column = columnService.findLazyById(columnId);
// 直接evict整个Column对象,包括其关联的属性
profileDAO.getSession().evict(column);

RmgtColumnBVO columnClone = new RmgtColumnBVOImpl();
BeanUtils.copyProperties(column, columnClone);
// 清空克隆对象的集合引用,避免复用原集合
columnClone.setColumnProperties(new HashSet<>());
columnClone.setRowguid(null);
columnClone.setProfileId(profileId);
columnClone.setGroupId(groupId);

2. 手动克隆属性,避免BeanUtils复制Hibernate内部状态

不要依赖BeanUtils复制属性对象,手动复制业务属性,确保克隆对象是完全干净的新实例:

Set<RmgtColumnPropertyBVO> propertysClone = new HashSet<>();
for (RmgtColumnPropertyBVO property : column.getColumnProperties()) {
    RmgtColumnPropertyBVO propertyClone = new RmgtColumnPropertyBVOImpl();
    // 手动复制业务属性,跳过Hibernate内部字段
    propertyClone.setKey(property.getKey());
    propertyClone.setValue(property.getValue());
    propertyClone.setOrder(property.getOrder());
    propertyClone.setRowguid(null);
    propertysClone.add(propertyClone);
}
columnClone.setColumnProperties(propertysClone);

3. 优化保存逻辑

由于映射配置了cascade="all-delete-orphan",可以直接保存克隆的Column对象,Hibernate会自动级联保存其关联的ColumnProperties:

// 直接保存克隆的Column,级联保存属性
columnService.saveOrUpdate(columnClone, object.getCreationUser());

// 若需通过Profile关联保存,确保Profile是Session中获取的实例
// RmgtProfileBVO profile = profileService.findById(profileId);
// profile.getGroup(groupId).getColumns().add(columnClone);
// profileService.saveOrUpdate(profile, object.getCreationUser());

内容的提问来源于stack exchange,提问作者d00d

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 12:40:14