boutss
February 27, 2023, 1:27pm
1
Hello,
In a hundred objects I have the use of an Action
object collection
The Action
object can only be referenced by a single object, so we use a foreign key with the role in the TACTION
table
As linked entities can be of any type we are forced to use an IPossedeAction
interface and the use of @Any
for hibernate persistence.
But it forces to define more than 100 @AnyDiscriminatorValue
public class Materiel
@OneToMany(fetch = LAZY, cascade = ALL)
@JoinColumn(name = "OBJETMAITRE_ID") // must be delete for use "mappedby" with @any
private Set<Action> actions = new HashSet<>();
public class Action
@Any(fetch = LAZY)
@AnyKeyJavaClass(String.class)
@AnyDiscriminatorValue(discriminator = "com.hermes.ref.affaireaction.businessobject.AffaireEcore", entity = AffaireEcore.class)
@AnyDiscriminatorValue(discriminator = "com.hermes.ref.acteur.businessobject.ActeurEcore", entity = ActeurEcore.class)
...
@JoinColumn(name = "OBJETMAITRE_ID")
@Column(name = "OBJETMAITRE_ROLE")
private IPossedeActions objetMaitre;
About ten objects contain an action list, but they themselves are broken down into numerous sub-objects
Example, Materiel
has 35 child classes
How can we make it easier than setting 100 @AnyDiscriminatorValue ?
Currently I’m trying via an Integrator to add all the @Entites that implement this interface but I’m still manipulating the metadata…
beikov
February 27, 2023, 1:38pm
2
I don’t think there is an easy way to do this, but do you really need this to be an association? Can’t you just store the primary key as Long
and a Class
:
@Column(name = "OBJETMAITRE_ID")
private Long objetMaitreId;
@Column(name = "OBJETMAITRE_ROLE")
private Class<?> objetMaitreRole;
public void setObjetMaitre(IPossedeActions objetMaitre) {
objetMaitreId = objetMaitre.getId();
objetMaitreRole = objetMaitre.getClass();
}
Then, when you need access to the object, just do session.find(objetMaitreRole, objetMaitreId)
1 Like
boutss
February 27, 2023, 1:57pm
3
Thank you for the answer.
It’s a good idea, which I will consider.
But currently we are obliged to keep backward compatibility with the old proprietary ORM.
I will do this when we are in full hibernate.
But at least I have the information that there is not a simple solution to do this.
boutss
February 28, 2023, 3:57pm
4
Hi,
I have a solution for automatically add the @AnyDiscriminatorValue with a Integrator.
Even if the idea is to simplify the model and use this mechanism very little
I must :
get package-info files (i don’t’ found the information in the integrator argument)
public class EfluidScanner extends StandardScanner {
public static final Set<String> packageInfos = new HashSet<>();
@Override
public ScanResult scan(ScanEnvironment environment, ScanOptions options, ScanParameters parameters) {
ScanResult scan = super.scan(environment, options, parameters);
scan.getLocatedPackages().forEach(packageDescriptor -> packageInfos.add(packageDescriptor.getName()));
return new EfluidScanResult(scan);
}
}
which contains the custom annotation @AnyInterfaces for determining the interface to exploited (type of attribut annoted with @Any )
@Target({ PACKAGE })
@Retention(RUNTIME)
public @interface AnyInterfaces {
// Liste des interfaces utilisées par des attributs annotés @Any
Class<?>[] interfaces();
}
package-info.java
@AnyInterfaces(interfaces = {
IActeurInterne.class,
IEntiteAdministrative.class,
IBusinessObjectParametrable.class,
IObjetDestinataireEdition.class,
BusinessObject.class,
HermesBusinessObject.class,}
)
package com.efluid.hibernate.core.scanner;
import com.imrglobal.framework.businessObject.BusinessObject;
import com.hermes.arc.commun.businessobject.HermesBusinessObject;
import com.hermes.arc.edition.businessobject.IObjetDestinataireEdition;
import com.hermes.arc.habilitation.businessobject.IActeurInterne;
import com.hermes.arc.habilitation.businessobject.IEntiteAdministrative;
import com.hermes.arc.modeleobjetmetier.businessobject.IBusinessObjectParametrable;
The integrator
3.1 List the interfaces
3.2 List the entity which implements interface
3.3 Add all implementation, same with @AnyDiscriminatorValue , to @Any attributes which returns a class with the interface
public class AnyDiscriminatorValueIntegrator implements Integrator {
private static final Logger LOG = LoggerFactory.getLogger(AnyDiscriminatorValueIntegrator.class);
private static final String SEPARATOR = "::";
// Interfaces @Any
private Set<Class<?>> interfaces = new HashSet<>();
// Entités qui implémentent les interfaces @Any.
private Map<String, List<String>> implementationsInterface = new HashMap<>();
@Override
public void integrate(Metadata metadata, BootstrapContext bootstrapContext, SessionFactoryImplementor sessionFactory) {
recuperationInterfaceAnySurPackageInfo(bootstrapContext);
metadata.getEntityBindings().forEach(this::identifierEntiteImplementInterfaces);
LOG.debug("Implémentations des interfaces {}", implementationsInterface);
metadata.getEntityBindings().forEach(this::addAnyDiscriminatorValues);
}
private void recuperationInterfaceAnySurPackageInfo(BootstrapContext bootstrapContext) {
for (String nomPackage : EfluidScanner.packageInfos) {
AnyInterfaces annotation = getAnnotationAnyInterfaces(bootstrapContext, nomPackage);
if (annotation != null) {
for (Class<?> anInterface : annotation.interfaces()) {
interfaces.add(anInterface);
implementationsInterface.put(anInterface.getName(), new ArrayList());
}
}
}
}
private void identifierEntiteImplementInterfaces(PersistentClass persistentClass) {
for (Class anInterface : interfaces) {
if (anInterface.isAssignableFrom(persistentClass.getMappedClass())) {
DiscriminatorValue annotationDiscriminatorValue = persistentClass.getMappedClass().getAnnotation(DiscriminatorValue.class);
implementationsInterface.get(anInterface.getName())
.add(persistentClass.getClassName()
+ SEPARATOR
+ (annotationDiscriminatorValue != null ? annotationDiscriminatorValue.value() : persistentClass.getClassName()));
}
}
}
private void addAnyDiscriminatorValues(PersistentClass persistentClass) {
persistentClass.getProperties().stream().filter(property -> property.getValue() instanceof Any).forEach(this::addAnyDiscriminatorValue);
}
private static AnyInterfaces getAnnotationAnyInterfaces(BootstrapContext bootstrapContext, String nomPackage) {
ClassLoaderService classLoaderService = bootstrapContext.getServiceRegistry().getService(ClassLoaderService.class);
Package packaze = classLoaderService.packageForNameOrNull(nomPackage);
XPackage pckg = bootstrapContext.getReflectionManager().toXPackage(packaze);
return pckg.getAnnotation(AnyInterfaces.class);
}
private void addAnyDiscriminatorValue(Property propriete) {
Any any = (Any) propriete.getValue();
Map<Object, String> metaValues = any.getMetaValues();
String typeRetour = (String) any.getDiscriminatorDescriptor().getTypeParameters().get(DynamicParameterizedType.RETURNED_CLASS);
List<String> implementations = implementationsInterface.get(typeRetour);
if (implementations != null) {
implementations.forEach(entite -> metaValues.put(entite.split(SEPARATOR)[1], entite.split(SEPARATOR)[0]));
} else if (metaValues.isEmpty()) {
LOG.error("""
L'interface %s pour l'attribut %s#%s annoté @Any
n'est pas déclarée dans une annotation AnyDiscriminatorValueIntegrator#interfaces
et ne possède pas de @AnyDiscriminatorValue.
""".formatted(typeRetour, propriete.getPersistentClass().getClassName(), propriete.getName()));
}
}
@Override
public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
LOG.debug("Exécuté lors d'une exception");
}
}
boutss
April 5, 2024, 9:38am
5
At first glance, since this PR, hibernate will retrieve the entities from the metamodel for the @Any
transparently, without having to provide a list of @AnyDiscriminatorValue
.
HHH-16339 - Unify entity and any discriminator handling