Hibernate 6.6.46.Final: byte code enhancer fails on final entity classes

Hello,

I am using Hibernate 6.6.46.Final and i have configured the compile time byte code enhancer maven plugin. On server startup i’ve got the following error on a final entity class:

Caused by: java.lang.IllegalArgumentException: Cannot subclass primitive, array or final types: …

at net.bytebuddy.ByteBuddy.subclass(ByteBuddy.java:542)

Indeed i have a lot of entities that are final classes and it seems that ByteBuddy doesn’t support this! I have tried to use the none enhancer provider configuring it using the META-INF/services way but the enhancer class initiator then finds two provider classes and fails to start (saying that there are more than one provider): the none provider i have configured and the one included in the hibernate-core/META-INF/services that is byte buddy.

How can i please overcome this without setting all my classes to be non final?

Thank you for your help

If you configured build time enhancement with the maven plugin, the server runtime shouldn’t attempt to do any enhancement. How did you configure the maven plugin? Please share the XML snippet.

Thank you for your quick reply. I have configured the plugin in the root pom file like this:

<plugin>

<groupId>org.hibernate.orm.tooling</groupId>

<artifactId>hibernate-enhance-maven-plugin</artifactId>

<version>${hibernate.version}</version>

<executions>

<execution>

<configuration>

<enableLazyInitialization>true</enableLazyInitialization>

</configuration>

<goals>

<goal>enhance</goal>

</goals>

</execution>

</executions>

</plugin>

But in all cases why is ByteBuddy complaining about final classes even if runtime enhancement is active?

I have tried removing the plugin to recheck that everything was working fine before adding the plugin and i still have the same error! I don’t understand what is happening.

When i try to compile using the mvn install command outside of Eclipse to check if it is related to Eclipse, i have errors like:

java.lang.ClassCastException: Object of type ‘class X’ can’t be cast to CompositeTracker
at org.hibernate.engine.internal.ManagedTypeHelper.asCompositeTracker(ManagedTypeHelper.java:352)

Please share the full stacktrace of the bytebuddy error. You can debug into org.hibernate.metamodel.internal.EntityRepresentationStrategyPojoStandard#resolveProxyFactory to see why it is not taking the branch for enhanced entity classes.

Here is the full stacktrace. I will try to debug the code asap:

Caused by: org.hibernate.MappingException: Could not instantiate persister org.hibernate.persister.entity.JoinedSubclassEntityPersister
	at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:105)
	at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:77)
	at org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl.processBootEntities(MappingMetamodelImpl.java:250)
	at org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl.finishInitialization(MappingMetamodelImpl.java:184)
	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.cfg.Configuration.buildSessionFactory(Configuration.java:980)
	at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:999)
	at org.springframework.orm.hibernate5.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:625)
	at org.springframework.orm.hibernate5.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:601)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1873)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1822)
	... 44 more
Caused by: java.lang.IllegalArgumentException: Cannot subclass primitive, array or final types: class com.enterprise.guide.app.core.place.model.transport.Airport
	at net.bytebuddy.ByteBuddy.subclass(ByteBuddy.java:542)
	at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyHelper.lambda$proxyBuilder$1(ByteBuddyProxyHelper.java:99)
	at org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState.load(ByteBuddyState.java:241)
	at org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState.loadProxy(ByteBuddyState.java:148)
	at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyHelper.buildProxy(ByteBuddyProxyHelper.java:63)
	at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyFactory.postInstantiate(ByteBuddyProxyFactory.java:65)
	at org.hibernate.metamodel.internal.EntityRepresentationStrategyPojoStandard.createProxyFactory(EntityRepresentationStrategyPojoStandard.java:305)
	at org.hibernate.metamodel.internal.EntityRepresentationStrategyPojoStandard.resolveProxyFactory(EntityRepresentationStrategyPojoStandard.java:179)
	at org.hibernate.metamodel.internal.EntityRepresentationStrategyPojoStandard.<init>(EntityRepresentationStrategyPojoStandard.java:145)
	at org.hibernate.metamodel.internal.ManagedTypeRepresentationResolverStandard.resolveStrategy(ManagedTypeRepresentationResolverStandard.java:62)
	at org.hibernate.persister.entity.AbstractEntityPersister.<init>(AbstractEntityPersister.java:563)
	at org.hibernate.persister.entity.JoinedSubclassEntityPersister.<init>(JoinedSubclassEntityPersister.java:187)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
	at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:94)
	... 56 more

