# Custom Composite User Type not working with hbm.xml in Hibernate 6.5

**URL:** <https://discourse.hibernate.org/t/custom-composite-user-type-not-working-with-hbm-xml-in-hibernate-6-5/11277>\
**Category:** Hibernate ORM\
**Created:** [March 25, 2025, 12:10pm UTC](https://discourse.hibernate.org/t/custom-composite-user-type-not-working-with-hbm-xml-in-hibernate-6-5/11277 "2025-03-25T12:10:48Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![ciaccia](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/ciaccia/32/3929_2.png) [@ciaccia](https://discourse.hibernate.org/u/ciaccia)\
**Post date:** [March 25, 2025, 12:10pm UTC](https://discourse.hibernate.org/t/custom-composite-user-type-not-working-with-hbm-xml-in-hibernate-6-5/11277/1 "2025-03-25T12:10:48Z")

</div>

Hi everyone,  
I have a CompositeUserType which defines two columns `amount` and `currency` as described in Vlad’s hypersistence-utils: [MonetaryAmountType.java](https://github.com/vladmihalcea/hypersistence-utils/blob/master/hypersistence-utils-hibernate-63/src/main/java/io/hypersistence/utils/hibernate/type/money/MonetaryAmountType.java)

The custom type works fine when used in an `@ElementCollection` when mapped with JPA annotations, as shown below:

```auto
@CompositeType(MonetaryAmountType.class)
@ElementCollection(fetch = FetchType.LAZY)
@OrderColumn(name = "amounts_idx")
@CollectionTable(name = "my_entity_monetary_amounts", joinColumns = @JoinColumn(name = "my_entity_id"))
private List<MonetaryAmount> monetaryAmounts = new ArrayList<>();

```

When I try to map the same Entity using HBM Files, however the two columns of the composite user type are not recognized.

```auto
<list name="monetaryAmounts" table="my_entity_monetary_amounts">
  <key>
    <column name="my_entity_id"/>
  </key>
  <index column="amounts_idx"/>
  <composite-element class="com.example.MonetaryAmountType"/>
</list>

```

What am I doing wrong? Or is this maybe an Hibernate Bug related to HBM in Hibernate 6.5?

---

<div class="post-metadata">

**Author:** ![beikov](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/beikov/32/258_2.png) [@beikov](https://discourse.hibernate.org/u/beikov)\
**Post date:** [March 25, 2025, 4:25pm UTC](https://discourse.hibernate.org/t/custom-composite-user-type-not-working-with-hbm-xml-in-hibernate-6-5/11277/2 "2025-03-25T16:25:23Z")

</div>

Note that hbm.xml is deprecated for removal, so I would suggest you to not use this anymore and switch to Hibernate orm.xml extensions with this xsd schema: [https://hibernate.org/xsd/orm/mapping/mapping-7.0.xsd](https://hibernate.org/xsd/orm/mapping/mapping-7.0.xsd)

AFAIK, in hbm.xml, the `composite-element` node needs to specify all of the properties that it has as child elements. It’s not enough to list just the class.

---

<div class="post-metadata">

**Author:** ![ciaccia](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/ciaccia/32/3929_2.png) [@ciaccia](https://discourse.hibernate.org/u/ciaccia)\
**Post date:** [March 26, 2025, 7:27am UTC](https://discourse.hibernate.org/t/custom-composite-user-type-not-working-with-hbm-xml-in-hibernate-6-5/11277/3 "2025-03-26T07:27:54Z")

</div>

Hallo Christian,  
Thank you for your answer. I am using Hibernate 6.5, if I understand everything correctly the file you mentioned is not available until hibernate [7.0](https://github.com/hibernate/hibernate-orm/tree/6.6/hibernate-core/src/main/resources/org/hibernate/xsd/mapping).

I tried using the xml “ORM” (as in [mapping-3.1.xml](https://github.com/hibernate/hibernate-orm/blob/main/hibernate-core/src/main/resources/org/hibernate/xsd/mapping/mapping-3.1.0.xsd)) without luck, is there an example that shows how this can be achieved?

Thank you in advance

---

<div class="post-metadata">

**Author:** ![beikov](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/beikov/32/258_2.png) [@beikov](https://discourse.hibernate.org/u/beikov)\
**Post date:** [March 26, 2025, 11:09am UTC](https://discourse.hibernate.org/t/custom-composite-user-type-not-working-with-hbm-xml-in-hibernate-6-5/11277/4 "2025-03-26T11:09:04Z")

</div>

Hi,

there are no examples unfortunately, but since this is an extension of the [JPA orm.xml schema](https://jakarta.ee/xml/ns/persistence/orm/orm_3_2.xsd), you can mostly rely on tutorials for that e.g. [eclipselink-orm.xml Schema Reference | EclipseLink 2.6.x Java Persistence API (JPA) Extensions Reference](https://eclipse.dev/eclipselink/documentation/2.6/jpa/extensions/schema.htm)  
The extensions are usually modeled just like the annotations, so if you know what annotations you want to use, just search the XSD to see if you can find the matching XML element that you need to use. In this particular case, you might be able to use the `composite-user-type` element to register the user type for the java class `MonetaryAmount` globally.

---

<div class="post-metadata">

**Author:** ![ciaccia](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/ciaccia/32/3929_2.png) [@ciaccia](https://discourse.hibernate.org/u/ciaccia)\
**Post date:** [March 26, 2025, 1:28pm UTC](https://discourse.hibernate.org/t/custom-composite-user-type-not-working-with-hbm-xml-in-hibernate-6-5/11277/5 "2025-03-26T13:28:13Z")

</div>

Hi Christian,  
I have already tried with the _global_ `composite-user-type` tag, either it does not work or I do something wrong.

Is it expected (i.e. by design) that the xml mapping (either `hbm` or `orm`) supports only a subset of the features available with annotations? When not I should problably open a bug for this particular issue. What do you think?

Thanks again

---

<div class="post-metadata">

**Author:** ![beikov](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/beikov/32/258_2.png) [@beikov](https://discourse.hibernate.org/u/beikov)\
**Post date:** [March 26, 2025, 5:49pm UTC](https://discourse.hibernate.org/t/custom-composite-user-type-not-working-with-hbm-xml-in-hibernate-6-5/11277/6 "2025-03-26T17:49:42Z")

</div>

The annotation model is the preferred choice, because that is what the majority of users use. hbm.xml is deprecated and didn’t receive support for any of the new features.  
On the other hand, the orm.xml extensions are somewhat new i.e. we only added support for that in Hibernate ORM 6, so we might not have full feature parity yet with all the new things that we’re adding.  
If you’re missing something, please create a ticket in our [issue tracker](https://hibernate.atlassian.net). Ideally, you would also try to work on the implementation 🙂

Note that so far, we tried to implement orm.xml extensions to have feature parity with hbm.xml, which is limited though and certainly there might be bugs hiding since we’re not testing this very well.

---

<div class="post-metadata">

**Author:** ![Yashwanth](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/yashwanth/32/3882_2.png) [@Yashwanth](https://discourse.hibernate.org/u/Yashwanth)\
**Post date:** [March 28, 2025, 7:13am UTC](https://discourse.hibernate.org/t/custom-composite-user-type-not-working-with-hbm-xml-in-hibernate-6-5/11277/7 "2025-03-28T07:13:20Z")

</div>

**Hi Ciaccia & Christian,**

We are facing a similar issue while using `CompositeUserType` in an `hbm.xml` file. However, unlike your case, we are not using it in a collection but as a regular variable. Below is how we have defined it in `hbm.xml`:

```auto
 <property generated='never' lazy='false' name='optionexpression' optimistic-lock='false' type='chs.pof.util.TwoStringIndefiniteLenghType' unique='false'>
      <column name='COLUMNNAME_1'/>
      <column name='COLUMNNAME_2'/>
    </property>

```

While building the `SessionFactory`, we are encountering the following exception:

```auto
Caused by: org.hibernate.MappingException: Property 'chs.pof.Functiondesign.optionexpression' maps to 2 columns but 1 columns are required (type 'chs.pof.util.TwoStringIndefiniteLenghType' spans 1 columns)
	at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:690)
	at org.hibernate.mapping.RootClass.validate(RootClass.java:286)
	at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:505)
	at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:282)
	at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:463)
	at org.hibernate.boot.internal.MetadataImpl.buildSessionFactory(MetadataImpl.java:210)
	at chs.capitalmanager.pof.HibernateUtil.<init>(HibernateUtil.java:811)
	... 5 more

```

Additionally, we use Hibernate Tools to generate Java classes from `hbm.xml`, but that too is failing with a `MappingException`, stating that it is unable to resolve the class `chs.pof.util.TwoStringIndefiniteLenghType`. We initially suspected that the custom type might not be compiled before the `hbm2java` task, so we compiled it beforehand, but the issue still persists.

Ciaccia, could you please share the exception you are encountering?

Thanks in advance for any help!

---

<div class="post-metadata">

**Author:** ![beikov](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/beikov/32/258_2.png) [@beikov](https://discourse.hibernate.org/u/beikov)\
**Post date:** [March 28, 2025, 9:05am UTC](https://discourse.hibernate.org/t/custom-composite-user-type-not-working-with-hbm-xml-in-hibernate-6-5/11277/8 "2025-03-28T09:05:07Z")

</div>

What you are encountering is documented in the migration guide: [hibernate-orm/migration-guide.adoc at 6.0 · hibernate/hibernate-orm · GitHub](https://github.com/hibernate/hibernate-orm/blob/6.0/migration-guide.adoc#compositeusertype-changes)

You simply can’t have basic types anymore that span multiple columns. You have to use a `CompositeUserType` instead, so in case of hbm.xml, use

```xml
<component class="chs.pof.util.TwoStringIndefiniteLenghType">
      <property name='prop1' column='COLUMNNAME_1'/>
      <property name='prop2' column='COLUMNNAME_2'/>
</component>

```

---

<div class="post-metadata">

**Author:** ![Yashwanth](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/yashwanth/32/3882_2.png) [@Yashwanth](https://discourse.hibernate.org/u/Yashwanth)\
**Post date:** [March 29, 2025, 12:11pm UTC](https://discourse.hibernate.org/t/custom-composite-user-type-not-working-with-hbm-xml-in-hibernate-6-5/11277/9 "2025-03-29T12:11:32Z")

</div>

Hi **Christian**

Thanks for your suggestion. I have implemented the change in `hbm.xml`, but I am now encountering the following exception:

```auto
Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
  Location:
    chs/pof/util/TwoStringIndefiniteLenghType$TwoStringMapper$HibernateAccessOptimizer$ptBR3C8s.setPropertyValues(Ljava/lang/Object;[Ljava/lang/Object;)V @11: invokevirtual
  Reason:
    Type 'chs/pof/util/TwoStringIndefiniteLenghType$TwoStringMapper' (current frame, stack[0]) is not assignable to 'chs/pof/util/TwoStringValue'
  Current Frame:
    bci: @11
    flags: { }
    locals: { 'chs/pof/util/TwoStringIndefiniteLenghType$TwoStringMapper$HibernateAccessOptimizer$ptBR3C8s', 'java/lang/Object', '[Ljava/lang/Object;' }
    stack: { 'chs/pof/util/TwoStringIndefiniteLenghType$TwoStringMapper', 'java/lang/String' }
  Bytecode:
    0000000: 2bc0 000c 2c12 0d32 c000 0fb6 0015 2bc0
    0000010: 000c 2c12 1632 c000 0fb6 0019 b1       

	at java.base/java.lang.ClassLoader.defineClass0(Native Method)
	at java.base/java.lang.System$2.defineClass(System.java:2394)
	at java.base/java.lang.invoke.MethodHandles$Lookup$ClassDefiner.defineClass(MethodHandles.java:2505)
	at java.base/java.lang.invoke.MethodHandles$Lookup$ClassDefiner.defineClass(MethodHandles.java:2480)
	at java.base/java.lang.invoke.MethodHandles$Lookup.defineClass(MethodHandles.java:1865)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at net.bytebuddy.utility.Invoker$Dispatcher.invoke(Unknown Source)
	at net.bytebuddy.utility.dispatcher.JavaDispatcher$Dispatcher$ForNonStaticMethod.invoke(JavaDispatcher.java:1033)
	at net.bytebuddy.utility.dispatcher.JavaDispatcher$ProxiedInvocationHandler.invoke(JavaDispatcher.java:1163)
	at jdk.proxy2/jdk.proxy2.$Proxy99.defineClass(Unknown Source)
	at net.bytebuddy.dynamic.loading.ClassInjector$UsingLookup.injectRaw(ClassInjector.java:1685)
	at net.bytebuddy.dynamic.loading.ClassInjector$AbstractBase.injectRaw(ClassInjector.java:166)
	at net.bytebuddy.dynamic.loading.ClassInjector$AbstractBase.inject(ClassInjector.java:154)
	at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$UsingLookup.load(ClassLoadingStrategy.java:519)
	at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:101)
	at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6424)
	at org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState.load(ByteBuddyState.java:149)
	at org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl.getReflectionOptimizer(BytecodeProviderImpl.java:244)
	at org.hibernate.metamodel.internal.EmbeddableRepresentationStrategyPojo.buildReflectionOptimizer(EmbeddableRepresentationStrategyPojo.java:249)
	at org.hibernate.metamodel.internal.EmbeddableRepresentationStrategyPojo.<init>(EmbeddableRepresentationStrategyPojo.java:94)
	at org.hibernate.metamodel.internal.ManagedTypeRepresentationResolverStandard.resolveStrategy(ManagedTypeRepresentationResolverStandard.java:156)
	at org.hibernate.metamodel.mapping.internal.EmbeddableMappingTypeImpl.<init>(EmbeddableMappingTypeImpl.java:197)
	at org.hibernate.metamodel.mapping.internal.EmbeddableMappingTypeImpl.from(EmbeddableMappingTypeImpl.java:144)
	at org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper.buildEmbeddedAttributeMapping(MappingModelCreationHelper.java:339)
	at org.hibernate.persister.entity.AbstractEntityPersister.buildEmbeddedAttributeMapping(AbstractEntityPersister.java:6021)
	at org.hibernate.persister.entity.AbstractEntityPersister.generateNonIdAttributeMapping(AbstractEntityPersister.java:5954)
	at org.hibernate.persister.entity.AbstractEntityPersister.prepareMappingModel(AbstractEntityPersister.java:5217)
	at org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess.execute(MappingModelCreationProcess.java:84)
	at org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess.process(MappingModelCreationProcess.java:42)
	at org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl.finishInitialization(MappingMetamodelImpl.java:200)
	at org.hibernate.internal.SessionFactoryImpl.initializeMappingModel(SessionFactoryImpl.java:373)
	at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:302)
	at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:463)
	at org.hibernate.boot.internal.MetadataImpl.buildSessionFactory(MetadataImpl.java:210)
	at chs.capitalmanager.pof.HibernateUtil.<init>(HibernateUtil.java:811)
	at chs.capitalmanager.pof.HibernateUtil.init(HibernateUtil.java:744)
	at chs.capitalmanager.appserver.AppServer.<init>(AppServer.java:249)
	at chs.capitalmanager.appserver.AppServer.lambda$main$0(AppServer.java:148)
	at chs.capitalmanager.appserver.AppServer.startEnsuringSingleInstance(AppServer.java:684)
	at chs.capitalmanager.appserver.AppServer.main(AppServer.java:120)

```

Below is my `CompositeUserType` class implementation:

```auto
public class TwoStringValue{
	private String optionexpression;

	private String optionexpressionClob;

	public TwoStringValue(String optionExpression, String optionExpressionClob) {
		this.optionexpression = optionExpression;
		this.optionexpressionClob = optionExpressionClob;
	}

	public String getOptionexpression() {
		return optionexpression;
	}

	public void setOptionexpression(String optionexpression) {
		this.optionexpression = optionexpression;
	}

	public String getOptionexpressionClob() {
		return optionexpressionClob;
	}

	public void setOptionexpressionClob(String optionexpressionClob) {
		this.optionexpressionClob = optionexpressionClob;
	}

	@Override
	public String value() {
		if (optionexpression.length() <= 1024) {
			return optionexpression;
		}
		else {
			return optionexpressionClob;
		}
	}
}

public class TwoStringIndefiniteLenghType implements CompositeUserType<TwoStringValue>, Serializable
{

	@SuppressWarnings("ALL")
	public static class TwoStringMapper {

		@Column (name = "OPTION_EXPRESSION")
		String optionexpression;
		@Column (name = "OPTION_EXPRESSION_CLOB")
		String optionexpressionClob;

		public String getOptionexpression() {
			return optionexpression;
		}

		public String getOptionexpressionClob() {
			return optionexpressionClob;
		}
	}

	public TwoStringIndefiniteLenghType()
	{
	}

	@Override
	public Object getPropertyValue(TwoStringValue twoStringValue, int i) throws HibernateException {
		if (i == 0) {
			return twoStringValue.getOptionexpression();
		} else if (i == 1) {
			return twoStringValue.getOptionexpressionClob();
		}
		throw new HibernateException("Invalid property index: " + i);
	}

	@NotNull
	@Override
	public TwoStringValue instantiate(ValueAccess valueAccess, SessionFactoryImplementor sessionFactoryImplementor) {
		String firstColumnValue = valueAccess.getValue(0, String.class);
		String secondColumnValue = valueAccess.getValue(1, String.class);
        return new TwoStringValue(firstColumnValue, secondColumnValue);
    }

	@Override
	public Class<?> embeddable() {
		return TwoStringMapper.class;
	}

	@Override
	public Class<TwoStringValue> returnedClass() {
		return TwoStringValue.class;
	}

	@Override
	public boolean equals(TwoStringValue twoStringValue, TwoStringValue j1) {
		return Objects.equals(twoStringValue, j1);
	}

	@Override
	public int hashCode(TwoStringValue twoStringValue) {
		return twoStringValue.hashCode();
	}

	@Override
	public TwoStringValue deepCopy(TwoStringValue twoStringValue) {
		return twoStringValue;
	}

	@Override
	public boolean isMutable() {
		return false;
	}

	@Override
	public Serializable disassemble(TwoStringValue twoStringValue) {
		return new String[]{twoStringValue.getOptionexpression(), twoStringValue.getOptionexpressionClob()};
	}

	@Override
	public TwoStringValue assemble(Serializable serializable, Object o) {
		String[] serializable1 = (String[]) serializable;
		return new TwoStringValue(serializable1[0], serializable1[1]);
	}

	@Override
	public TwoStringValue replace(TwoStringValue twoStringValue, TwoStringValue j1, Object o) {
		return twoStringValue;
	}
}

```

I don’t understand what I’m doing wrong. I found some help online, and if I return `TwoStringValue` instead of `TwoStringMapper` in the `embeddable()` method, it works fine. However, I followed the [Using CompositeUserType](https://docs.jboss.org/hibernate/orm/6.6/userguide/html_single/Hibernate_User_Guide.html#embeddable-mapping-custom) documentation, so I’m not sure why this is happening.

Any insights?

---

<div class="post-metadata">

**Author:** ![ciaccia](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/ciaccia/32/3929_2.png) [@ciaccia](https://discourse.hibernate.org/u/ciaccia)\
**Post date:** [March 31, 2025, 12:56pm UTC](https://discourse.hibernate.org/t/custom-composite-user-type-not-working-with-hbm-xml-in-hibernate-6-5/11277/10 "2025-03-31T12:56:45Z")

</div>

Hi Yashwanth,  
We didn’t experience any “Hibernate” exception. In our case, we just had an IndexOutOfBounds exception in our custom type class, since hibernate initialized our custom type without any columns.

Have you tried to add an empty constructor to your TwoStringValue class?

Cheers

---

<div class="post-metadata">

**Author:** ![beikov](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/beikov/32/258_2.png) [@beikov](https://discourse.hibernate.org/u/beikov)\
**Post date:** [April 1, 2025, 1:58pm UTC](https://discourse.hibernate.org/t/custom-composite-user-type-not-working-with-hbm-xml-in-hibernate-6-5/11277/11 "2025-04-01T13:58:56Z")

</div>

This looks good to me, but note that Hibernate ORM 6.5 is not supported anymore, so please update to the latest version first before discussing further.
