use of org.hibernate.boot.model.convert.spi.JpaAttributeConverterCreationContext in project hibernate-orm by hibernate.
the class SimpleValue method buildAttributeConverterTypeAdapter.
/**
* 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>
* <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)}
* </li>
* </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 JpaAttributeConverter jpaAttributeConverter = attributeConverterDescriptor.createJpaAttributeConverter(new JpaAttributeConverterCreationContext() {
@Override
public ManagedBeanRegistry getManagedBeanRegistry() {
return getMetadata().getMetadataBuildingOptions().getServiceRegistry().getService(ManagedBeanRegistry.class);
}
@Override
public org.hibernate.type.descriptor.java.spi.JavaTypeDescriptorRegistry getJavaTypeDescriptorRegistry() {
return metadata.getTypeConfiguration().getJavaTypeDescriptorRegistry();
}
});
final BasicJavaDescriptor entityAttributeJavaTypeDescriptor = jpaAttributeConverter.getDomainJavaTypeDescriptor();
// 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
final SqlTypeDescriptor recommendedSqlType = jpaAttributeConverter.getRelationalJavaTypeDescriptor().getJdbcRecommendedSqlType(// todo (6.0) : handle the other JdbcRecommendedSqlTypeMappingContext methods
metadata::getTypeConfiguration);
int jdbcTypeCode = recommendedSqlType.getSqlType();
if (isLob()) {
if (LobTypeMappings.INSTANCE.hasCorrespondingLobCode(jdbcTypeCode)) {
jdbcTypeCode = LobTypeMappings.INSTANCE.getCorrespondingLobCode(jdbcTypeCode);
} else {
if (Serializable.class.isAssignableFrom(entityAttributeJavaTypeDescriptor.getJavaType())) {
jdbcTypeCode = Types.BLOB;
} else {
throw new IllegalArgumentException(String.format(Locale.ROOT, "JDBC type-code [%s (%s)] not known to have a corresponding LOB equivalent, and Java type is not Serializable (to use BLOB)", jdbcTypeCode, JdbcTypeNameMapper.getTypeName(jdbcTypeCode)));
}
}
}
if (isNationalized()) {
jdbcTypeCode = NationalizedTypeMappings.INSTANCE.getCorrespondingNationalizedCode(jdbcTypeCode);
}
// find the standard SqlTypeDescriptor for that JDBC type code (allow itr to be remapped if needed!)
final SqlTypeDescriptor sqlTypeDescriptor = getMetadata().getMetadataBuildingOptions().getServiceRegistry().getService(JdbcServices.class).getJdbcEnvironment().getDialect().remapSqlTypeDescriptor(metadata.getTypeConfiguration().getSqlTypeDescriptorRegistry().getDescriptor(jdbcTypeCode));
// and finally construct the adapter, which injects the AttributeConverter calls into the binding/extraction
// process...
final SqlTypeDescriptor sqlTypeDescriptorAdapter = new AttributeConverterSqlTypeDescriptorAdapter(jpaAttributeConverter, sqlTypeDescriptor, jpaAttributeConverter.getRelationalJavaTypeDescriptor());
// todo : cache the AttributeConverterTypeAdapter in case that AttributeConverter is applied multiple times.
final String name = AttributeConverterTypeAdapter.NAME_PREFIX + jpaAttributeConverter.getConverterJavaTypeDescriptor().getJavaType().getName();
final String description = String.format("BasicType adapter for AttributeConverter<%s,%s>", jpaAttributeConverter.getDomainJavaTypeDescriptor().getJavaType().getSimpleName(), jpaAttributeConverter.getRelationalJavaTypeDescriptor().getJavaType().getSimpleName());
return new AttributeConverterTypeAdapter(name, description, jpaAttributeConverter, sqlTypeDescriptorAdapter, jpaAttributeConverter.getDomainJavaTypeDescriptor().getJavaType(), jpaAttributeConverter.getRelationalJavaTypeDescriptor().getJavaType(), entityAttributeJavaTypeDescriptor);
}
Aggregations