|
I have 2 entities: User and UserGroup (omitting some code to shorten here..):
@Entity
@Table(name = "`User`") public class User implements java.io.Serializable {
[...]
private UserGroup userGroup;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "userGroupId", insertable = false)
@Generated(GenerationTime.INSERT)
public UserGroup getUserGroup() {
return this.userGroup;
}
[...] }
@Entity
@Table(name = "UserGroup")
public class UserGroup implements java.io.Serializable {
private int id;
private String name;
[...] }
The userGroup property is generated at row insertion with a database trigger (this is not my real application but just to render the concept..), so I used @org.hibernate.annotations.Generated(GenerationTime.INSERT) to refresh the entity after a persist().
This behaviour worked fine in Hibernate 3.6.10. Migrating to 4.3.10 or 5.0.0.CR1 does not work anymore, giving this exception:
javax.persistence.PersistenceException: [PersistenceUnit: TestPU] Unable to build Hibernate SessionFactory
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:1249)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.access$600(EntityManagerFactoryBuilderImpl.java:120)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:860)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850)
at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:425)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:849)
at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:75)
at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:54)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:55)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:39)
at testH4.TestH4.main(TestH4.java:10)
Caused by: org.hibernate.MappingException: Unknown entity: testH4.entities.UserGroup
at org.hibernate.internal.SessionFactoryImpl.getEntityPersister(SessionFactoryImpl.java:1096)
at org.hibernate.internal.SessionFactoryImpl.getIdentifierType(SessionFactoryImpl.java:1193)
at org.hibernate.type.EntityType.getIdentifierType(EntityType.java:635)
at org.hibernate.type.EntityType.getIdentifierOrUniqueKeyType(EntityType.java:672)
at org.hibernate.type.ManyToOneType.getColumnSpan(ManyToOneType.java:142)
at org.hibernate.tuple.entity.EntityMetamodel.create(EntityMetamodel.java:459)
at org.hibernate.tuple.entity.EntityMetamodel.buildGenerationStrategyPair(EntityMetamodel.java:424)
at org.hibernate.tuple.entity.EntityMetamodel.<init>(EntityMetamodel.java:266)
at org.hibernate.persister.entity.AbstractEntityPersister.<init>(AbstractEntityPersister.java:520)
at org.hibernate.persister.entity.SingleTableEntityPersister.<init>(SingleTableEntityPersister.java:148)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at org.hibernate.persister.internal.PersisterFactoryImpl.create(PersisterFactoryImpl.java:163)
at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:135)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:401)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1859)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:857)
... 8 more
At first glance, it seems the entity is not found by the factory, but removing the annotation @Generated from userGroup property, makes the sessionfactory building process working again.
Investigating in core hibernate code, brought me to org.hibernate.tuple.entity.EntityMetamodel#create() at line 459 (hibernate 4.3.10, but 5.0.0.CR1 is similar). Here the EntityMetamodel tries to calculate the numberOfMappedColumns from the association:
public static InDatabaseValueGenerationStrategyImpl create(
SessionFactoryImplementor sessionFactoryImplementor,
Property mappingProperty,
ValueGeneration valueGeneration) {
final int numberOfMappedColumns = mappingProperty.getType().getColumnSpan( sessionFactoryImplementor ); if ( numberOfMappedColumns == 1 ) {
return new InDatabaseValueGenerationStrategyImpl(
valueGeneration.getGenerationTiming(),
valueGeneration.referenceColumnInSql(),
new String[] { valueGeneration.getDatabaseGeneratedReferencedColumnValue() }
);
}
else {
[....]
}
The problem here is that the metamodel tries to get the UserGroup entity persister, before it is actually created, and execution flow stops at SessionFactotyImpl:1096 (here the entityPersisters field is actually an empty map):
public EntityPersister getEntityPersister(String entityName) throws MappingException {
EntityPersister result = entityPersisters.get(entityName);
if ( result == null ) {
throw new MappingException( "Unknown entity: " + entityName ); }
return result;
}
Removing the @Generated annotations makes the sessionfactory build ok.
Another trick: changing EntityMetamodel, line 459 from:
final int numberOfMappedColumns = mappingProperty.getType().getColumnSpan( sessionFactoryImplementor );
to
final int numberOfMappedColumns = 1;
makes the software work again, but I think this is not correct in all cases..
|