Hello,

I have some additional info:

A) All things work back again when i remove the enhance plugin but this time clean and install all the projects so just removing the plugin from the pom seems to keep the classes enhanced somehow.

B) When i put back the plugin and clean/install all the projects, the install fails (during test phase in my case but i guess this is unrelated) with

java.lang.ClassCastException: Object of type 'TYPEA' can't be cast to CompositeTracker
	at org.hibernate.engine.internal.ManagedTypeHelper.asCompositeTracker(ManagedTypeHelper.java:352)
	at ....$$_hibernate_write_FieldA(Code.java)

The TYPEA type is an Embeddable type annotated with Embeddable, and the FieldA is of that TYPEA. I found this discussion on a probably similar issue here: classCastException: CompositeTracker hibernate 6 & mapstruct · Issue #34603 · quarkusio/quarkus · GitHub (see the last comments please on people trying with newer versions of Hibernate 6)

The error i am having with the stacktrace above comes after i run my server but with the install phase that have failed so i should probably wait for the install fix before i try again no?

Hey, We also had the same problem recently - Where an @Entity class with an embedded field was throwing the exception (Can’t cast to CompositeTracker) after applying the enhancer plugin…

After investigation, It turns out this happens when you mix AccessTypes, Hibernate defaults to AccessType.FIELD for an entity not explicitly marked, Whether explicit or implicit it is inherited by all embedded fields whose @Embeddable classes are also not explicitly marked - But this relationship can only be determined when the entities are enhanced at runtime…

At compile time, A class marked with @Embeddable is examined as one without considering its relationship to entities, And hence the enhancer can’t accurately determine its AccessType for all involved entities, As the same @Embeddable class can have different AccesTypes for different entities…

Anyway, To solve this issue - You have to explicitly specify the AccessType in each @Embeddable class with the @Access annotation - This forces it to always have the same AccessType, overriding what would have been inherited from the @Entity class…

As a side note, Also having a getter in an @Entity or @Embeddable class without a matching field, and not explicity marked with @Transient can cause the problem…

Hello gmatege,

Thank you for your reply but this does not work for me:

  1. None of my classes are annotated with @Access.

  2. The Embeddable class and its close hierarchy don’t have any getter without a matching field. Do you think i should check all my classes?

I was able to reproduce using a basic JUnit test without having to install all projects and the test code is:

  1. ClassEmb is an Embeddable class
  2. ClassEntity is an Entity class having a field emb of type ClassEmb
  3. The test case calls: ClassEmb emb=new ClassEmb(); then classEntity.setEmb(emb);
  4. The error is raised because the injected $$_hibernate_write_emb method in setEmb method calls ManagedTypeHelper.asCompositeTracker with the ‘emb’ instance that is not a PrimeAmongSecondarySupertypes (but a ClassEmbinstance that seems not enhanced) as expected by the asCompositeTracker code:
public static CompositeTracker asCompositeTracker(final Object entity) {
		Objects.requireNonNull( entity );
		if ( entity instanceof PrimeAmongSecondarySupertypes ) {
			PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity;
			final CompositeTracker e = t.asCompositeTracker();
			if ( e != null ) {
				return e;
			}
		}
		throw new ClassCastException( "Object of type '" + entity.getClass() + "' can't be cast to CompositeTracker" );
	}

Ok i just figured it out using my simple test case. Indeed there was a getter in another @Embedabbleclass with no mapped field. I have tried, as per your suggestion, adding the @Transienton that fake getter and it failed.

Then i tried rename it by replacing the ‘get’ with ‘return’ and it worked!

