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())); } }
/** * 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; }