public void addAttributeConverterDefinition(AttributeConverterDefinition definition) {
      if (this.attributeConverterDefinitionsByClass == null) {
        this.attributeConverterDefinitionsByClass =
            new HashMap<Class, AttributeConverterDefinition>();
      }

      final Object old =
          this.attributeConverterDefinitionsByClass.put(
              definition.getAttributeConverter().getClass(), definition);

      if (old != null) {
        throw new AssertionFailure(
            String.format(
                "AttributeConverter class [%s] registered multiple times",
                definition.getAttributeConverter().getClass()));
      }
    }
Пример #2
0
  /**
   * Build a Hibernate Type that incorporates the JPA AttributeConverter. AttributeConverter works
   * totally in memory, meaning it converts between one Java representation (the entity attribute
   * representation) and another (the value bound into JDBC statements or extracted from results).
   * However, the Hibernate Type system operates at the lower level of actually dealing directly
   * with those JDBC objects. So even though we have an AttributeConverter, we still need to "fill
   * out" the rest of the BasicType data and bridge calls to bind/extract through the converter.
   *
   * <p>Essentially the idea here is that an intermediate Java type needs to be used. Let's use an
   * example as a means to illustrate... Consider an {@code AttributeConverter<Integer,String>}.
   * This tells Hibernate that the domain model defines this attribute as an Integer value (the
   * 'entityAttributeJavaType'), but that we need to treat the value as a String (the
   * 'databaseColumnJavaType') when dealing with JDBC (aka, the database type is a VARCHAR/CHAR):
   *
   * <ul>
   *   <li>When binding values to PreparedStatements we need to convert the Integer value from the
   *       entity into a String and pass that String to setString. The conversion is handled by
   *       calling {@link AttributeConverter#convertToDatabaseColumn(Object)}
   *   <li>When extracting values from ResultSets (or CallableStatement parameters) we need to
   *       handle the value via getString, and convert that returned String to an Integer. That
   *       conversion is handled by calling {@link
   *       AttributeConverter#convertToEntityAttribute(Object)}
   * </ul>
   *
   * @return The built AttributeConverter -> Type adapter
   * @todo : ultimately I want to see attributeConverterJavaType and attributeConverterJdbcTypeCode
   *     specify-able separately then we can "play them against each other" in terms of determining
   *     proper typing
   * @todo : see if we already have previously built a custom on-the-fly BasicType for this
   *     AttributeConverter; see note below about caching
   */
  @SuppressWarnings("unchecked")
  private Type buildAttributeConverterTypeAdapter() {
    // todo : validate the number of columns present here?

    final Class entityAttributeJavaType = attributeConverterDefinition.getEntityAttributeType();
    final Class databaseColumnJavaType = attributeConverterDefinition.getDatabaseColumnType();

    // resolve the JavaTypeDescriptor
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // For the JavaTypeDescriptor portion we simply resolve the "entity attribute representation"
    // part of
    // the AttributeConverter to resolve the corresponding descriptor.
    final JavaTypeDescriptor entityAttributeJavaTypeDescriptor =
        JavaTypeDescriptorRegistry.INSTANCE.getDescriptor(entityAttributeJavaType);

    // build the SqlTypeDescriptor adapter
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Going back to the illustration, this should be a SqlTypeDescriptor that handles the Integer
    // <-> String
    //		conversions.  This is the more complicated piece.  First we need to determine the JDBC type
    // code
    //		corresponding to the AttributeConverter's declared "databaseColumnJavaType" (how we read
    // that value out
    // 		of ResultSets).  See JdbcTypeJavaClassMappings for details.  Again, given example, this
    // should return
    // 		VARCHAR/CHAR
    int jdbcTypeCode =
        JdbcTypeJavaClassMappings.INSTANCE.determineJdbcTypeCodeForJavaClass(
            databaseColumnJavaType);
    if (isNationalized()) {
      jdbcTypeCode =
          NationalizedTypeMappings.INSTANCE.getCorrespondingNationalizedCode(jdbcTypeCode);
    }
    // find the standard SqlTypeDescriptor for that JDBC type code.
    final SqlTypeDescriptor sqlTypeDescriptor =
        SqlTypeDescriptorRegistry.INSTANCE.getDescriptor(jdbcTypeCode);
    // find the JavaTypeDescriptor representing the "intermediate database type representation".
    // Back to the
    // 		illustration, this should be the type descriptor for Strings
    final JavaTypeDescriptor intermediateJavaTypeDescriptor =
        JavaTypeDescriptorRegistry.INSTANCE.getDescriptor(databaseColumnJavaType);
    // and finally construct the adapter, which injects the AttributeConverter calls into the
    // binding/extraction
    // 		process...
    final SqlTypeDescriptor sqlTypeDescriptorAdapter =
        new AttributeConverterSqlTypeDescriptorAdapter(
            attributeConverterDefinition.getAttributeConverter(),
            sqlTypeDescriptor,
            intermediateJavaTypeDescriptor);

    // todo : cache the AttributeConverterTypeAdapter in case that AttributeConverter is applied
    // multiple times.

    final String name =
        AttributeConverterTypeAdapter.NAME_PREFIX
            + attributeConverterDefinition.getAttributeConverter().getClass().getName();
    final String description =
        String.format(
            "BasicType adapter for AttributeConverter<%s,%s>",
            entityAttributeJavaType.getSimpleName(), databaseColumnJavaType.getSimpleName());
    return new AttributeConverterTypeAdapter(
        name,
        description,
        attributeConverterDefinition.getAttributeConverter(),
        sqlTypeDescriptorAdapter,
        entityAttributeJavaType,
        databaseColumnJavaType,
        entityAttributeJavaTypeDescriptor);
  }
 @Override
 public MetadataBuilder applyAttributeConverter(AttributeConverter attributeConverter) {
   applyAttributeConverter(AttributeConverterDefinition.from(attributeConverter));
   return this;
 }
 @Override
 public MetadataBuilder applyAttributeConverter(
     AttributeConverter attributeConverter, boolean autoApply) {
   applyAttributeConverter(AttributeConverterDefinition.from(attributeConverter, autoApply));
   return this;
 }
 @Override
 public MetadataBuilder applyAttributeConverter(
     Class<? extends AttributeConverter> attributeConverterClass, boolean autoApply) {
   applyAttributeConverter(AttributeConverterDefinition.from(attributeConverterClass, autoApply));
   return this;
 }