DefaultDiscriminatorConverter with hashcode to ROLE (DTYPE)

Hi,

In our information system, we have encoded roles to save space and also to be able to rename or move the relevant classes (not having the Java model in the database).

Example :

It’s silimary to :
@AnyDiscriminatorValue(discriminator = "AA", entity = Agent.class)

We cannot use @AnyDiscriminatorValue if we want to use DefaultDiscriminatorConverter to reference many entities. Also, if the entity is not known from the library, this poses a second problem.

One solution is to use DefaultDiscriminatorConverter and convert the entity name into a hashcode. However, there is no possibility to override DiscriminatedAssociationAttributeMapping , so I have to duplicate a bit of code to achieve this.

  • JavaRoleMapUtil is us process for hashcode the role.
  • Override to DiscriminatedAssociationAttributeMapping for use JavaRoleMapUtil
public class EfluidDiscriminatedAssociationAttributeMapping extends DiscriminatedAssociationAttributeMapping {

  public EfluidDiscriminatedAssociationAttributeMapping(NavigableRole attributeRole, JavaType<?> baseAssociationJtd,
                                                        ManagedMappingType declaringType, int stateArrayPosition, int fetchableIndex, AttributeMetadata attributeMetadata,
                                                        FetchTiming fetchTiming, PropertyAccess propertyAccess, Property bootProperty, AnyType anyType,
                                                        Any bootValueMapping, MappingModelCreationProcess creationProcess) {
    super(attributeRole, baseAssociationJtd, declaringType, stateArrayPosition, fetchableIndex, attributeMetadata, fetchTiming, propertyAccess, bootProperty, anyType, bootValueMapping, creationProcess);
  }

  @Override
  public EntityMappingType resolveDiscriminatorValue(Object discriminatorValue) {
    try {
      return super.resolveDiscriminatorValue(discriminatorValue);
    } catch (HibernateException ex) {
      if (discriminatorValue instanceof String s) {
        return super.resolveDiscriminatorValue(JavaRoleMapUtil.fromHashCode(s));
      }
      throw ex;
    }
  }

  @Override
  public Object resolveDiscriminatorForEntityType(EntityMappingType entityMappingType) {
    Object object = super.resolveDiscriminatorForEntityType(entityMappingType);
    if (object instanceof String s) {
      return Optional.ofNullable(JavaRoleMapUtil.toHashCode(s)).orElse(s);
    }
    return object;
  }
}
  • The code duplicated for use EfluidDiscriminatedAssociationAttributeMapping :
public class EfluidSingleTableEntityPersister extends SingleTableEntityPersister {

  public EfluidSingleTableEntityPersister(PersistentClass persistentClass, EntityDataAccess cacheAccessStrategy, NaturalIdDataAccess naturalIdRegionAccessStrategy, PersisterCreationContext creationContext) throws HibernateException {
    super(persistentClass, cacheAccessStrategy, naturalIdRegionAccessStrategy, creationContext);
  }

  /**
   * Cette surcharge permet de gérer le {@link com.hermes.arc.commun.util.JavaRoleMapUtil} pour les relations @{@link Any}.
   */
  @Override
  protected AttributeMapping generateNonIdAttributeMapping(NonIdentifierAttribute tupleAttrDefinition, Property bootProperty, int stateArrayPosition, int fetchableIndex, MappingModelCreationProcess creationProcess) {
    final Type attrType = tupleAttrDefinition.getType();
    if (attrType instanceof AnyType) {
      return getDiscriminatedAssociationAttributeMapping(bootProperty, stateArrayPosition, fetchableIndex, creationProcess, (AnyType) attrType);
    } else {
      return super.generateNonIdAttributeMapping(tupleAttrDefinition, bootProperty, stateArrayPosition, fetchableIndex, creationProcess);
    }
  }

