One-to-many assoc with link table + additional column does not work

This is what I have:

The Profile:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.cl.ilogic.core.rmgt.profile.business.object.RmgtProfileBVOImpl" table="rmgt_t_profile" proxy="com.cl.ilogic.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>  
	                         
        <property name="denotation" type="java.lang.String" column="rmgtp_denotation" not-null="true" />         
        <property name="description" type="java.lang.String" column="rmgtp_description" not-null="false" />  
        
        <property name="transportMode" type="java.lang.String" column="rmgtp_transport_mode" not-null="true" />    
        <property name="owner" type="java.lang.String" column="rmgtp_owner" not-null="false" /> 		       
        <property name="minRole" type="java.lang.String" column="rmgtp_min_role" not-null="false" />
        <property name="status" type="java.lang.String" column="rmgtp_status" not-null="true" />
              
        <property name="creationDate" type="timestamp" column="rmgtp_creation_date" /> 
        <property name="creationUser" type="java.lang.String" column="rmgtp_creation_user" />  
        <property name="modificationDate" type="timestamp" column="rmgtp_modification_date" /> 
        <property name="modificationUser" type="java.lang.String" column="rmgtp_modification_user" /> 
        <property name="cloneOfProfileId" type="java.lang.Long" column="rmgtp_t_clone_of_profile_id" />
        <property name="type" type="java.lang.String" column="rmgtp_type" />
        <property name="parentProfileId" type="java.lang.Long" column="rmgtp_parent_profile_id" />
        <property name="showStatus" type="java.lang.Boolean" column="rmgtp_show_status" />
        <property name="showMember" type="java.lang.Boolean" column="rmgtp_show_member" />
        <property name="showNature" type="java.lang.Boolean" column="rmgtp_show_nature" />
        <property name="showAssignment" type="java.lang.Boolean" column="rmgtp_show_assignment" />
        <property name="enabled" type="java.lang.Boolean" column="rmgtp_enabled" />
        
        <property name="shipmentProfileEnabled" type="java.lang.Boolean" 
	       	formula="(SELECT (CASE WHEN count(p.rowguid) = 0 THEN 0 ELSE 1 END) FROM rmgt_t_profile p
    			   			  WHERE EXISTS (SELECT pe.rowguid FROM rmgt_t_profile pe WHERE pe.rmgtp_parent_profile_id = rowguid AND pe.rmgtp_type = 'SHIPMENT DATA' AND pe.rmgtp_status = 'ACTIVE'))" />
    	
    	
        <property name="savingProfileEnabled" type="java.lang.Boolean" 
	       	formula="(SELECT (CASE WHEN count(p.rowguid) = 0 THEN 0 ELSE 1 END) FROM rmgt_t_profile p
    			   			  WHERE EXISTS (SELECT pe.rowguid FROM rmgt_t_profile pe WHERE pe.rmgtp_parent_profile_id = rowguid AND pe.rmgtp_type = 'SAVING PROFILE'))" />
    			   			  
    	<property name="spotRelevant" type="java.lang.Boolean" column="rmgtp_spot_relevant" />
    	
        <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.cl.ilogic.core.rmgt.profile.business.object.RmgtGroupBVOImpl"/>
        </set>

        <set name="profileValidations" table="rmgt_t_profile_validation" inverse="false" lazy="true" fetch="select" cascade="all-delete-orphan">
            <key property-ref="rowguid">
                <column name="fk_profile_id" not-null="true"  />
            </key>
            <many-to-many entity-name="com.cl.ilogic.core.rmgt.profile.business.object.RmgtProfileValidationBVOImpl" >
                <column name="fk_profile_id" not-null="true" />
            </many-to-many>
        </set>

</hibernate-mapping>

The Validation

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.cl.ilogic.core.vs.business.object.VsValidationBVOImpl" table="vs_t_validation" proxy="com.cl.ilogic.core.vs.common.business.object.VsValidationBVO">
        <id name="rowguid" column="rowguid" type="java.lang.Long">
	    	<generator class="sequence">
		        <param name="sequence_name">vs_t_validation_rowguid_seq</param>
		    </generator>
	    </id>                      
        <property name="denotation" type="java.lang.String" column="v_denotation" />        		       
        <property name="description" type="java.lang.String" column="v_description" />       
        <property name="path" type="java.lang.String" column="v_path" /> 
        <property name="owner" type="java.lang.String" column="v_owner" />     
        
        <property name="creationDate" type="timestamp" column="v_creation_date" /> 
        <property name="creationUser" type="java.lang.String" column="v_creation_user" />
        <property name="modificationDate" type="timestamp" column="v_modification_date" />
        <property name="modificationUser" type="java.lang.String" column="v_modification_user" />
        <property name="hasMultiColumnValidations" type="java.lang.Boolean" column="v_multicolumn" />
                
        <set name="rules" table="vs_t_v_vr" inverse="false" lazy="false" fetch="select">
			<key column="fk_validation_id"/>		
			<many-to-many class="com.cl.ilogic.core.vs.business.object.VsRuleBVOImpl" column="fk_validation_rule_id" unique="false" order-by="r_description asc"/>
        </set>

        <set name="profileValidations" table="rmgt_t_profile_validation" lazy="false" cascade="all-delete-orphan" >
            <key column="fk_validation_id"/>
            <one-to-many class="com.cl.ilogic.core.rmgt.profile.business.object.RmgtProfileValidationBVOImpl"/>
        </set>
         
    </class>
</hibernate-mapping>

The profileValidation link table:

@Entity
@Table(name = "rmgt_t_profile_validation")
@Proxy(proxyClass = RmgtProfileValidationBVO.class)
public class RmgtProfileValidationBVOImpl implements RmgtProfileValidationBVO {
    @EmbeddedId
    private RmgtProfileValidationId id;

    @Column(name = "pv_post_import")
    private Boolean pvPostImport;

    @Override
    @Get(POST_IMPORT)
    public Boolean getPostImport() {
        return pvPostImport;
    }

    @Override
    @Set(POST_IMPORT)
    public void setPostImport(Boolean pvPostImport) {
        this.pvPostImport = pvPostImport;
    }

	@Override
	@Get(PROFILE_ID)
	public Long getProfileId() {
		return id != null ? id.getProfileId() : null;
	}

	@Override
	@Set(PROFILE_ID)
	public void setProfileId(Long profileId) {
		if (id == null) {
			id = new RmgtProfileValidationId();
		}
		id.setProfileId(profileId);
	}

	@Override
	@Get(VALIDATION_ID)
	public Long getValidationId() {
		return id != null ? id.getValidationId() : null;
	}

	@Override
	@Set(VALIDATION_ID)
	public void setValidationId(Long validationId) {
		if (id == null) {
			id = new RmgtProfileValidationId();
		}
		id.setValidationId(validationId);
	}

}

The Embedded key:


@Embeddable
public class RmgtProfileValidationId implements Serializable {
    private static final long serialVersionUID = 6805524598016438604L;

    @OneToMany(mappedBy =)
    @NotNull
    @Column(name = "fk_profile_id", nullable = false, insertable = false, updatable = false)
    private Long profileId;

    @NotNull
    @Column(name = "fk_validation_id", nullable = false, insertable = false, updatable = false)
    private Long validationId;

    public RmgtProfileValidationId(Long profileId, Long validationId) {
        this.profileId = profileId;
        this.validationId = validationId;
    }

}

When starting wildfly, I get this error:


Caused by: org.hibernate.MappingException: collection element mapping has wrong number of columns: com.cl.myApp.core.rmgt.profile.business.object.RmgtProfileBVOImpl.profileValidations type: com.cl.myApp.core.rmgt.profile.business.object.RmgtProfileValidationBVOImpl
        at deployment.tg-clienta.war//org.hibernate.mapping.Collection.validate(Collection.java:320)
        at deployment.tg-clienta.war//org.hibernate.mapping.Set.validate(Set.java:36)
        at deployment.tg-clienta.war//org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:

Mappings for Profile and Validations are .hbm.xml because they are legacy tables. I created the profileValidations mapping from scratch. I had it at a point where I could fetch the data but when I removed a validation from a profile and wanted to persist via saveOrUpdate(), Hibernate started to set the fk_profile_id to null in the mapping table.

Any idea where one of the two errors come from?

Would like to give some more information. In the meantime i switched from EmbeddedId to IdClass to reduce some complexity. When I want to persist a profile with no attached profileValidations, hibernate tries to make an update on the profile table. This is totally wrong, it should remove the line from the mapping table where profile id = the id of my profile that I want to persist.

error
org.springframework.dao.DataIntegrityViolationException: could not execute batch; SQL [update rmgt_t_profile_validation set fk_profile_id=null where fk_profile_id=?]; constraint [fk_profile_id" of relation "rmgt_t_profile_validation]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute batch

My current mapping is this:

Profile mapping:

<hibernate-mapping>
    <class name="com.cl.myapp.core.rmgt.profile.business.object.RmgtProfileBVOImpl" table="rmgt_t_profile" proxy="com.cl.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="profileValidations" table="rmgt_t_profile_validation" lazy="false" cascade="all-delete-orphan">
			<key column="fk_profile_id" />
            <one-to-many class="com.cl.myapp.core.rmgt.profile.business.object.RmgtProfileValidationBVOImpl"/>
        </set>
    </class>
</hibernate-mapping>

Profile_Validations mapping table:

@Entity
@IdClass(RmgtProfileValidationId.class)
@Table(name = "rmgt_t_profile_validation")
@Proxy(proxyClass = RmgtProfileValidationBVO.class)
public class RmgtProfileValidationBVOImpl implements RmgtProfileValidationBVO {

	@Id
	@NotNull
	@Column(name="fk_profile_id", insertable = false, updatable = false)
	private Long profileId;

	@Id
	@NotNull
	@Column(name="fk_validation_id", insertable = false, updatable = false)
	private Long validationId;

    @Column(name = "pv_post_import")
    private Boolean pvPostImport;
}

ProfileValidationid class:

public class RmgtProfileValidationId implements Serializable {
    private static final long serialVersionUID = 6805524598016438604L;

    private Long profileId;

    private Long validationId;

    public RmgtProfileValidationId(Long profileId, Long validationId) {
        this.profileId = profileId;
        this.validationId = validationId;
    }

    public RmgtProfileValidationId() {
    }
}

Hey there, can you please create a JIRA issue for this problem and attach a reproducer test case? Looks like we need to dig into the details of this.

Will do so.

But just for the sake of completeness: when I add update=“false” and not-null=“true” to the set in the Profile mapping, hibernate does not fail with an update statement anymore. I will have to dive into the logging to see whats going on there.

<set name="profileValidations" table="rmgt_t_profile_validation" lazy="false" cascade="all-delete-orphan">
            <key column="fk_profile_id" on-delete="cascade" update="false" not-null="true"/>
            <one-to-many class="com.cl.myapp.core.rmgt.profile.business.object.RmgtProfileValidationBVOImpl" />
</set>

When finishing the test case I noticed that the scenario actually works. Hence, the error that caused the odd insert / update statements is obviously caused by our application somehow. Non-issue from hibernate side.