I know there are no plans to be able to remove an attribute mapped by a superclass but … ^^
I am working on a big and old project (efluid - created year 2000) and it is complex to modify the modeling of main objects which does not respect this constraint
A solution that I implemented to overcome this is to put @Transient on the attributes of the parent class (which in my case is abstract …), to use on the daughter class @Access ( AccessType.PROPERTY) and then redefine the getters of the fields I want to map.
@MappedSuperclass
public class EfluidBusinessObject {
@Id
String id;
@Transient
private Timestamp dateCreation;
public Timestamp getDateCreation() {
return dateCreation;
}
...
}
@Entity
@Access(PROPERTY)
public class Contrat extends EfluidBusinessObject {
@Override
public Timestamp getDateCreation() {
return super.getDateCreation();
}
...
}
What do you think ?
Also this constraint is very restrictive or even blocking
Is it technical or political not to want @Transient an attribute mapped by the parent class?
Your solution looks fine to me. What’s your problem with that? There is no way fields can be removed from super classes, that’s just impossible design-wise.
My solution is operational, it was just to find out if there was another way to do it or if it was not right to do it like this.
But a priori it is good, so I leave it like that and it is that I would propose if it is not possible to lower the attributes in the child classes.
I have another solution that I wanted to share with you to get your opinion.
I am exploiting @Parent to use the same attributes and @Embedded declaring the mapped attributes of the parent class.
The only surprising thing is that I have to put the setter in nullSafe because in the @Parent valuation mechanism seems late during the first call of the setter. The second call the parent is well valued.
Your opinion ?
public class HermesRisque extends HermesBusinessObject {
@Embedded
private DatesActeursTechniquesEtatObjetEmbarque attributsTechniques;
@Embeddable
@Access(AccessType.PROPERTY)
public class DatesActeursTechniquesEtatObjetEmbarque implements Serializable {
private HermesBusinessObject proprietaire;
@Parent
public HermesBusinessObject getProprietaire() {
return proprietaire;
}
public void setProprietaire(HermesBusinessObject proprietaire) {
this.proprietaire = proprietaire;
}
public Timestamp getDateCreation() {
return proprietaire.getDateCreation();
}
public void setDateCreation(Timestamp dateCreation) {
if (proprietaire != null) {
proprietaire.setDateCreation(dateCreation);
}
}
}
I don’t know if there are guarantees about what is initialized in which order, but to be safe, I would suggest you put the dateCreation value into a field and set that on the parent as soon as the parent is set. Apart from that, it looks ok as long as this does what you are looking for.
Here is the final solution :
I had to remove and the property but also the column from the table definition
package com.efluid.hibernate.integrator;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.*;
/**
* Permet d'indiquer les attributs persistés par une classe parent que l'on souhaite rendre Transient.
*/
@Target({ TYPE })
@Retention(RUNTIME)
public @interface TransientOverride {
GroupeAttributs[] groupesAttributs() default {};
String[] attributs() default {};
}
I have a concern because in the metadata the instantiation of the model of a MappedSuperClass is done at the first entity found and references the latter for its properties.
And the problem that appears is if the first class iterates is a class annotated with @AttributesTransient which has the effect of removing the properties defined on the current entity, then the properties of the MappedSuperClass will point to nowhere.
I am trying to work a solution in the integration to retrieve a MappedSuperClass which is based on an entity which does not have an @AttributesTransient annotation
It’s not possible to make an attribute from a super class transient in a subclass. I would suggest you rethink your modelling. Use interfaces for polymorphism and accept that you will have to declare fields and getters/setters in multiple implementations.
Well I persevered and found this solution.
For it to work, the 1st child class must not be annotated by @AttributesTransient, so I created a dummy class inheriting from the superclass in question.
@Entity
public class HibernateHermesBusinessObjectFactice extends HermesBusinessObject {}
And I sorted it 1st during Scan and then read metadata during session factory creation
1. Sorting after scanning :
<property name="hibernate.archive.scanner" value="com.efluid.hibernate.core.EfluidScanner"/>
public class EfluidScanner extends AbstractScannerImpl {
public EfluidScanner() {
this(EfluidArchiveDescriptorFactory.INSTANCE);
}
public EfluidScanner(ArchiveDescriptorFactory value) {
super(value);
}
@Override
public ScanResult scan(ScanEnvironment environment, ScanOptions options, ScanParameters parameters) {
ScanResult scan = super.scan(environment, options, parameters);
return new EfluidScanResult(scan);
}
}
/**
* Permet de placer en 1er une classe héritant de HermesBusiness mais sans avoir de {@link TransientOverride}.
*
* @See {@link TransientIntegrator}
**/
public class EfluidScanResult implements ScanResult {
private ScanResult scanResult;
public EfluidScanResult(ScanResult scanResult) {
this.scanResult = scanResult;
}
@Override
public Set<PackageDescriptor> getLocatedPackages() {
return scanResult.getLocatedPackages();
}
@Override
public Set<ClassDescriptor> getLocatedClasses() {
ClassDescriptor classDescriptorTypeCritere = scanResult.getLocatedClasses().stream().filter(o -> o.getName().equals(HibernateHermesBusinessObjectFactice.class.getName())).findFirst().get();
List<ClassDescriptor> classDescriptors = scanResult.getLocatedClasses().stream().collect(Collectors.toList());
classDescriptors.remove(classDescriptorTypeCritere);
LinkedHashSet<ClassDescriptor> locatedClasses = new LinkedHashSet();
locatedClasses.add(classDescriptorTypeCritere);
locatedClasses.addAll(classDescriptors);
return locatedClasses;
}
@Override
public Set<MappingFileDescriptor> getLocatedMappingFiles() {
return scanResult.getLocatedMappingFiles();
}
}
2. Sorting when building the session factory :
File: src/main/resources/META-INF/services/org.hibernate.service.spi.ServiceContributor
Content: com.efluid.hibernate.core.EfluidServiceContributor
public class EfluidServiceContributor implements ServiceContributor {
@Override
public void contribute(StandardServiceRegistryBuilder serviceRegistryBuilder) {
serviceRegistryBuilder.addService(SessionFactoryBuilderService.class, EfluidSessionFactoryBuilderService.INSTANCE);
}
}
public class EfluidSessionFactoryBuilderService implements SessionFactoryBuilderService {
protected static final EfluidSessionFactoryBuilderService INSTANCE = new EfluidSessionFactoryBuilderService();
@Override
public SessionFactoryBuilderImplementor createSessionFactoryBuilder(MetadataImpl metadata, BootstrapContext bootstrapContext) {
return new SessionFactoryBuilderImpl(new EfluidDelegatingMetadata(metadata), bootstrapContext);
}
}
public class EfluidDelegatingMetadata extends AbstractDelegatingMetadata {
private final List<PersistentClass> entityBindingsTriees;
public EfluidDelegatingMetadata(MetadataImplementor delegate) {
super(delegate);
entityBindingsTriees = super.getEntityBindings().stream().collect(toList());
triEntityBindings();
}
@Override
public Collection<PersistentClass> getEntityBindings() {
return entityBindingsTriees;
}
private void triEntityBindings() {
PersistentClass typeCriterePersistentClass = entityBindingsTriees.stream().filter(o -> o.getClassName().equals(HibernateHermesBusinessObjectFactice.class.getName())).findFirst().get();
entityBindingsTriees.remove(typeCriterePersistentClass);
entityBindingsTriees.add(0, typeCriterePersistentClass);
}
}