What an ugly scenario! i don’t understand why Hibernate does not throw a clear error instead of this mess! I now need to check all my 1000 classes for this pattern :slight_smile:

Thank you very much for all your help! I appreciate it.

Ok so after that fake getter fix (not fixing all fake getters), the mvn install is successful as my test cases do not fail anymore. But i still have my first problem

Caused by: org.hibernate.MappingException: Could not instantiate persister org.hibernate.persister.entity.JoinedSubclassEntityPersister
	at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:105)
	at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:77)
	at org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl.processBootEntities(MappingMetamodelImpl.java:250)
	at org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl.finishInitialization(MappingMetamodelImpl.java:184)
	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.cfg.Configuration.buildSessionFactory(Configuration.java:980)
	at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:999)
	at org.springframework.orm.hibernate5.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:625)
	at org.springframework.orm.hibernate5.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:601)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1873)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1822)
	... 44 more
Caused by: java.lang.IllegalArgumentException: Cannot subclass primitive, array or final types: class com.enterprise.guide.app.core.place.model.transport.Airport
	at net.bytebuddy.ByteBuddy.subclass(ByteBuddy.java:542)
	at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyHelper.lambda$proxyBuilder$1(ByteBuddyProxyHelper.java:99)
	at org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState.load(ByteBuddyState.java:241)
	at org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState.loadProxy(ByteBuddyState.java:148)
	at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyHelper.buildProxy(ByteBuddyProxyHelper.java:63)
	at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyFactory.postInstantiate(ByteBuddyProxyFactory.java:65)
	at org.hibernate.metamodel.internal.EntityRepresentationStrategyPojoStandard.createProxyFactory(EntityRepresentationStrategyPojoStandard.java:305)
	at org.hibernate.metamodel.internal.EntityRepresentationStrategyPojoStandard.resolveProxyFactory(EntityRepresentationStrategyPojoStandard.java:179)
	at org.hibernate.metamodel.internal.EntityRepresentationStrategyPojoStandard.<init>(EntityRepresentationStrategyPojoStandard.java:145)
	at org.hibernate.metamodel.internal.ManagedTypeRepresentationResolverStandard.resolveStrategy(ManagedTypeRepresentationResolverStandard.java:62)
	at org.hibernate.persister.entity.AbstractEntityPersister.<init>(AbstractEntityPersister.java:563)
	at org.hibernate.persister.entity.JoinedSubclassEntityPersister.<init>(JoinedSubclassEntityPersister.java:187)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
	at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:94)
	... 56 more

I have debugged as suggested the org.hibernate.metamodel.internal.EntityRepresentationStrategyPojoStandard#resolveProxyFactoryand for the incriminated class:

  1. enhancedForLazyLoading is true
  2. bootDescriptor.getRootClass() == bootDescriptor is false
  3. !bootDescriptor.hasSubclasses() is true

So the first check is false.

And then:

  1. proxyJtd != null is true
  2. entityMetamodel.isLazy() is true
  3. So it goes into that 2nd condition
  4. In createProxyFactory:
    1. proxyFactory is org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyFactory

But i think that the two issues are related where i have indeed many other fake getters and Hibernate is then not enhancing some classes and trying to enhance them again but at runtime!?

So most probably i need to clean all my fake getters and try again. But if ByteBuddy is used too at compile time enhancement, will i have the same error? i will try and see.

You could add the @Access annotation on a base @MappedSuperclass for entities, It worked for us - Though applying it on a base class for embeddables didn’t work - We had to apply the annotation on each and every concrete class, as its these that had the @Embeddable annotation…

In our case, When we specified the annotation explicitly as @Access(AccessType.FIELD) as mentioned, We didn’t even need to apply the @Transient annotation on any unmatched getter…

The getters continued working as before…

Ok thank you for the info.

I wanted to try out your solution, so i have first renamed my fake getter to ‘getX’ again and run my simple JUnit test to make sure i have the issue again and so to start on a clean base but my test was successful!

