Hi everyone,
I have a CompositeUserType which defines two columns amount and currency as described in Vlad’s hypersistence-utils: MonetaryAmountType.java
The custom type works fine when used in an @ElementCollection when mapped with JPA annotations, as shown below:
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.
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.
I tried using the xml “ORM” (as in mapping-3.1.xml) without luck, is there an example that shows how this can be achieved?
there are no examples unfortunately, but since this is an extension of the JPA orm.xml schema, 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
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.
Hi Christian,
I have already tried with the globalcomposite-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?
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. 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.
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:
While building the SessionFactory, we are encountering the following exception:
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 for your suggestion. I have implemented the change in hbm.xml, but I am now encountering the following exception:
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:
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 documentation, so I’m not sure why this is happening.
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?
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.