0

I am using Hibernate 5.6 and have user Profile with Groups, which have Columns, which have ColumnProperties. I'd like to clone a column of a profile and attach it to the column group.

Profile -> Groups -> Columns -> ColumnProperties

I get this error:

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]

Here's my mapping files:

<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>

What I do is i fetch a Profile from DB, then I use Spring's BeanUtils.copyProperties() to create a copy of the column that I want to clone. I made sure to evict all columnProperties before I proceed.

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);

Then I null all rowguids of columnProperties objects of the columnClone:

// 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);

After that, I fetch the profile and attach the cloned column to it and save the profile again:

RmgtProfileBVO profile = profileService.findById(column.getProfileId());
profile.getGroup(groupId).getColumns().add(columnClone);
profileService.saveOrUpdate(profile, object.getCreationUser());

What can I do to fix the problem? I don't understand why Hibernate is still complaining about the properties when I evicted every original one.

d00d
  • 688
  • 1
  • 8
  • 29
  • [Which `BeanUtils` are you using?](https://stackoverflow.com/questions/19763278/how-to-use-beanutils-copyproperties) – XtremeBaumer Dec 01 '22 at 10:52
  • org.springframework.beans.BeanUtils.copyProperties, but that's not the cause of the issue. It's a Hibernate thing. – d00d Dec 01 '22 at 11:14
  • Does this answer your question? [Hibernate Error: a different object with the same identifier value was already associated with the session](https://stackoverflow.com/questions/16246675/hibernate-error-a-different-object-with-the-same-identifier-value-was-already-a) – XtremeBaumer Dec 01 '22 at 11:15
  • There should be no need to manually evict anything – XtremeBaumer Dec 01 '22 at 11:17

1 Answers1

0

I found a solution for my problem. I replaced the profileService.saveOrUpdate() with profileService.merge() which did the job (although I do not fully understand why saveOrUpdate() does not).

In my opinion, saveOrUpdate should

  1. Check if profile exists and check if there are changes (new elements in collections, new attribute values).
  2. Check for every mapped collection (in my case: columns) if there are attribute changes or new items
  3. Traverse the mapping tree and check for every mapped collection (columnProperties) of a mapped collection (columns) if value changes or new items (which was the case for me) and update/insert accordingly.
d00d
  • 688
  • 1
  • 8
  • 29