Announcement Announcement Module
Collapse
No announcement yet.
Unable to define/override Version on a subclass Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Unable to define/override Version on a subclass

    Hello,

    We have a legacy database without version column, so we avoid it with the attribute versionField = "" within the @RooEntity annotation.

    However, I forgot to do that in the child classes of a hierarchy, but not in the parent class, and I have this error:

    Code:
    -------------------------------------------------------------------------------
    Test set: com.malsolo.codes.analisis.domain.AlertaComercioIntegrationTest
    -------------------------------------------------------------------------------
    Tests run: 9, Failures: 0, Errors: 9, Skipped: 0, Time elapsed: 2.329 sec <<< FAILURE!
    testMarkerMethod(com.malsolo.codes.analisis.domain.AlertaComercioIntegrationTest)  Time elapsed: 1.153 sec  <<< ERROR!
    java.lang.IllegalStateException: Failed to load ApplicationContext
    	at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:308)
    ...
    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager' defined in class path resource [META-INF/spring/applicationContext.xml]: Cannot resolve reference to bean 'entityManagerFactory' while setting bean property 'entityManagerFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [META-INF/spring/applicationContext.xml]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: Unable to define/override @Version on a subclass: com.malsolo.codes.analisis.domain.AlertaComercio
    	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
    ...
    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [META-INF/spring/applicationContext.xml]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: Unable to define/override @Version on a subclass: com.malsolo.codes.analisis.domain.AlertaComercio
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1420)
    ...
    Caused by: org.hibernate.AnnotationException: Unable to define/override @Version on a subclass: com.malsolo.codes.analisis.domain.AlertaComercio
    	at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:1532)
    	at org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:796)
    	at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:707)
    	at org.hibernate.cfg.Configuration$MetadataSourceQueue.processAnnotatedClassesQueue(Configuration.java:4035)
    	at org.hibernate.cfg.Configuration$MetadataSourceQueue.processMetadata(Configuration.java:3989)
    	at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1398)
    	at org.hibernate.cfg.Configuration.buildMappings(Configuration.java:1375)
    	at org.hibernate.ejb.Ejb3Configuration.buildMappings(Ejb3Configuration.java:1519)
    	at org.hibernate.ejb.EventListenerConfigurator.configure(EventListenerConfigurator.java:193)
    	at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:1100)
    	at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:689)
    	at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:73)
    	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:225)
    	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:308)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
    	... 53 more
    Here is a script Roo to reproduce the error (Windows, STS 2.7.1, Roo 1.1.5, PostgreSQL)

    Code:
    //Entity: parent, with common fields. No version field (update the @RooEntity annotation in the Java Entity once created)
    entity --class ~.analisis.domain.Alerta --abstract --table alerta --identifierField codigo --identifierColumn alerta_id --inheritanceType SINGLE_TABLE
    //Id pushed in the Java class because we need to specify the , columnDefinition within the @Column annotation
    field string --fieldName tipo --notNull  --sizeMax 1  --column alerta_type
    //@DiscriminatorColumn(name="alerta_type", columnDefinition="char(1)")
    field date --fieldName fecha --notNull   --type java.util.Date  --column alerta_date
    //field date --fieldName fecha --notNull   --type java.sql.Timestamp  --column alerta_tmst
    field number --fieldName datos --notNull   --type java.lang.Integer  --column alerta_data
    field string --fieldName comentario   --sizeMax 40 --column alerta_come
    //Entity: child, with a calculated transient field based on the parent's data. No version field as well (but I forgot to update in the @RooEntity annotation)
    entity --class ~.analisis.domain.AlertaComercio --extends ~.analisis.domain.Alerta --testAutomatically
    //@DiscriminatorValue("1")
    field string --fieldName codigoComercio --notNull  --sizeMax 9 --transient
    The problem: because I forgot to eliminate the version field in the child class but I did it in the parent class, this code in the _entity.aj is no longer valid:

    Code:
        @Version
        @Column(name = "version")
        private Integer AlertaComercio.version;
        
        public Integer AlertaComercio.getVersion() {
            return this.version;//It doesn't exist actually
        }
    Using @RooEntity(versionField = "") in the child class solves the problem:

    Code:
    @RooJavaBean
    @RooToString
    @RooEntity(identifierField = "codigo", identifierColumn = "alerta_id", table = "alerta", inheritanceType = "SINGLE_TABLE", versionField = "")
    @DiscriminatorColumn(name = "alerta_type", columnDefinition = "char(1)")
    public abstract class Alerta {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name = "alerta_id", columnDefinition = "integer")
        private Long codigo;
    
        @NotNull
        @Column(name = "alerta_data", columnDefinition = "decimal(15)")
        private Integer datos;
    
        @Column(name = "alerta_come", columnDefinition = "char(40)")
        @Size(max = 40)
        private String comentario;
    
        @NotNull
        @Column(name = "alerta_tmst")
        @Temporal(TemporalType.TIMESTAMP)
        @DateTimeFormat(style = "M-")
        private Date fecha;
    
    }
    
    @RooJavaBean
    @RooToString
    @RooEntity(versionField = "")
    @DiscriminatorValue("1")
    public class AlertaComercio extends Alerta {
    
        @NotNull
        @Size(max = 9)
        private transient String codigoComercio;
    
        public String getCodigoComercio() {
            return codigoComercio;
        }
    
        @Override
        public void setDatos(Integer arg0) {
            super.setDatos(arg0);
            this.codigoComercio = arg0.toString().substring(1);
        }
    }

  • #2
    This appears to be working as designed. Looking at my code in EntityMetadata, you still need to prevent the child class from generating a version field (using versionField="") as the parent has not provided the field, otherwise the field is created.

    Code:
    	private FieldMetadata getVersionField() {
    		if (parentEntity != null) {
    			final FieldMetadata result = parentEntity.getVersionField();
    			if (result != null) {
    				return result;
    			}
    		}
    		
    		// Try to locate an existing field with @Version
    		final List<FieldMetadata> found = MemberFindingUtils.getFieldsWithAnnotation(governorTypeDetails, new JavaType("javax.persistence.Version"));
    		if (found.size() > 0) {
    			Assert.isTrue(found.size() == 1, "More than 1 field was annotated with @Version in '" + destination.getFullyQualifiedTypeName() + "'");
    			return found.get(0);
    		}
    ...
    Alan

    Comment


    • #3
      Ok.

      According KISS, it's a good design decission.

      However, in my humble opinion, the code that Roo creates in my application is not correct, according Hibernate.

      I presume that Hibernate treats the @Version field/column with care in a SINGLE_TABLE inheritance type, but I haven't checked in the Hibernate code nor in other ORMs.

      I appreciate your dedication and prompt response.
      Last edited by jbbarquero; Sep 2nd, 2011, 10:13 AM. Reason: Bad expressed

      Comment


      • #4
        Hello again,

        I see that Hibernate considers that only a Root class should have a @Version property, at org.hibernate.cfg.AnnotationBinder.processElementA nnotations(AnnotationBinder.java:1532) you can see:

        Code:
        			if ( !( propertyHolder.getPersistentClass() instanceof RootClass ) ) {//NOTE: RootClass javadoc: The root class of an inheritance hierarchy
        				throw new AnnotationException(
        						"Unable to define/override @Version on a subclass: "
        								+ propertyHolder.getEntityName()
        				);
        			}
        But it seems they're not very convinced of it, because at org.hibernate.mapping.RootClass(48) you can see:

        Code:
        	private Property version; //may be final

        Comment

        Working...
        X