MappingException after Update from 5.2.17 to 5.4.22

The error message is:
org.hibernate.MappingException: property [userId] not found on entity [CostCenter]

This doesn’t make any sense since userId is a property of User, not CostCenter.

The mappings worked with Hibernate 5.2.17 and were not changed:

<class name="CostCenter" table="SIS_KST" select-before-update="true">
	<cache usage="read-write"/>
	<id name="code" column="KST_CD" length="6">
	  <type name="CustomDbType">
		<param name="implementorKey">1026</param>
	  </type>
	</id>
	<version name="changeId" column="KST_CH_ID" type="versiontype"/>
	...
    <set name="costCenters" inverse="true" optimistic-lock="false">
      <key column="KST_NEW_CD"/>
      <one-to-many class="CostCenter" not-found="ignore"/>
    </set>
	...
	<many-to-one name="successor" unique="false" column="KST_NEW_CD" not-null="false" class="CostCenter"/>
	<many-to-one name="supervisingUser" unique="false" column="KST_KTKUSR" not-null="false" class="User" property-ref="userId" fetch="join"/>
	...
</class>

<class name="User" table="G_USER" select-before-update="true">
	<cache usage="read-write"/>
	<id name="id" column="GU_LFDNR" length="7">
	  <type name="CustomDbType">
		<param name="implementorKey">4342</param>
	  </type>
	</id>
	...
    <set name="costCenters" inverse="true" optimistic-lock="false">
      <key column="KST_KTKUSR" property-ref="userId"/>
      <one-to-many class="CostCenter" not-found="ignore"/>
    </set>
	...
    <property name="userId" unique="true" column="GU_ID" not-null="true" length="10">
      <type name="CustomDbType">
        <param name="implementorKey">4384</param>
      </type>
    </property>
	...
</class>

I only removed the qualifying package names from the above xml.

Seems like a bug to me.

Can you provide a reproducer for that, preferrably with the testcase template?

I’m not fluent in the hbm.xml schema, but it seems to me that the following is the issue:

<set name="costCenters" inverse="true" optimistic-lock="false">
  <key column="KST_KTKUSR" property-ref="userId"/> <!-- THIS HERE -->
  <one-to-many class="CostCenter" not-found="ignore"/>
</set>

That’s exactly what I thought at first. But if I remove the property-ref, or even the whole <set> mapping, the error persists. To make things even worse, if I remove the other <set> mapping, the one in the CostCenter entity itself, the error is gone. Doesn’t make any sense whatsoever. Seriously weird.

I don’t know how to map this with hbm.xml but if you can switch to annotations, the following should work:

@Entity
@Table(name = "SIS_KST")
public calss CostCenter {
    @Id
    @Column(name = "KST_CD", length = 6)
    @Type(type = "CustomDbType", parameters = @Parameter(name = "implementorKey", value = "1026"))
    String code;
    @Version
    @Column(name = "KST_CH_ID", columnDefinition = "versiontype")
    Long changeId;

    @ManyToOne
    @JoinColumn(name = "KST_NEW_CD")
    CostCenter successor;
    @ManyToOne
    @JoinColumn(name = "KST_KTKUSR", referencedColumn = "GU_ID")
    User supervisingUser;

    @OneToMany(mappedBy = "successor")
    Set<CostCenter> costCenters;
}

@Entity
@Table(name = "G_USER")
public calss User {
    @Id
    @Column(name = "GU_LFDNR", length = 7)
    @Type(type = "CustomDbType", parameters = @Parameter(name = "implementorKey", value = "4342"))
    String code;

    @Column(name = "GU_ID", length = 10, unique = true)
    @Type(type = "CustomDbType", parameters = @Parameter(name = "implementorKey", value = "4384"))
    String userId;

    @OneToMany(mappedBy = "supervisingUser")
    Set<CostCenter> costCenters;
}

Maybe this helps you fix the hbm.xml mapping as well.

Thanks for the suggestion. I am sure those annotation should work. So should the xml though. And it did, with Hibernate 5.2.

Cannot switch to annotations, all the xml is generated on system start.

If you can provide a test case I can give this a deeper look

Thanks a lot for the offer. I can’t use the template though as it assumes I’m using JPA. Which I don’t. Just plain old proprietary Hibernate:

MetadataSources sources = new MetadataSources(...);
Stream<DOMDocument> docs = ...;
docs.forEach(sources::addInputStream);
Metadata metadata = sources.buildMetadata();
sessionFactory = metadata.getSessionFactoryBuilder().build();

But I think I found the bug anyway. Look for it in the constructor of AbstractPluralAttributeSourceImpl:

protected AbstractPluralAttributeSourceImpl(
			MappingDocument mappingDocument,
			final PluralAttributeInfo pluralAttributeJaxbMapping,
			AttributeSourceContainer container) {
		super( mappingDocument );
		this.pluralAttributeJaxbMapping = pluralAttributeJaxbMapping;
		this.container = container;

		this.attributeRole = container.getAttributeRoleBase().append( pluralAttributeJaxbMapping.getName() );
		this.attributePath = container.getAttributePathBase().append( pluralAttributeJaxbMapping.getName() );

/* This whole thing is new in 5.4 */

		Optional<JaxbHbmManyToOneType> jaxbHbmManyToOneTypeOptional = Optional.empty();

		if ( pluralAttributeJaxbMapping.isInverse() && pluralAttributeJaxbMapping.getOneToMany() != null ) {
			String childClass = pluralAttributeJaxbMapping.getOneToMany().getClazz();

			if ( childClass != null ) {
				jaxbHbmManyToOneTypeOptional = mappingDocument.getDocumentRoot().getClazz()
						.stream()

	/* If I'm not mistaken, this checks if the class in question references itself */
						.filter( (JaxbHbmRootEntityType entityType) -> childClass.equals( entityType.getName() ) )

	/* If it does, we forget about it and look for any (!!!) many-to-one attribute with a property-ref ... */
						.flatMap( jaxbHbmRootEntityType -> jaxbHbmRootEntityType.getAttributes().stream() )
						.filter(
								attribute -> attribute instanceof JaxbHbmManyToOneType &&
										( (JaxbHbmManyToOneType) attribute ).getPropertyRef() != null )
						.map( JaxbHbmManyToOneType.class::cast )
						.findFirst();
			}
		}

	/* ... and use it to fill the keySource, even if (as in my case) it has nothing to do with the original pluralAttributeJaxbMapping at all. This cannot work. */

		this.keySource = jaxbHbmManyToOneTypeOptional
				.map( jaxbHbmManyToOneType -> new PluralAttributeKeySourceImpl(
						sourceMappingDocument(),
						jaxbHbmManyToOneType,
						container

/* This is the old 5.2 behavior, which does work. */

				) ).orElseGet( () -> new PluralAttributeKeySourceImpl(
						sourceMappingDocument(),
						pluralAttributeJaxbMapping.isInverse() ? /* And what's this? It's not wrong, but still. */
								pluralAttributeJaxbMapping.getKey() :
								pluralAttributeJaxbMapping.getKey(),
						container
				) );

I just don’t know what is the right fix here. That will have to be figured out by someone who has an idea about why all this stuff was added in the first place.

There is a test case template for using Hibernate APIs as well of course :slight_smile:

Thanks for your analysis so far. We really need a test case though, so please try to take the time to write one.

How can I attach the test files?

Just fork the repository on Github and send me a link to the test on your Github fork.

Made a test here.

Thanks, I’ll take a look as soon as I find the time

It’s a bug. I created https://hibernate.atlassian.net/browse/HHH-14322 for this

Thanks for your support!

1 Like