I am really confused! I even tried to rebuild all the projects in case my plugin was failing somehow and the problem disappeared too! This is really messy as if the enhancement code is erratic somehow or that the plugin within Eclipse using m2e is erratic!

What i will do now is follow your recommandation concerning the access type, rebuild everything and see if the problem disappears. If so i will hope that it won’t appear again in the future :frowning:

Thank you again for your extremely useful help! At least i have a clue after days of brain damage :slight_smile:

Can you please create a Jira ticket for this and provide a reproducer? This is something worth fixing, even if you figured out the pattern now and fixed your code base to work around this.

The thing is, during enhancement, we have to understand what access type a class uses, which depends on the access type of the entity classes that refer to the embeddable type. Maybe there is some confusion going on in that code that leads the enhancer to believe that it needs to do property based access enhancement. With property based access, we require currently that you have a matching field for every property accessor, which is why annotating @Transient on your “fake” getters makes it work.

How can the class not be the root class but also not have subclasses. You will please have to share the entity model with us so we can check what is going on there.

I don’t know what the mentions about “clean” are supposed to mean, but when trying this out, please use command line tools and avoid incremental compilation with Eclipse and m2e to avoid side effects of these tools.

Ok i have some additional info (hopefully!):

Without setting any access type (my current code base) and excluding, as advised by beikov, the Eclipse and m2e overheads, i renamed the unmatched getter to ‘getX’ and used the mvn command to clean and install and it failed as expected because of that unmatched getter probably:

java.lang.ClassCastException: Object of type 'TYPEA' can't be cast to CompositeTracker

Then i renamed the getter to ‘returnX’ and the mvn command failed too! So just renaming the getter does not fix the issue so most probably (?), the Eclipse and/or the m2e are playing a role in the previous erratic results.

So then i renamed ‘returnX’ to ‘getX’ and set all access types to FIELD for entity and embeddable classes. Now everything works fine using both environments: Eclipse or mvn command! as if somehow Eclipse and/or the m2e were corrupting the code (?). At least i hope this is the case and not one more erratic scenario. And may be in my case i had other unmatched getters elsewhere causing the problem.

So i am not sure @beikov that i can create a simple test case here apart from the known access type solution.

Anyway, after the successful compilation, when the server starts, i still have the error:

Caused by: java.lang.IllegalArgumentException: Cannot subclass primitive, array or final types: class com.enterprise.guide.app.core.place.model.transport.Airport

The Airport entity class is a class having only @Embedded fields and no unmatched getters (TransportPlace is an entity class without any field and extends other mappedsuperclasses or entities):

@Entity
@Access(AccessType.FIELD)
public final class Airport extends TransportPlace implements IAirport{
    @Embedded
    private EMBA embA;

    @Embedded
    private EMBB embB;
}

But even when i remove the final keywork for this class, other final classes with different models fail too. And if i decompile the Airport class, i can see that it has been enhanced.

And @beikov , about the debugging analysis, you said:

How can the class not be the root class but also not have subclasses.

The debugger gives for the Airport class:

  • bootDescriptor.getRootClass() is AbstractPlace which is the root class in my case
  • bootDescriptor is the Airport class
  • !bootDescriptor.hasSubclasses() is true as the Airport class is final.

So the Airport class is not the root class and does not have subclasses as it is final. no?

When you talk about :

the branch for enhanced entity classes

in

org.hibernate.metamodel.internal.EntityRepresentationStrategyPojoStandard#resolveProxyFactory

what do you mean? which condition is that ? The 1st that should return null or the 2nd that returns the byte buddy factory?

Ok, so it seems like Eclipse with its incremental compiler and m2e were playing some games on you which caused some confusion. Glad you figured this out now. This is why it’s important to have a reproducer that can be run on the command line.

I forgot to think about the scenario when a final class is the subclass of some other class. Indeed, in that case the conditions will evaluate like you say.

Please try to create a reproducer with our test case template and if you are able to reproduce the issue, create a bug ticket in our issue tracker and attach that reproducer.