  /**
   * Code dupliqué de {@link org.hibernate.persister.entity.AbstractEntityPersister#generateNonIdAttributeMapping} pour la partie AnyType.
   * Aucune autre modification que l'utilisation de {@link EfluidDiscriminatedAssociationAttributeMapping}.
   */
  private DiscriminatedAssociationAttributeMapping getDiscriminatedAssociationAttributeMapping(final Property bootProperty, final int stateArrayPosition, final int fetchableIndex, final MappingModelCreationProcess creationProcess, final AnyType attrType) {
    PropertyAccess propertyAccess = getRepresentationStrategy().resolvePropertyAccess(bootProperty);
    RuntimeModelCreationContext creationContext = creationProcess.getCreationContext();
    Value value = bootProperty.getValue();
    JavaType<Object> baseAssociationJtd = creationContext.getTypeConfiguration().getJavaTypeRegistry().getDescriptor(Object.class);

    MutabilityPlan<?> mutabilityPlan = new DiscriminatedAssociationAttributeMapping.MutabilityPlanImpl(attrType);
    SimpleAttributeMetadata attributeMetadataAccess = new SimpleAttributeMetadata(propertyAccess, mutabilityPlan, bootProperty.isOptional(), bootProperty.isInsertable(), bootProperty.isUpdateable(), bootProperty.isOptimisticLocked(), bootProperty.isSelectable());

    return new EfluidDiscriminatedAssociationAttributeMapping(getNavigableRole().append(bootProperty.getName()), baseAssociationJtd, this, stateArrayPosition, fetchableIndex, attributeMetadataAccess, bootProperty.isLazy() ? FetchTiming.DELAYED : FetchTiming.IMMEDIATE, propertyAccess, bootProperty, attrType, (Any) value, creationProcess);
  }
}

Do you also have a question or a request? :sweat_smile:

Oh, sorry! Yes :slight_smile: :

  • Is there a cleaner way to override it?
  • Alternatively, can I submit a PR to propose an entry point?

So you are defining global discriminator values for classes to use when using @Any? I don’t quite understand the role of JavaRoleMapUtil. What does it do?

For the class entity Lot.class
with entityName "com.hermes.ref.campagne.businessobject.Lot"
we want in database the value “L”.

The DefaultDiscriminatorConverter use only the entityName "com.hermes.ref.campagne.businessobject.Lot" for the any key value.

database mappingMetaModel#key mappingMetaModel#value
“L” “com.hermes.ref.campagne.businessobject.Lot” Lot.class

"L""com.hermes.ref.campagne.businessobject.Lot"Lot.class

But maybe I didn’t understand what you didn’t understand?

What is BDD? Is that the type column for @Any mappings?

Sorry !
BDD = database
In french (Base De Données)

  • The global discriminator is the MappingMetatmodelImpl, valorised by hibernate

  • I combine with our javaRoleMap

I still don’t understand what you’re trying to do here. You write

It’s silimary to :
@AnyDiscriminatorValue(discriminator = "AA", entity = Agent.class)

Tell me, how is what you are doing different from using annotations?

So there are two differences :

  • the first is that I don’t know the exhaustive list of referenced types
  • the second is that some types are only known at runtime (defined in another JAR).

I had implemented this solution → Lots of @AnyDiscriminatorValue - Hibernate ORM - Hibernate to solve these two problems.

Which I was able to eliminate with the implementation of DefaultDiscriminatorConverter → HHH-16339 - Consolidate DiscriminatorType, DiscriminatorMetadata, MetadataTpe by sebersole · Pull Request #6286 · hibernate/hibernate-orm (github.com)

However, the hashCode of the roles still needed to be resolved.

I think we talked about this before. ORM 5 and older versions supported referring to all entities of the persistence unit if no explicit list of @AnyDiscriminatorValue was present, but we lack this feature in ORM 6. We’d very much appreciate if you could work on this.

Using short names instead of the FQN should IMO be made possible through converters, but I suggest you start a discussion about this on Zulip.