[HHH-17294] DeepCopy non-Embedded JSON or XML JdbcTypCode attribute using FormatMapper
This commit is contained in:
parent
2e0f0c5b58
commit
47e827200e
|
@ -152,6 +152,7 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
||||||
private TemporalType temporalPrecision;
|
private TemporalType temporalPrecision;
|
||||||
private TimeZoneStorageType timeZoneStorageType;
|
private TimeZoneStorageType timeZoneStorageType;
|
||||||
private boolean partitionKey;
|
private boolean partitionKey;
|
||||||
|
private Integer jdbcTypeCode;
|
||||||
|
|
||||||
private Table table;
|
private Table table;
|
||||||
private AnnotatedColumns columns;
|
private AnnotatedColumns columns;
|
||||||
|
@ -1072,6 +1073,12 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
final org.hibernate.annotations.JdbcTypeCode jdbcType =
|
||||||
|
findAnnotation( attributeXProperty, org.hibernate.annotations.JdbcTypeCode.class );
|
||||||
|
if ( jdbcType != null ) {
|
||||||
|
jdbcTypeCode = jdbcType.value();
|
||||||
|
}
|
||||||
|
|
||||||
normalJdbcTypeDetails( attributeXProperty);
|
normalJdbcTypeDetails( attributeXProperty);
|
||||||
normalMutabilityDetails( attributeXProperty );
|
normalMutabilityDetails( attributeXProperty );
|
||||||
|
|
||||||
|
@ -1223,6 +1230,10 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
||||||
basicValue.setTemporalPrecision( temporalPrecision );
|
basicValue.setTemporalPrecision( temporalPrecision );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( jdbcTypeCode != null ) {
|
||||||
|
basicValue.setExplicitJdbcTypeCode( jdbcTypeCode );
|
||||||
|
}
|
||||||
|
|
||||||
linkWithValue();
|
linkWithValue();
|
||||||
|
|
||||||
boolean isInSecondPass = buildingContext.getMetadataCollector().isInSecondPass();
|
boolean isInSecondPass = buildingContext.getMetadataCollector().isInSecondPass();
|
||||||
|
|
|
@ -12,6 +12,7 @@ import java.util.Properties;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.hibernate.Incubating;
|
||||||
import org.hibernate.Internal;
|
import org.hibernate.Internal;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.TimeZoneStorageStrategy;
|
import org.hibernate.TimeZoneStorageStrategy;
|
||||||
|
@ -46,6 +47,7 @@ import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
|
||||||
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||||
import org.hibernate.type.BasicType;
|
import org.hibernate.type.BasicType;
|
||||||
import org.hibernate.type.CustomType;
|
import org.hibernate.type.CustomType;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
import org.hibernate.type.WrapperArrayHandling;
|
import org.hibernate.type.WrapperArrayHandling;
|
||||||
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
|
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
|
||||||
|
@ -53,6 +55,10 @@ import org.hibernate.type.descriptor.java.BasicJavaType;
|
||||||
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.java.MutabilityPlan;
|
import org.hibernate.type.descriptor.java.MutabilityPlan;
|
||||||
|
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
||||||
|
import org.hibernate.type.descriptor.java.spi.JsonJavaType;
|
||||||
|
import org.hibernate.type.descriptor.java.spi.RegistryHelper;
|
||||||
|
import org.hibernate.type.descriptor.java.spi.XmlJavaType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||||
import org.hibernate.type.internal.BasicTypeImpl;
|
import org.hibernate.type.internal.BasicTypeImpl;
|
||||||
|
@ -97,6 +103,7 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// Resolved state - available after `#resolve`
|
// Resolved state - available after `#resolve`
|
||||||
private Resolution<?> resolution;
|
private Resolution<?> resolution;
|
||||||
|
private Integer jdbcTypeCode;
|
||||||
|
|
||||||
|
|
||||||
public BasicValue(MetadataBuildingContext buildingContext) {
|
public BasicValue(MetadataBuildingContext buildingContext) {
|
||||||
|
@ -495,15 +502,15 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
||||||
|
|
||||||
private JavaType<?> determineJavaType(JavaType<?> explicitJavaType) {
|
private JavaType<?> determineJavaType(JavaType<?> explicitJavaType) {
|
||||||
JavaType<?> javaType = explicitJavaType;
|
JavaType<?> javaType = explicitJavaType;
|
||||||
|
//
|
||||||
if ( javaType == null ) {
|
// if ( javaType == null ) {
|
||||||
if ( implicitJavaTypeAccess != null ) {
|
// if ( implicitJavaTypeAccess != null ) {
|
||||||
final java.lang.reflect.Type implicitJtd = implicitJavaTypeAccess.apply( getTypeConfiguration() );
|
// final java.lang.reflect.Type implicitJtd = implicitJavaTypeAccess.apply( getTypeConfiguration() );
|
||||||
if ( implicitJtd != null ) {
|
// if ( implicitJtd != null ) {
|
||||||
javaType = getTypeConfiguration().getJavaTypeRegistry().getDescriptor( implicitJtd );
|
// javaType = getTypeConfiguration().getJavaTypeRegistry().getDescriptor( implicitJtd );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if ( javaType == null ) {
|
if ( javaType == null ) {
|
||||||
final JavaType<?> reflectedJtd = determineReflectedJavaType();
|
final JavaType<?> reflectedJtd = determineReflectedJavaType();
|
||||||
|
@ -518,11 +525,12 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
||||||
private JavaType<?> determineReflectedJavaType() {
|
private JavaType<?> determineReflectedJavaType() {
|
||||||
final java.lang.reflect.Type impliedJavaType;
|
final java.lang.reflect.Type impliedJavaType;
|
||||||
|
|
||||||
|
final TypeConfiguration typeConfiguration = getTypeConfiguration();
|
||||||
if ( resolvedJavaType != null ) {
|
if ( resolvedJavaType != null ) {
|
||||||
impliedJavaType = resolvedJavaType;
|
impliedJavaType = resolvedJavaType;
|
||||||
}
|
}
|
||||||
else if ( implicitJavaTypeAccess != null ) {
|
else if ( implicitJavaTypeAccess != null ) {
|
||||||
impliedJavaType = implicitJavaTypeAccess.apply( getTypeConfiguration() );
|
impliedJavaType = implicitJavaTypeAccess.apply( typeConfiguration );
|
||||||
}
|
}
|
||||||
else if ( ownerName != null && propertyName != null ) {
|
else if ( ownerName != null && propertyName != null ) {
|
||||||
impliedJavaType = ReflectHelper.reflectedPropertyType(
|
impliedJavaType = ReflectHelper.reflectedPropertyType(
|
||||||
|
@ -541,7 +549,40 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return getTypeConfiguration().getJavaTypeRegistry().resolveDescriptor( impliedJavaType );
|
final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry();
|
||||||
|
final JavaType<Object> javaType = javaTypeRegistry.findDescriptor( impliedJavaType );
|
||||||
|
final MutabilityPlan<Object> explicitMutabilityPlan = explicitMutabilityPlanAccess != null
|
||||||
|
? explicitMutabilityPlanAccess.apply( typeConfiguration )
|
||||||
|
: null;
|
||||||
|
final MutabilityPlan<Object> determinedMutabilityPlan = explicitMutabilityPlan != null
|
||||||
|
? explicitMutabilityPlan
|
||||||
|
: RegistryHelper.INSTANCE.determineMutabilityPlan( impliedJavaType, typeConfiguration );
|
||||||
|
if ( javaType == null ) {
|
||||||
|
if ( jdbcTypeCode != null ) {
|
||||||
|
// Construct special JavaType instances for JSON/XML types which can report recommended JDBC types
|
||||||
|
// and implement toString/fromString as well as copying based on FormatMapper operations
|
||||||
|
switch ( jdbcTypeCode ) {
|
||||||
|
case SqlTypes.JSON:
|
||||||
|
final JavaType<Object> jsonJavaType = new JsonJavaType<>(
|
||||||
|
impliedJavaType,
|
||||||
|
determinedMutabilityPlan,
|
||||||
|
typeConfiguration
|
||||||
|
);
|
||||||
|
javaTypeRegistry.addDescriptor( jsonJavaType );
|
||||||
|
return jsonJavaType;
|
||||||
|
case SqlTypes.SQLXML:
|
||||||
|
final JavaType<Object> xmlJavaType = new XmlJavaType<>(
|
||||||
|
impliedJavaType,
|
||||||
|
determinedMutabilityPlan,
|
||||||
|
typeConfiguration
|
||||||
|
);
|
||||||
|
javaTypeRegistry.addDescriptor( xmlJavaType );
|
||||||
|
return xmlJavaType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return javaTypeRegistry.resolveDescriptor( impliedJavaType );
|
||||||
|
}
|
||||||
|
return javaType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Resolution<?> interpretExplicitlyNamedType(
|
private static Resolution<?> interpretExplicitlyNamedType(
|
||||||
|
@ -871,6 +912,11 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
||||||
return javaTypeClass == Byte[].class || javaTypeClass == Character[].class;
|
return javaTypeClass == Byte[].class || javaTypeClass == Character[].class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Incubating
|
||||||
|
public void setExplicitJdbcTypeCode(Integer jdbcTypeCode) {
|
||||||
|
this.jdbcTypeCode = jdbcTypeCode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolved form of {@link BasicValue} as part of interpreting the
|
* Resolved form of {@link BasicValue} as part of interpreting the
|
||||||
* boot-time model into the run-time model
|
* boot-time model into the run-time model
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
import org.hibernate.type.descriptor.java.AbstractClassJavaType;
|
import org.hibernate.type.descriptor.java.AbstractClassJavaType;
|
||||||
import org.hibernate.type.descriptor.java.JavaType;
|
import org.hibernate.type.descriptor.java.JavaType;
|
||||||
import org.hibernate.type.descriptor.java.MutabilityPlan;
|
import org.hibernate.type.descriptor.java.MutabilityPlan;
|
||||||
|
import org.hibernate.type.descriptor.java.MutableMutabilityPlan;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
|
import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
|
||||||
|
@ -242,7 +243,9 @@ public abstract class AbstractStandardBasicType<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final boolean isDirty(Object old, Object current) {
|
protected final boolean isDirty(Object old, Object current) {
|
||||||
return !isSame( old, current );
|
// MutableMutabilityPlan.INSTANCE is a special plan for which we always have to assume the value is dirty,
|
||||||
|
// because we can't actually copy a value, but have no knowledge about the mutability of the java type
|
||||||
|
return getMutabilityPlan() == MutableMutabilityPlan.INSTANCE || !isSame( old, current );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.type.descriptor.java.spi;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
import org.hibernate.Incubating;
|
||||||
|
import org.hibernate.SharedSessionContract;
|
||||||
|
import org.hibernate.type.descriptor.WrapperOptions;
|
||||||
|
import org.hibernate.type.descriptor.java.AbstractJavaType;
|
||||||
|
import org.hibernate.type.descriptor.java.MutabilityPlan;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||||
|
import org.hibernate.type.format.FormatMapper;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Java type for {@link FormatMapper} based types i.e. {@link org.hibernate.type.SqlTypes#JSON}
|
||||||
|
* or {@link org.hibernate.type.SqlTypes#SQLXML} mapped types.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
public abstract class FormatMapperBasedJavaType<T> extends AbstractJavaType<T> implements MutabilityPlan<T> {
|
||||||
|
|
||||||
|
private final TypeConfiguration typeConfiguration;
|
||||||
|
|
||||||
|
public FormatMapperBasedJavaType(
|
||||||
|
Type type,
|
||||||
|
MutabilityPlan<T> mutabilityPlan,
|
||||||
|
TypeConfiguration typeConfiguration) {
|
||||||
|
super( type, mutabilityPlan );
|
||||||
|
this.typeConfiguration = typeConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract FormatMapper getFormatMapper(TypeConfiguration typeConfiguration);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) {
|
||||||
|
throw new JdbcTypeRecommendationException(
|
||||||
|
"Could not determine recommended JdbcType for Java type '" + getJavaType().getTypeName() + "'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(T value) {
|
||||||
|
return getFormatMapper( typeConfiguration ).toString(
|
||||||
|
value,
|
||||||
|
this,
|
||||||
|
typeConfiguration.getSessionFactory().getWrapperOptions()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T fromString(CharSequence string) {
|
||||||
|
return getFormatMapper( typeConfiguration ).fromString(
|
||||||
|
string,
|
||||||
|
this,
|
||||||
|
typeConfiguration.getSessionFactory().getWrapperOptions()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> X unwrap(T value, Class<X> type, WrapperOptions options) {
|
||||||
|
if ( type.isAssignableFrom( getJavaTypeClass() ) ) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return (X) value;
|
||||||
|
}
|
||||||
|
else if ( type == String.class ) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return (X) getFormatMapper( typeConfiguration ).toString( value, this, options );
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Unwrap strategy not known for this Java type : " + getJavaType().getTypeName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <X> T wrap(X value, WrapperOptions options) {
|
||||||
|
if ( getJavaTypeClass().isInstance( value ) ) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return (T) value;
|
||||||
|
}
|
||||||
|
else if ( value instanceof String ) {
|
||||||
|
return getFormatMapper( typeConfiguration ).fromString( (String) value, this, options );
|
||||||
|
}
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Wrap strategy not known for this Java type : " + getJavaType().getTypeName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MutabilityPlan<T> getMutabilityPlan() {
|
||||||
|
final MutabilityPlan<T> mutabilityPlan = super.getMutabilityPlan();
|
||||||
|
return mutabilityPlan == null ? this : mutabilityPlan;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMutable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T deepCopy(T value) {
|
||||||
|
return fromString( toString( value ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Serializable disassemble(T value, SharedSessionContract session) {
|
||||||
|
return toString( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T assemble(Serializable cached, SharedSessionContract session) {
|
||||||
|
return fromString( (CharSequence) cached );
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import java.io.Serializable;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
@ -110,6 +111,22 @@ public class JavaTypeRegistry implements JavaTypeBaseline.BaselineTarget, Serial
|
||||||
}
|
}
|
||||||
|
|
||||||
public <J> JavaType<J> resolveDescriptor(Type javaType) {
|
public <J> JavaType<J> resolveDescriptor(Type javaType) {
|
||||||
|
return resolveDescriptor( javaType, (elementJavaType, typeConfiguration) -> {
|
||||||
|
final MutabilityPlan<J> determinedPlan = RegistryHelper.INSTANCE.determineMutabilityPlan(
|
||||||
|
elementJavaType,
|
||||||
|
typeConfiguration
|
||||||
|
);
|
||||||
|
if ( determinedPlan != null ) {
|
||||||
|
return determinedPlan;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MutableMutabilityPlan.INSTANCE;
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public <J> JavaType<J> resolveDescriptor(
|
||||||
|
Type javaType,
|
||||||
|
BiFunction<Type, TypeConfiguration, MutabilityPlan<?>> mutabilityPlanCreator) {
|
||||||
return resolveDescriptor(
|
return resolveDescriptor(
|
||||||
javaType,
|
javaType,
|
||||||
() -> {
|
() -> {
|
||||||
|
@ -131,21 +148,10 @@ public class JavaTypeRegistry implements JavaTypeBaseline.BaselineTarget, Serial
|
||||||
elementTypeDescriptor = null;
|
elementTypeDescriptor = null;
|
||||||
}
|
}
|
||||||
if ( elementTypeDescriptor == null ) {
|
if ( elementTypeDescriptor == null ) {
|
||||||
|
//noinspection unchecked
|
||||||
elementTypeDescriptor = RegistryHelper.INSTANCE.createTypeDescriptor(
|
elementTypeDescriptor = RegistryHelper.INSTANCE.createTypeDescriptor(
|
||||||
elementJavaType,
|
elementJavaType,
|
||||||
() -> {
|
() -> (MutabilityPlan<J>) mutabilityPlanCreator.apply( elementJavaType, typeConfiguration ),
|
||||||
final MutabilityPlan<J> determinedPlan = RegistryHelper.INSTANCE.determineMutabilityPlan(
|
|
||||||
elementJavaType,
|
|
||||||
typeConfiguration
|
|
||||||
);
|
|
||||||
if ( determinedPlan != null ) {
|
|
||||||
return determinedPlan;
|
|
||||||
}
|
|
||||||
|
|
||||||
//noinspection unchecked
|
|
||||||
return (MutabilityPlan<J>) MutableMutabilityPlan.INSTANCE;
|
|
||||||
|
|
||||||
},
|
|
||||||
typeConfiguration
|
typeConfiguration
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
|
||||||
|
*/
|
||||||
|
package org.hibernate.type.descriptor.java.spi;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
import org.hibernate.Incubating;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
import org.hibernate.type.descriptor.java.MutabilityPlan;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||||
|
import org.hibernate.type.format.FormatMapper;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
@Incubating
|
||||||
|
public class JsonJavaType<T> extends FormatMapperBasedJavaType<T> {
|
||||||
|
|
||||||
|
public JsonJavaType(
|
||||||
|
Type type,
|
||||||
|
MutabilityPlan<T> mutabilityPlan,
|
||||||
|
TypeConfiguration typeConfiguration) {
|
||||||
|
super( type, mutabilityPlan, typeConfiguration );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FormatMapper getFormatMapper(TypeConfiguration typeConfiguration) {
|
||||||
|
return typeConfiguration.getSessionFactory().getFastSessionServices().getJsonFormatMapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) {
|
||||||
|
return context.getJdbcType( SqlTypes.JSON );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "JsonJavaType(" + getJavaType().getTypeName() + ")";
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,11 +64,7 @@ public class RegistryHelper {
|
||||||
return typeConfiguration.createMutabilityPlan( annotation.value() );
|
return typeConfiguration.createMutabilityPlan( annotation.value() );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( javaTypeClass.isEnum() ) {
|
if ( javaTypeClass.isEnum() || javaTypeClass.isPrimitive() || ReflectHelper.isRecord( javaTypeClass ) ) {
|
||||||
return ImmutableMutabilityPlan.instance();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( javaTypeClass.isPrimitive() ) {
|
|
||||||
return ImmutableMutabilityPlan.instance();
|
return ImmutableMutabilityPlan.instance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
|
||||||
|
*/
|
||||||
|
package org.hibernate.type.descriptor.java.spi;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
import org.hibernate.Incubating;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
import org.hibernate.type.descriptor.java.MutabilityPlan;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||||
|
import org.hibernate.type.format.FormatMapper;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
@Incubating
|
||||||
|
public class XmlJavaType<T> extends FormatMapperBasedJavaType<T> {
|
||||||
|
|
||||||
|
public XmlJavaType(
|
||||||
|
Type type,
|
||||||
|
MutabilityPlan<T> mutabilityPlan,
|
||||||
|
TypeConfiguration typeConfiguration) {
|
||||||
|
super( type, mutabilityPlan, typeConfiguration );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FormatMapper getFormatMapper(TypeConfiguration typeConfiguration) {
|
||||||
|
return typeConfiguration.getSessionFactory().getFastSessionServices().getXmlFormatMapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) {
|
||||||
|
return context.getJdbcType( SqlTypes.SQLXML );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "XmlJavaType(" + getJavaType().getTypeName() + ")";
|
||||||
|
}
|
||||||
|
}
|
|
@ -466,4 +466,11 @@ public class Aggregate {
|
||||||
}
|
}
|
||||||
return Objects.equals( mutableValue, that.mutableValue );
|
return Objects.equals( mutableValue, that.mutableValue );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = Objects.hash(theBoolean, theNumericBoolean, theStringBoolean, theString, theInteger, theInt, theDouble, theUrl, theClob, theDate, theTime, theTimestamp, theInstant, theUuid, gender, convertedGender, ordinalGender, theDuration, theLocalDateTime, theLocalDate, theLocalTime, theZonedDateTime, theOffsetDateTime, mutableValue);
|
||||||
|
result = 31 * result + Arrays.hashCode(theBinary);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -12,23 +12,24 @@ import org.hibernate.annotations.JdbcTypeCode;
|
||||||
import org.hibernate.testing.orm.junit.BaseSessionFactoryFunctionalTest;
|
import org.hibernate.testing.orm.junit.BaseSessionFactoryFunctionalTest;
|
||||||
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
|
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
|
||||||
import org.hibernate.testing.orm.junit.JiraKey;
|
import org.hibernate.testing.orm.junit.JiraKey;
|
||||||
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
|
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
|
||||||
import org.hibernate.type.SqlTypes;
|
import org.hibernate.type.SqlTypes;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
//@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonAggregate.class)
|
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsJsonAggregate.class)
|
||||||
public class JsonAggregateTest extends BaseSessionFactoryFunctionalTest {
|
public class AggregateTest extends BaseSessionFactoryFunctionalTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<?>[] getAnnotatedClasses() {
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
return new Class<?>[] {
|
return new Class<?>[] {
|
||||||
JsonHolder.class
|
JsonHolder.class, XmlHolder.class
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +38,7 @@ public class JsonAggregateTest extends BaseSessionFactoryFunctionalTest {
|
||||||
inTransaction(
|
inTransaction(
|
||||||
session -> {
|
session -> {
|
||||||
session.persist( new JsonHolder( 1L, Aggregate.createAggregate2() ) );
|
session.persist( new JsonHolder( 1L, Aggregate.createAggregate2() ) );
|
||||||
|
session.persist( new XmlHolder( 1L, Aggregate.createAggregate2() ) );
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -46,27 +48,41 @@ public class JsonAggregateTest extends BaseSessionFactoryFunctionalTest {
|
||||||
inTransaction(
|
inTransaction(
|
||||||
session -> {
|
session -> {
|
||||||
session.createMutationQuery( "delete from JsonHolder h" ).executeUpdate();
|
session.createMutationQuery( "delete from JsonHolder h" ).executeUpdate();
|
||||||
|
session.createMutationQuery( "delete from XmlHolder h" ).executeUpdate();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@JiraKey("HHH-17294")
|
@JiraKey("HHH-17294")
|
||||||
public void testDirtyChecking() {
|
public void testDirtyCheckingJsonAggregate() {
|
||||||
sessionFactoryScope().inTransaction(
|
sessionFactoryScope().inTransaction(
|
||||||
entityManager -> {
|
entityManager -> {
|
||||||
JsonHolder jsonHolder = entityManager.find( JsonHolder.class, 1L );
|
JsonHolder aggregateHolder = entityManager.find( JsonHolder.class, 1L );
|
||||||
assertEquals("String 'abc'", jsonHolder.getAggregate().getTheString());
|
Assertions.assertEquals("String 'abc'", aggregateHolder.getAggregate().getTheString());
|
||||||
jsonHolder.getAggregate().setTheString( "MyString" );
|
aggregateHolder.getAggregate().setTheString( "MyString" );
|
||||||
entityManager.flush();
|
entityManager.flush();
|
||||||
entityManager.clear();
|
entityManager.clear();
|
||||||
// Fails, when it should pass
|
Assertions.assertEquals( "MyString", entityManager.find( JsonHolder.class, 1L ).getAggregate().getTheString() );
|
||||||
assertEquals( "String 'MyString'", entityManager.find( JsonHolder.class, 1L ).getAggregate().getTheString() );
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@JiraKey("HHH-17294")
|
||||||
|
public void testDirtyCheckingXmlAggregate() {
|
||||||
|
sessionFactoryScope().inTransaction(
|
||||||
|
entityManager -> {
|
||||||
|
XmlHolder aggregateHolder = entityManager.find( XmlHolder.class, 1L );
|
||||||
|
Assertions.assertEquals("String 'abc'", aggregateHolder.getAggregate().getTheString());
|
||||||
|
aggregateHolder.getAggregate().setTheString( "MyString" );
|
||||||
|
entityManager.flush();
|
||||||
|
entityManager.clear();
|
||||||
|
Assertions.assertEquals( "MyString", entityManager.find( XmlHolder.class, 1L ).getAggregate().getTheString() );
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//tag::json-type-mapping-example[]
|
|
||||||
@Entity(name = "JsonHolder")
|
@Entity(name = "JsonHolder")
|
||||||
public static class JsonHolder {
|
public static class JsonHolder {
|
||||||
|
|
||||||
|
@ -75,9 +91,6 @@ public class JsonAggregateTest extends BaseSessionFactoryFunctionalTest {
|
||||||
@JdbcTypeCode(SqlTypes.JSON)
|
@JdbcTypeCode(SqlTypes.JSON)
|
||||||
private Aggregate aggregate;
|
private Aggregate aggregate;
|
||||||
|
|
||||||
//end::json-type-mapping-example[]
|
|
||||||
//Getters and setters are omitted for brevity
|
|
||||||
|
|
||||||
public JsonHolder() {
|
public JsonHolder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,10 +114,39 @@ public class JsonAggregateTest extends BaseSessionFactoryFunctionalTest {
|
||||||
public void setAggregate(Aggregate aggregate) {
|
public void setAggregate(Aggregate aggregate) {
|
||||||
this.aggregate = aggregate;
|
this.aggregate = aggregate;
|
||||||
}
|
}
|
||||||
|
|
||||||
//tag::json-type-mapping-example[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//end::json-type-mapping-example[]
|
@Entity(name = "XmlHolder")
|
||||||
|
public static class XmlHolder {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
@JdbcTypeCode(SqlTypes.SQLXML)
|
||||||
|
private Aggregate aggregate;
|
||||||
|
|
||||||
|
public XmlHolder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlHolder(Long id, Aggregate aggregate) {
|
||||||
|
this.id = id;
|
||||||
|
this.aggregate = aggregate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Aggregate getAggregate() {
|
||||||
|
return aggregate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAggregate(Aggregate aggregate) {
|
||||||
|
this.aggregate = aggregate;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue