HHH-10999 Add support for SQL array types mapped as Java arrays and collections
This commit is contained in:
parent
39d8fa0662
commit
45fc49314e
|
@ -396,6 +396,12 @@ Can also specify the name of the constant in `org.hibernate.type.SqlTypes` inste
|
|||
Global setting identifying the preferred JDBC type code for storing instant values.
|
||||
Can also specify the name of the constant in `org.hibernate.type.SqlTypes` instead.
|
||||
|
||||
`*hibernate.type.preferred_array_jdbc_type*` (e.g. `2003` for `java.sql.Types.ARRAY`)::
|
||||
Global setting identifying the preferred JDBC type code for storing basic array and collection values.
|
||||
Can also specify the name of the constant in `org.hibernate.type.SqlTypes` instead.
|
||||
+
|
||||
The default value is determined via `org.hibernate.dialect.Dialect.getPreferredSqlTypeCodeForArray()`.
|
||||
|
||||
==== Bean Validation options
|
||||
`*jakarta.persistence.validation.factory*` (e.g. `jakarta.validation.ValidationFactory` implementation)::
|
||||
Specify the `javax.validation.ValidationFactory` implementation to use for Bean Validation.
|
||||
|
|
|
@ -1374,6 +1374,42 @@ include::{sourcedir}/basic/XmlMappingTests.java[tags=basic-xml-example]
|
|||
----
|
||||
====
|
||||
|
||||
[[basic-mapping-array]]
|
||||
==== Basic array mapping
|
||||
|
||||
Basic arrays, other than `byte[]`/Byte[] and `char[]`/`Character[]`, map to the type code `SqlTypes.ARRAY` by default,
|
||||
which maps to the SQL standard `array` type if possible,
|
||||
as determined via the new methods `getArrayTypeName` and `supportsStandardArrays` of `org.hibernate.dialect.Dialect`.
|
||||
If SQL standard array types are not available, data will be modeled as `SqlTypes.JSON`, `SqlTypes.XML` or `SqlTypes.VARBINARY`,
|
||||
depending on the database support as determined via the new method `org.hibernate.dialect.Dialect.getPreferredSqlTypeCodeForArray`.
|
||||
|
||||
[[basic-array-example]]
|
||||
.Mapping basic arrays
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/basic/BasicArrayMappingTests.java[tags=basic-array-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[basic-mapping-collection]]
|
||||
==== Basic collection mapping
|
||||
|
||||
Basic collections (only subtypes of `Collection`), which are not annotated with `@ElementCollection`,
|
||||
map to the type code `SqlTypes.ARRAY` by default, which maps to the SQL standard `array` type if possible,
|
||||
as determined via the new methods `getArrayTypeName` and `supportsStandardArrays` of `org.hibernate.dialect.Dialect`.
|
||||
If SQL standard array types are not available, data will be modeled as `SqlTypes.JSON`, `SqlTypes.XML` or `SqlTypes.VARBINARY`,
|
||||
depending on the database support as determined via the new method `org.hibernate.dialect.Dialect.getPreferredSqlTypeCodeForArray`.
|
||||
|
||||
[[basic-collection-example]]
|
||||
.Mapping basic collections
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/basic/BasicCollectionMappingTests.java[tags=basic-collection-example]
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
[[basic-mapping-composition]]
|
||||
==== Compositional basic mapping
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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.userguide.mapping.basic;
|
||||
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
|
||||
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
/**
|
||||
* Tests for mapping basic arrays
|
||||
*/
|
||||
@DomainModel(annotatedClasses = BasicArrayMappingTests.EntityOfArrays.class)
|
||||
@SessionFactory
|
||||
public class BasicArrayMappingTests {
|
||||
|
||||
@Test
|
||||
public void testMappings(SessionFactoryScope scope) {
|
||||
// first, verify the type selections...
|
||||
final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getMappingMetamodel();
|
||||
final EntityPersister entityDescriptor = mappingMetamodel.findEntityDescriptor( EntityOfArrays.class);
|
||||
|
||||
{
|
||||
final BasicAttributeMapping attribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping("wrapper");
|
||||
assertThat( attribute.getJavaType().getJavaTypeClass(), equalTo( Short[].class));
|
||||
|
||||
final JdbcMapping jdbcMapping = attribute.getJdbcMapping();
|
||||
assertThat(jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo(Short[].class));
|
||||
}
|
||||
|
||||
{
|
||||
final BasicAttributeMapping attribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping("primitive");
|
||||
assertThat( attribute.getJavaType().getJavaTypeClass(), equalTo( short[].class));
|
||||
|
||||
final JdbcMapping jdbcMapping = attribute.getJdbcMapping();
|
||||
assertThat(jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo(short[].class));
|
||||
}
|
||||
|
||||
|
||||
// and try to use the mapping
|
||||
scope.inTransaction(
|
||||
(session) -> session.persist(new EntityOfArrays( 1, new Short[]{ (short) 3 }, new short[]{ (short) 5 }))
|
||||
);
|
||||
scope.inTransaction(
|
||||
(session) -> session.get( EntityOfArrays.class, 1)
|
||||
);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dropData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> session.createQuery("delete EntityOfArrays").executeUpdate()
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "EntityOfArrays")
|
||||
@Table(name = "EntityOfArrays")
|
||||
public static class EntityOfArrays {
|
||||
@Id
|
||||
Integer id;
|
||||
|
||||
//tag::basic-array-example[]
|
||||
Short[] wrapper;
|
||||
short[] primitive;
|
||||
//end::basic-array-example[]
|
||||
|
||||
public EntityOfArrays() {
|
||||
}
|
||||
|
||||
public EntityOfArrays(Integer id, Short[] wrapper, short[] primitive) {
|
||||
this.id = id;
|
||||
this.wrapper = wrapper;
|
||||
this.primitive = primitive;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.userguide.mapping.basic;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
|
||||
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
/**
|
||||
* Tests for mapping basic collections
|
||||
*/
|
||||
@DomainModel(annotatedClasses = BasicCollectionMappingTests.EntityOfCollections.class)
|
||||
@SessionFactory
|
||||
public class BasicCollectionMappingTests {
|
||||
|
||||
@Test
|
||||
public void testMappings(SessionFactoryScope scope) {
|
||||
// first, verify the type selections...
|
||||
final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory()
|
||||
.getRuntimeMetamodels()
|
||||
.getMappingMetamodel();
|
||||
final EntityPersister entityDescriptor = mappingMetamodel.findEntityDescriptor( EntityOfCollections.class);
|
||||
|
||||
{
|
||||
final BasicAttributeMapping attribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping("list");
|
||||
assertThat( attribute.getJavaType().getJavaTypeClass(), equalTo( List.class));
|
||||
|
||||
final JdbcMapping jdbcMapping = attribute.getJdbcMapping();
|
||||
assertThat(jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo(List.class));
|
||||
}
|
||||
|
||||
{
|
||||
final BasicAttributeMapping attribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping("sortedSet");
|
||||
assertThat( attribute.getJavaType().getJavaTypeClass(), equalTo( SortedSet.class));
|
||||
|
||||
final JdbcMapping jdbcMapping = attribute.getJdbcMapping();
|
||||
assertThat(jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo(SortedSet.class));
|
||||
}
|
||||
|
||||
|
||||
// and try to use the mapping
|
||||
scope.inTransaction(
|
||||
(session) -> session.persist(
|
||||
new EntityOfCollections(
|
||||
1,
|
||||
List.of( (short) 3 ),
|
||||
new TreeSet<>( Set.of( (short) 5 ) )
|
||||
)
|
||||
)
|
||||
);
|
||||
scope.inTransaction(
|
||||
(session) -> session.get( EntityOfCollections.class, 1)
|
||||
);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dropData(SessionFactoryScope scope) {
|
||||
scope.inTransaction(
|
||||
(session) -> session.createQuery("delete EntityOfCollections").executeUpdate()
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "EntityOfCollections")
|
||||
@Table(name = "EntityOfCollections")
|
||||
public static class EntityOfCollections {
|
||||
@Id
|
||||
Integer id;
|
||||
|
||||
//tag::basic-collection-example[]
|
||||
List<Short> list;
|
||||
SortedSet<Short> sortedSet;
|
||||
//end::basic-collection-example[]
|
||||
|
||||
public EntityOfCollections() {
|
||||
}
|
||||
|
||||
public EntityOfCollections(Integer id, List<Short> list, SortedSet<Short> sortedSet) {
|
||||
this.id = id;
|
||||
this.list = list;
|
||||
this.sortedSet = sortedSet;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -218,6 +218,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
private final int preferredSqlTypeCodeForDuration;
|
||||
private final int preferredSqlTypeCodeForUuid;
|
||||
private final int preferredSqlTypeCodeForInstant;
|
||||
private final int preferredSqlTypeCodeForArray;
|
||||
private final TimeZoneStorageStrategy defaultTimeZoneStorageStrategy;
|
||||
|
||||
// Caching
|
||||
|
@ -428,6 +429,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
this.preferredSqlTypeCodeForDuration = ConfigurationHelper.getPreferredSqlTypeCodeForDuration( serviceRegistry );
|
||||
this.preferredSqlTypeCodeForUuid = ConfigurationHelper.getPreferredSqlTypeCodeForUuid( serviceRegistry );
|
||||
this.preferredSqlTypeCodeForInstant = ConfigurationHelper.getPreferredSqlTypeCodeForInstant( serviceRegistry );
|
||||
this.preferredSqlTypeCodeForArray = ConfigurationHelper.getPreferredSqlTypeCodeForArray( serviceRegistry );
|
||||
this.defaultTimeZoneStorageStrategy = context.getMetadataBuildingOptions().getDefaultTimeZoneStorage();
|
||||
|
||||
final RegionFactory regionFactory = serviceRegistry.getService( RegionFactory.class );
|
||||
|
@ -1242,6 +1244,11 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
|
|||
return preferredSqlTypeCodeForInstant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPreferredSqlTypeCodeForArray() {
|
||||
return preferredSqlTypeCodeForArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() {
|
||||
return defaultTimeZoneStorageStrategy;
|
||||
|
|
|
@ -207,7 +207,7 @@ public class TypeDefinition implements Serializable {
|
|||
|
||||
@Override
|
||||
public BasicValueConverter getValueConverter() {
|
||||
return null;
|
||||
return resolvedBasicType.getValueConverter();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -259,7 +259,7 @@ public class TypeDefinition implements Serializable {
|
|||
|
||||
@Override
|
||||
public BasicValueConverter getValueConverter() {
|
||||
return null;
|
||||
return resolved.getValueConverter();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -8,23 +8,24 @@ package org.hibernate.boot.model.process.internal;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.mapping.BasicValue;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.Selectable;
|
||||
import org.hibernate.mapping.Table;
|
||||
import org.hibernate.metamodel.model.convert.internal.NamedEnumValueConverter;
|
||||
import org.hibernate.metamodel.model.convert.internal.OrdinalEnumValueConverter;
|
||||
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||
import org.hibernate.type.AdjustableBasicType;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.CustomType;
|
||||
import org.hibernate.type.SerializableType;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||
import org.hibernate.type.descriptor.java.BasicJavaType;
|
||||
import org.hibernate.type.descriptor.java.EnumJavaType;
|
||||
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
|
||||
|
@ -52,6 +53,7 @@ public class InferredBasicValueResolver {
|
|||
Selectable selectable,
|
||||
String ownerName,
|
||||
String propertyName,
|
||||
Dialect dialect,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
final JavaType<T> reflectedJtd = reflectedJtdResolver.get();
|
||||
|
||||
|
@ -64,8 +66,26 @@ public class InferredBasicValueResolver {
|
|||
|
||||
if ( explicitJavaType != null ) {
|
||||
// we have an explicit JavaType
|
||||
|
||||
if ( explicitJdbcType != null ) {
|
||||
if ( explicitJavaType instanceof EnumJavaType ) {
|
||||
return fromEnum(
|
||||
(EnumJavaType) explicitJavaType,
|
||||
null,
|
||||
explicitJdbcType,
|
||||
stdIndicators,
|
||||
typeConfiguration
|
||||
);
|
||||
}
|
||||
else if ( explicitJavaType instanceof TemporalJavaType ) {
|
||||
return fromTemporal(
|
||||
(TemporalJavaType<T>) explicitJavaType,
|
||||
null,
|
||||
explicitJdbcType,
|
||||
resolvedJavaType,
|
||||
stdIndicators,
|
||||
typeConfiguration
|
||||
);
|
||||
}
|
||||
else if ( explicitJdbcType != null ) {
|
||||
// we also have an explicit JdbcType
|
||||
|
||||
jdbcMapping = typeConfiguration.getBasicTypeRegistry().resolve(
|
||||
|
@ -75,41 +95,12 @@ public class InferredBasicValueResolver {
|
|||
legacyType = jdbcMapping;
|
||||
}
|
||||
else {
|
||||
if ( explicitJavaType instanceof TemporalJavaType ) {
|
||||
return fromTemporal(
|
||||
(TemporalJavaType<T>) explicitJavaType,
|
||||
null,
|
||||
null,
|
||||
resolvedJavaType,
|
||||
stdIndicators,
|
||||
typeConfiguration
|
||||
);
|
||||
}
|
||||
// we need to infer the JdbcType and use that to build the value-mapping
|
||||
final JdbcType inferredJdbcType = explicitJavaType.getRecommendedJdbcType( stdIndicators );
|
||||
if ( inferredJdbcType instanceof ObjectJdbcType ) {
|
||||
// have a "fallback" JDBC type... see if we can decide a better choice
|
||||
|
||||
if ( explicitJavaType instanceof EnumJavaType ) {
|
||||
return fromEnum(
|
||||
(EnumJavaType) reflectedJtd,
|
||||
explicitJavaType,
|
||||
null,
|
||||
stdIndicators,
|
||||
typeConfiguration
|
||||
);
|
||||
}
|
||||
else if ( explicitJavaType instanceof TemporalJavaType ) {
|
||||
return fromTemporal(
|
||||
(TemporalJavaType<T>) reflectedJtd,
|
||||
explicitJavaType,
|
||||
null,
|
||||
resolvedJavaType,
|
||||
stdIndicators,
|
||||
typeConfiguration
|
||||
);
|
||||
}
|
||||
else if ( explicitJavaType instanceof SerializableJavaType
|
||||
if ( explicitJavaType instanceof SerializableJavaType
|
||||
|| explicitJavaType.getJavaType() instanceof Serializable ) {
|
||||
legacyType = new SerializableType( explicitJavaType );
|
||||
jdbcMapping = legacyType;
|
||||
|
@ -133,7 +124,28 @@ public class InferredBasicValueResolver {
|
|||
}
|
||||
else if ( reflectedJtd != null ) {
|
||||
// we were able to determine the "reflected java-type"
|
||||
if ( explicitJdbcType != null ) {
|
||||
// Use JTD if we know it to apply any specialized resolutions
|
||||
|
||||
if ( reflectedJtd instanceof EnumJavaType ) {
|
||||
return fromEnum(
|
||||
(EnumJavaType) reflectedJtd,
|
||||
null,
|
||||
explicitJdbcType,
|
||||
stdIndicators,
|
||||
typeConfiguration
|
||||
);
|
||||
}
|
||||
else if ( reflectedJtd instanceof TemporalJavaType ) {
|
||||
return fromTemporal(
|
||||
(TemporalJavaType<T>) reflectedJtd,
|
||||
null,
|
||||
explicitJdbcType,
|
||||
resolvedJavaType,
|
||||
stdIndicators,
|
||||
typeConfiguration
|
||||
);
|
||||
}
|
||||
else if ( explicitJdbcType != null ) {
|
||||
// we also have an explicit JdbcType
|
||||
|
||||
jdbcMapping = typeConfiguration.getBasicTypeRegistry().resolve(
|
||||
|
@ -144,32 +156,59 @@ public class InferredBasicValueResolver {
|
|||
legacyType = jdbcMapping;
|
||||
}
|
||||
else {
|
||||
// Use JTD if we know it to apply any specialized resolutions
|
||||
|
||||
if ( reflectedJtd instanceof EnumJavaType ) {
|
||||
return fromEnum(
|
||||
(EnumJavaType) reflectedJtd,
|
||||
// see if there is a registered BasicType for this JavaType and, if so, use it.
|
||||
// this mimics the legacy handling
|
||||
final BasicType registeredType;
|
||||
if ( reflectedJtd instanceof BasicPluralJavaType<?> ) {
|
||||
final BasicPluralJavaType<?> containerJtd = (BasicPluralJavaType<?>) reflectedJtd;
|
||||
final JavaType<?> elementJtd = containerJtd.getElementJavaType();
|
||||
final BasicType registeredElementType;
|
||||
if ( elementJtd instanceof EnumJavaType ) {
|
||||
final InferredBasicValueResolution resolution = InferredBasicValueResolver.fromEnum(
|
||||
(EnumJavaType) elementJtd,
|
||||
null,
|
||||
null,
|
||||
stdIndicators,
|
||||
typeConfiguration
|
||||
);
|
||||
registeredElementType = resolution.getLegacyResolvedBasicType();
|
||||
}
|
||||
else if ( reflectedJtd instanceof TemporalJavaType ) {
|
||||
return fromTemporal(
|
||||
(TemporalJavaType<T>) reflectedJtd,
|
||||
else if ( elementJtd instanceof TemporalJavaType ) {
|
||||
final InferredBasicValueResolution resolution = InferredBasicValueResolver.fromTemporal(
|
||||
(TemporalJavaType<T>) elementJtd,
|
||||
null,
|
||||
null,
|
||||
resolvedJavaType,
|
||||
stdIndicators,
|
||||
typeConfiguration
|
||||
);
|
||||
registeredElementType = resolution.getLegacyResolvedBasicType();
|
||||
}
|
||||
else {
|
||||
// see if there is a registered BasicType for this JavaType and, if so, use it.
|
||||
// this mimics the legacy handling
|
||||
final BasicType<T> registeredType = typeConfiguration.getBasicTypeRegistry()
|
||||
registeredElementType = typeConfiguration.getBasicTypeRegistry()
|
||||
.getRegisteredType( elementJtd.getJavaType() );
|
||||
}
|
||||
final ColumnTypeInformation columnTypeInformation;
|
||||
if ( selectable instanceof ColumnTypeInformation ) {
|
||||
columnTypeInformation = (ColumnTypeInformation) selectable;
|
||||
}
|
||||
else {
|
||||
columnTypeInformation = null;
|
||||
}
|
||||
registeredType = registeredElementType == null ? null : containerJtd.resolveType(
|
||||
typeConfiguration,
|
||||
dialect,
|
||||
registeredElementType,
|
||||
columnTypeInformation
|
||||
);
|
||||
if ( registeredType != null ) {
|
||||
typeConfiguration.getBasicTypeRegistry().register( registeredType );
|
||||
}
|
||||
}
|
||||
else {
|
||||
registeredType = typeConfiguration.getBasicTypeRegistry()
|
||||
.getRegisteredType( reflectedJtd.getJavaType() );
|
||||
}
|
||||
|
||||
if ( registeredType != null ) {
|
||||
// so here is the legacy resolution
|
||||
|
@ -200,7 +239,6 @@ public class InferredBasicValueResolver {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( explicitJdbcType != null ) {
|
||||
// we have an explicit STD, but no JTD - infer JTD
|
||||
|
@ -271,40 +309,40 @@ public class InferredBasicValueResolver {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an inference-based resolution
|
||||
*/
|
||||
public static <T> BasicValue.Resolution<T> from(
|
||||
Function<TypeConfiguration, BasicJavaType> explicitJavaTypeAccess,
|
||||
Function<TypeConfiguration, JdbcType> explicitSqlTypeAccess,
|
||||
Type resolvedJavaType,
|
||||
Supplier<JavaType<T>> reflectedJtdResolver,
|
||||
JdbcTypeIndicators stdIndicators,
|
||||
Table table,
|
||||
Selectable selectable,
|
||||
String ownerName,
|
||||
String propertyName,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
|
||||
final BasicJavaType<T> explicitJavaType = explicitJavaTypeAccess != null
|
||||
? explicitJavaTypeAccess.apply( typeConfiguration )
|
||||
: null;
|
||||
final JdbcType explicitJdbcType = explicitSqlTypeAccess
|
||||
!= null ? explicitSqlTypeAccess.apply( typeConfiguration )
|
||||
: null;
|
||||
return InferredBasicValueResolver.from(
|
||||
explicitJavaType,
|
||||
explicitJdbcType,
|
||||
resolvedJavaType,
|
||||
reflectedJtdResolver,
|
||||
stdIndicators,
|
||||
table,
|
||||
selectable,
|
||||
ownerName,
|
||||
propertyName,
|
||||
typeConfiguration
|
||||
);
|
||||
}
|
||||
// /**
|
||||
// * Create an inference-based resolution
|
||||
// */
|
||||
// public static <T> BasicValue.Resolution<T> from(
|
||||
// Function<TypeConfiguration, BasicJavaType> explicitJavaTypeAccess,
|
||||
// Function<TypeConfiguration, JdbcType> explicitSqlTypeAccess,
|
||||
// Type resolvedJavaType,
|
||||
// Supplier<JavaType<T>> reflectedJtdResolver,
|
||||
// JdbcTypeIndicators stdIndicators,
|
||||
// Table table,
|
||||
// Selectable selectable,
|
||||
// String ownerName,
|
||||
// String propertyName,
|
||||
// TypeConfiguration typeConfiguration) {
|
||||
//
|
||||
// final BasicJavaType<T> explicitJavaType = explicitJavaTypeAccess != null
|
||||
// ? explicitJavaTypeAccess.apply( typeConfiguration )
|
||||
// : null;
|
||||
// final JdbcType explicitJdbcType = explicitSqlTypeAccess
|
||||
// != null ? explicitSqlTypeAccess.apply( typeConfiguration )
|
||||
// : null;
|
||||
// return InferredBasicValueResolver.from(
|
||||
// explicitJavaType,
|
||||
// explicitJdbcType,
|
||||
// resolvedJavaType,
|
||||
// reflectedJtdResolver,
|
||||
// stdIndicators,
|
||||
// table,
|
||||
// selectable,
|
||||
// ownerName,
|
||||
// propertyName,
|
||||
// typeConfiguration
|
||||
// );
|
||||
// }
|
||||
|
||||
public static <T> BasicType<T> resolveSqlTypeIndicators(
|
||||
JdbcTypeIndicators stdIndicators,
|
||||
|
@ -345,6 +383,7 @@ public class InferredBasicValueResolver {
|
|||
enumJavaType,
|
||||
explicitJavaType,
|
||||
explicitJdbcType,
|
||||
stdIndicators,
|
||||
typeConfiguration
|
||||
);
|
||||
}
|
||||
|
@ -354,12 +393,13 @@ public class InferredBasicValueResolver {
|
|||
}
|
||||
}
|
||||
|
||||
private static <E extends Enum<E>> InferredBasicValueResolution<E, Integer> ordinalEnumValueResolution(
|
||||
private static <E extends Enum<E>> InferredBasicValueResolution<E, Number> ordinalEnumValueResolution(
|
||||
EnumJavaType<E> enumJavaType,
|
||||
BasicJavaType<?> explicitJavaType,
|
||||
JdbcType explicitJdbcType,
|
||||
JdbcTypeIndicators stdIndicators,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
final JavaType<Integer> relationalJtd;
|
||||
final JavaType<Number> relationalJtd;
|
||||
if ( explicitJavaType != null ) {
|
||||
if ( ! Integer.class.isAssignableFrom( explicitJavaType.getJavaTypeClass() ) ) {
|
||||
throw new MappingException(
|
||||
|
@ -369,7 +409,7 @@ public class InferredBasicValueResolver {
|
|||
);
|
||||
}
|
||||
//noinspection unchecked
|
||||
relationalJtd = (BasicJavaType<Integer>) explicitJavaType;
|
||||
relationalJtd = (BasicJavaType<Number>) explicitJavaType;
|
||||
}
|
||||
else {
|
||||
relationalJtd = typeConfiguration.getJavaTypeRegistry().getDescriptor( Integer.class );
|
||||
|
@ -377,7 +417,7 @@ public class InferredBasicValueResolver {
|
|||
|
||||
final JdbcType jdbcType = explicitJdbcType != null
|
||||
? explicitJdbcType
|
||||
: typeConfiguration.getJdbcTypeRegistry().getDescriptor( SqlTypes.TINYINT );
|
||||
: typeConfiguration.getJdbcTypeRegistry().getDescriptor( stdIndicators.getPreferredSqlTypeCodeForEnum() );
|
||||
|
||||
final OrdinalEnumValueConverter<E> valueConverter = new OrdinalEnumValueConverter<>(
|
||||
enumJavaType,
|
||||
|
@ -386,7 +426,7 @@ public class InferredBasicValueResolver {
|
|||
);
|
||||
|
||||
return new InferredBasicValueResolution<>(
|
||||
typeConfiguration.getBasicTypeRegistry().resolve(relationalJtd, jdbcType),
|
||||
typeConfiguration.getBasicTypeRegistry().resolve( relationalJtd, jdbcType ),
|
||||
enumJavaType,
|
||||
relationalJtd,
|
||||
jdbcType,
|
||||
|
|
|
@ -58,6 +58,9 @@ public class UserTypeResolution implements BasicValue.Resolution {
|
|||
|
||||
@Override
|
||||
public BasicValueConverter getValueConverter() {
|
||||
// Even though we could expose the value converter of the user type here,
|
||||
// we can not do it, as the conversion is done behind the scenes in the binder/extractor,
|
||||
// whereas the converter returned here would, AFAIU, be used to construct a converted attribute mapping
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.sql.SQLException;
|
|||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
|
||||
import org.hibernate.type.ConvertedBasicType;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
@ -22,7 +23,7 @@ import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
|||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ValueConverterTypeAdapter<J> extends AbstractSingleColumnStandardBasicType<J> {
|
||||
public class ValueConverterTypeAdapter<J> extends AbstractSingleColumnStandardBasicType<J> implements ConvertedBasicType<J> {
|
||||
private final String description;
|
||||
private final BasicValueConverter<J, Object> converter;
|
||||
|
||||
|
@ -75,6 +76,11 @@ public class ValueConverterTypeAdapter<J> extends AbstractSingleColumnStandardBa
|
|||
return ( (JavaType<Object>) converter.getDomainJavaType() ).areEqual( one, another );
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicValueConverter getValueConverter() {
|
||||
return converter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return description;
|
||||
|
|
|
@ -134,7 +134,7 @@ public class VersionResolution<E> implements BasicValue.Resolution<E> {
|
|||
|
||||
@Override
|
||||
public BasicValueConverter<E,E> getValueConverter() {
|
||||
return null;
|
||||
return legacyType.getValueConverter();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -401,6 +401,15 @@ public class MetadataBuildingProcess {
|
|||
}
|
||||
jdbcTypeRegistry.addDescriptorIfAbsent( JsonJdbcType.INSTANCE );
|
||||
jdbcTypeRegistry.addDescriptorIfAbsent( XmlAsStringJdbcType.INSTANCE );
|
||||
|
||||
final int preferredSqlTypeCodeForArray = ConfigurationHelper.getPreferredSqlTypeCodeForArray( bootstrapContext.getServiceRegistry() );
|
||||
if ( preferredSqlTypeCodeForArray != SqlTypes.ARRAY ) {
|
||||
jdbcTypeRegistry.addDescriptor( SqlTypes.ARRAY, jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForArray ) );
|
||||
}
|
||||
else if ( jdbcTypeRegistry.findDescriptor( SqlTypes.ARRAY ) == null ) {
|
||||
// Fallback to VARBINARY
|
||||
jdbcTypeRegistry.addDescriptor( SqlTypes.ARRAY, jdbcTypeRegistry.getDescriptor( SqlTypes.VARBINARY ) );
|
||||
}
|
||||
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INET, SqlTypes.VARBINARY );
|
||||
final int preferredSqlTypeCodeForDuration = ConfigurationHelper.getPreferredSqlTypeCodeForDuration( bootstrapContext.getServiceRegistry() );
|
||||
if ( preferredSqlTypeCodeForDuration != SqlTypes.INTERVAL_SECOND ) {
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.function.Supplier;
|
|||
|
||||
import org.hibernate.CustomEntityDirtinessStrategy;
|
||||
import org.hibernate.EntityNameResolver;
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.Interceptor;
|
||||
import org.hibernate.SessionFactoryObserver;
|
||||
import org.hibernate.TimeZoneStorageStrategy;
|
||||
|
@ -452,6 +453,11 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp
|
|||
return delegate.getPreferredSqlTypeCodeForInstant();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPreferredSqlTypeCodeForArray() {
|
||||
return delegate.getPreferredSqlTypeCodeForArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() {
|
||||
return delegate.getDefaultTimeZoneStorageStrategy();
|
||||
|
|
|
@ -72,6 +72,11 @@ public interface MetadataBuildingContext {
|
|||
return ConfigurationHelper.getPreferredSqlTypeCodeForInstant( getBootstrapContext().getServiceRegistry() );
|
||||
}
|
||||
|
||||
@Incubating
|
||||
default int getPreferredSqlTypeCodeForArray() {
|
||||
return ConfigurationHelper.getPreferredSqlTypeCodeForArray( getBootstrapContext().getServiceRegistry() );
|
||||
}
|
||||
|
||||
TypeDefinitionRegistry getTypeDefinitionRegistry();
|
||||
|
||||
/**
|
||||
|
|
|
@ -311,6 +311,9 @@ public interface SessionFactoryOptions extends QueryEngineOptions {
|
|||
@Incubating
|
||||
int getPreferredSqlTypeCodeForInstant();
|
||||
|
||||
@Incubating
|
||||
int getPreferredSqlTypeCodeForArray();
|
||||
|
||||
TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy();
|
||||
|
||||
FormatMapper getJsonFormatMapper();
|
||||
|
|
|
@ -2556,6 +2556,23 @@ public interface AvailableSettings {
|
|||
@Incubating
|
||||
String PREFERRED_INSTANT_JDBC_TYPE = "hibernate.type.preferred_instant_jdbc_type";
|
||||
|
||||
/**
|
||||
* Specifies the preferred JDBC type for storing basic array and collection values. When no
|
||||
* type is explicitly specified, a sensible
|
||||
* {@link org.hibernate.dialect.Dialect#getPreferredSqlTypeCodeForArray()
|
||||
* dialect-specific default type code} is used.
|
||||
*
|
||||
* Can be overridden locally using {@link org.hibernate.annotations.JdbcType},
|
||||
* {@link org.hibernate.annotations.JdbcTypeCode} and friends
|
||||
*
|
||||
* Can also specify the name of the {@link org.hibernate.type.SqlTypes} constant field. E.g.
|
||||
* {@code hibernate.type.preferred_array_jdbc_type=VARBINARY}
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
@Incubating
|
||||
String PREFERRED_ARRAY_JDBC_TYPE = "hibernate.type.preferred_array_jdbc_type";
|
||||
|
||||
/**
|
||||
* Specifies a {@link org.hibernate.type.FormatMapper} used for JSON serialization
|
||||
* and deserialization, either:
|
||||
|
|
|
@ -231,6 +231,11 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
|||
return buildingContext.getPreferredSqlTypeCodeForInstant();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPreferredSqlTypeCodeForArray() {
|
||||
return buildingContext.getPreferredSqlTypeCodeForArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNationalized() {
|
||||
return isNationalized;
|
||||
|
@ -719,10 +724,10 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
|||
this.temporalPrecision = null;
|
||||
}
|
||||
|
||||
if ( javaTypeClass.isEnum() ) {
|
||||
final Enumerated enumeratedAnn = attributeDescriptor.getAnnotation( Enumerated.class );
|
||||
if ( enumeratedAnn != null ) {
|
||||
this.enumType = enumeratedAnn.value();
|
||||
if ( javaTypeClass.isEnum() || javaTypeClass.isArray() && javaTypeClass.getComponentType().isEnum() ) {
|
||||
if ( this.enumType == null ) {
|
||||
throw new IllegalStateException(
|
||||
"jakarta.persistence.EnumType was null on @jakarta.persistence.Enumerated " +
|
||||
|
@ -731,9 +736,7 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( attributeDescriptor.isAnnotationPresent( Enumerated.class ) ) {
|
||||
throw new AnnotationException(
|
||||
String.format(
|
||||
"Attribute [%s.%s] was annotated as enumerated, but its java type is not an enum [%s]",
|
||||
|
@ -743,6 +746,8 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
|||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.enumType = null;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,9 @@ public class StandardSortedMapSemantics<K,V> extends AbstractMapSemantics<Sorted
|
|||
public TreeMap<K,V> instantiateRaw(
|
||||
int anticipatedSize,
|
||||
CollectionPersister collectionDescriptor) {
|
||||
return new TreeMap<K,V>( (Comparator) collectionDescriptor.getSortingComparator() );
|
||||
return new TreeMap<K,V>(
|
||||
collectionDescriptor == null ? null : (Comparator) collectionDescriptor.getSortingComparator()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -44,7 +44,9 @@ public class StandardSortedSetSemantics<E> extends AbstractSetSemantics<SortedSe
|
|||
public SortedSet<E> instantiateRaw(
|
||||
int anticipatedSize,
|
||||
CollectionPersister collectionDescriptor) {
|
||||
return new TreeSet<E>( (Comparator) collectionDescriptor.getSortingComparator() );
|
||||
return new TreeSet<E>(
|
||||
collectionDescriptor == null ? null : (Comparator) collectionDescriptor.getSortingComparator()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -309,6 +309,11 @@ public class CockroachDialect extends Dialect {
|
|||
return " cascade";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsDistinctFromPredicate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsIfExistsBeforeTableName() {
|
||||
return true;
|
||||
|
@ -427,6 +432,11 @@ public class CockroachDialect extends Dialect {
|
|||
return 63;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsStandardArrays() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendDateTimeLiteral(
|
||||
SqlAppender appender,
|
||||
|
|
|
@ -191,6 +191,11 @@ public class DB2Dialect extends Dialect {
|
|||
return 31;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsDistinctFromPredicate() {
|
||||
return getDB2Version().isSameOrAfter( 11, 1 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
||||
super.initializeFunctionRegistry( queryEngine );
|
||||
|
|
|
@ -73,6 +73,11 @@ public class DB2iDialect extends DB2Dialect {
|
|||
: super.createUniqueDelegate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsDistinctFromPredicate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* No support for sequences.
|
||||
*/
|
||||
|
|
|
@ -78,6 +78,12 @@ public class DB2zDialect extends DB2Dialect {
|
|||
return DB2_LUW_VERSION9;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsDistinctFromPredicate() {
|
||||
// Supported at least since DB2 z/OS 9.0
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeZoneSupport getTimeZoneSupport() {
|
||||
return getVersion().isAfter(10) ? TimeZoneSupport.NATIVE : TimeZoneSupport.NONE;
|
||||
|
|
|
@ -146,11 +146,14 @@ import org.hibernate.type.BasicType;
|
|||
import org.hibernate.type.BasicTypeRegistry;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.InstantAsTimestampJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.InstantAsTimestampWithTimeZoneJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.LongNVarcharJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.NCharJdbcType;
|
||||
|
@ -472,13 +475,57 @@ public abstract class Dialect implements ConversionContext {
|
|||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the {@link SqlTypes} type code for the given column type name as reported by the database,
|
||||
* or <code>null</code> if it can't be resolved.
|
||||
*/
|
||||
protected Integer resolveSqlTypeCode(String columnTypeName, TypeConfiguration typeConfiguration) {
|
||||
final int parenthesisIndex = columnTypeName.lastIndexOf( '(' );
|
||||
final String baseTypeName;
|
||||
if ( parenthesisIndex == -1 ) {
|
||||
baseTypeName = columnTypeName;
|
||||
}
|
||||
else {
|
||||
baseTypeName = columnTypeName.substring( 0, parenthesisIndex ).trim();
|
||||
}
|
||||
return resolveSqlTypeCode( columnTypeName, baseTypeName, typeConfiguration );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the {@link SqlTypes} type code for the given column type name as reported by the database
|
||||
* and the base type name (i.e. without precision/length and scale), or <code>null</code> if it can't be resolved.
|
||||
*/
|
||||
protected Integer resolveSqlTypeCode(String typeName, String baseTypeName, TypeConfiguration typeConfiguration) {
|
||||
return typeConfiguration.getDdlTypeRegistry().getSqlTypeCode( baseTypeName );
|
||||
}
|
||||
|
||||
public JdbcType resolveSqlTypeDescriptor(
|
||||
String columnTypeName,
|
||||
int jdbcTypeCode,
|
||||
int precision,
|
||||
int scale,
|
||||
JdbcTypeRegistry jdbcTypeRegistry) {
|
||||
return jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
|
||||
final JdbcType jdbcType = jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
|
||||
if ( jdbcTypeCode == Types.ARRAY && jdbcType instanceof ArrayJdbcType ) {
|
||||
// Special handling for array types, because we need the proper element/component type
|
||||
// To determine the element JdbcType, we pass the database reported type to #resolveSqlTypeCode
|
||||
final int arraySuffixIndex = columnTypeName.toLowerCase( Locale.ROOT ).indexOf( " array" );
|
||||
if ( arraySuffixIndex != -1 ) {
|
||||
final String componentTypeName = columnTypeName.substring( 0, arraySuffixIndex );
|
||||
final Integer sqlTypeCode = resolveSqlTypeCode( componentTypeName, jdbcTypeRegistry.getTypeConfiguration() );
|
||||
if ( sqlTypeCode != null ) {
|
||||
return ( (ArrayJdbcType) jdbcType ).resolveType(
|
||||
jdbcTypeRegistry.getTypeConfiguration(),
|
||||
jdbcTypeRegistry.getTypeConfiguration().getServiceRegistry()
|
||||
.getService( JdbcServices.class )
|
||||
.getDialect(),
|
||||
jdbcTypeRegistry.getDescriptor( sqlTypeCode ),
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return jdbcType;
|
||||
}
|
||||
|
||||
public int resolveSqlTypeLength(
|
||||
|
@ -1170,8 +1217,8 @@ public abstract class Dialect implements ConversionContext {
|
|||
* {@link Types#LONGVARBINARY LONGVARBINARY} as the same type, since
|
||||
* Hibernate doesn't really differentiate these types.
|
||||
*
|
||||
* @param typeCode1 the first JDBC type code
|
||||
* @param typeCode2 the second JDBC type code
|
||||
* @param column1 the first column type info
|
||||
* @param column2 the second column type info
|
||||
*
|
||||
* @return {@code true} if the two type codes are equivalent
|
||||
*/
|
||||
|
@ -1255,6 +1302,10 @@ public abstract class Dialect implements ConversionContext {
|
|||
else {
|
||||
typeContributions.contributeJdbcType( InstantAsTimestampJdbcType.INSTANCE );
|
||||
}
|
||||
|
||||
if ( supportsStandardArrays() ) {
|
||||
typeContributions.contributeJdbcType( ArrayJdbcType.INSTANCE );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3239,6 +3290,84 @@ public abstract class Dialect implements ConversionContext {
|
|||
return NationalizationSupport.EXPLICIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Database has native support for SQL standard arrays which can be referred to through base type name.
|
||||
* Oracle for example doesn't support this, but instead has support for named arrays.
|
||||
*
|
||||
* @return boolean
|
||||
* @since 6.1
|
||||
*/
|
||||
public boolean supportsStandardArrays() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The SQL type name for the array of the given type name.
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
public String getArrayTypeName(String elementTypeName) {
|
||||
if ( supportsStandardArrays() ) {
|
||||
return elementTypeName + " array";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void appendArrayLiteral(
|
||||
SqlAppender appender,
|
||||
Object[] literal,
|
||||
JdbcLiteralFormatter<Object> elementFormatter,
|
||||
WrapperOptions wrapperOptions) {
|
||||
if ( !supportsStandardArrays() ) {
|
||||
throw new UnsupportedOperationException( getClass().getName() + " does not support array literals" );
|
||||
}
|
||||
appender.appendSql( "ARRAY[" );
|
||||
if ( literal.length != 0 ) {
|
||||
if ( literal[0] == null ) {
|
||||
appender.appendSql( "null" );
|
||||
}
|
||||
else {
|
||||
elementFormatter.appendJdbcLiteral( appender, literal[0], this, wrapperOptions );
|
||||
}
|
||||
for ( int i = 1; i < literal.length; i++ ) {
|
||||
appender.appendSql( ',' );
|
||||
if ( literal[i] == null ) {
|
||||
appender.appendSql( "null" );
|
||||
}
|
||||
else {
|
||||
elementFormatter.appendJdbcLiteral( appender, literal[i], this, wrapperOptions );
|
||||
}
|
||||
}
|
||||
}
|
||||
appender.appendSql( ']' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this SQL dialect known to support some kind of distinct from predicate.
|
||||
* <p/>
|
||||
* Basically, does it support syntax like
|
||||
* "... where FIRST_NAME IS DISTINCT FROM LAST_NAME"
|
||||
*
|
||||
* @return True if this SQL dialect is known to support some kind of distinct from predicate; false otherwise
|
||||
* @since 6.1
|
||||
*/
|
||||
public boolean supportsDistinctFromPredicate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The JDBC {@link SqlTypes type code} to use for mapping
|
||||
* properties of basic Java array or {@code Collection} types.
|
||||
* <p>
|
||||
* Usually {@link SqlTypes#ARRAY} or {@link SqlTypes#VARBINARY}.
|
||||
*
|
||||
* @return one of the type codes defined by {@link SqlTypes}.
|
||||
* @since 6.1
|
||||
*/
|
||||
public int getPreferredSqlTypeCodeForArray() {
|
||||
return supportsStandardArrays() ? ARRAY : VARBINARY;
|
||||
}
|
||||
|
||||
/**
|
||||
* The JDBC {@link Types type code} to use for mapping
|
||||
* properties of Java type {@code boolean}.
|
||||
|
@ -3644,6 +3773,7 @@ public abstract class Dialect implements ConversionContext {
|
|||
case SqlTypes.DOUBLE:
|
||||
case SqlTypes.REAL:
|
||||
// this is almost always the thing we use:
|
||||
length = null;
|
||||
size.setPrecision( javaType.getDefaultSqlPrecision( Dialect.this, jdbcType ) );
|
||||
if ( scale != null && scale != 0 ) {
|
||||
throw new IllegalArgumentException("scale has no meaning for floating point numbers");
|
||||
|
@ -3658,6 +3788,7 @@ public abstract class Dialect implements ConversionContext {
|
|||
case SqlTypes.TIMESTAMP:
|
||||
case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
|
||||
case SqlTypes.TIMESTAMP_UTC:
|
||||
length = null;
|
||||
size.setPrecision( javaType.getDefaultSqlPrecision( Dialect.this, jdbcType ) );
|
||||
if ( scale != null && scale != 0 ) {
|
||||
throw new IllegalArgumentException("scale has no meaning for timestamps");
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.dialect;
|
|||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.PessimisticLockException;
|
||||
|
@ -64,6 +65,7 @@ import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
|||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
|
@ -168,6 +170,11 @@ public class H2Dialect extends Dialect {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsStandardArrays() {
|
||||
return getVersion().isSameOrAfter( 2 );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String columnType(int sqlTypeCode) {
|
||||
switch ( sqlTypeCode ) {
|
||||
|
@ -326,6 +333,16 @@ public class H2Dialect extends Dialect {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer resolveSqlTypeCode(String columnTypeName, TypeConfiguration typeConfiguration) {
|
||||
switch ( columnTypeName ) {
|
||||
case "FLOAT(24)":
|
||||
// Use REAL instead of FLOAT to get Float as recommended Java type
|
||||
return Types.REAL;
|
||||
}
|
||||
return super.resolveSqlTypeCode( columnTypeName, typeConfiguration );
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcType resolveSqlTypeDescriptor(
|
||||
String columnTypeName,
|
||||
|
@ -340,6 +357,15 @@ public class H2Dialect extends Dialect {
|
|||
return super.resolveSqlTypeDescriptor( columnTypeName, jdbcTypeCode, precision, scale, jdbcTypeRegistry );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer resolveSqlTypeCode(String typeName, String baseTypeName, TypeConfiguration typeConfiguration) {
|
||||
switch ( baseTypeName ) {
|
||||
case "CHARACTER VARYING":
|
||||
return VARCHAR;
|
||||
}
|
||||
return super.resolveSqlTypeCode( typeName, baseTypeName, typeConfiguration );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxVarcharLength() {
|
||||
return 1_048_576;
|
||||
|
@ -420,6 +446,11 @@ public class H2Dialect extends Dialect {
|
|||
return limitHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsDistinctFromPredicate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsIfExistsAfterTableName() {
|
||||
return !supportsIfExistsBeforeTableName();
|
||||
|
|
|
@ -76,6 +76,7 @@ import jakarta.persistence.TemporalType;
|
|||
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
|
||||
import static org.hibernate.type.SqlTypes.BLOB;
|
||||
import static org.hibernate.type.SqlTypes.CLOB;
|
||||
import static org.hibernate.type.SqlTypes.DOUBLE;
|
||||
import static org.hibernate.type.SqlTypes.NCLOB;
|
||||
import static org.hibernate.type.SqlTypes.NUMERIC;
|
||||
|
||||
|
@ -145,6 +146,15 @@ public class HSQLDialect extends Dialect {
|
|||
return super.columnType( sqlTypeCode );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer resolveSqlTypeCode(String typeName, String baseTypeName, TypeConfiguration typeConfiguration) {
|
||||
switch ( baseTypeName ) {
|
||||
case "DOUBLE":
|
||||
return DOUBLE;
|
||||
}
|
||||
return super.resolveSqlTypeCode( typeName, baseTypeName, typeConfiguration );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultStatementBatchSize() {
|
||||
return 15;
|
||||
|
@ -364,6 +374,11 @@ public class HSQLDialect extends Dialect {
|
|||
return pattern.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsDistinctFromPredicate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLockTimeouts() {
|
||||
return false;
|
||||
|
@ -420,6 +435,11 @@ public class HSQLDialect extends Dialect {
|
|||
return SequenceInformationExtractorHSQLDBDatabaseImpl.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsStandardArrays() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
|
||||
return getVersion().isBefore( 2 ) ? EXTRACTOR_18 : EXTRACTOR_20;
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
|
|||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||
|
||||
/**
|
||||
* A SQL AST translator for HSQL.
|
||||
|
@ -215,10 +216,18 @@ public class HSQLSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAs
|
|||
switch ( operator ) {
|
||||
case DISTINCT_FROM:
|
||||
case NOT_DISTINCT_FROM:
|
||||
if ( lhs.getExpressionType().getJdbcMappings().get( 0 ).getJdbcType() instanceof ArrayJdbcType ) {
|
||||
// HSQL implements distinct from semantics for arrays
|
||||
lhs.accept( this );
|
||||
appendSql( operator == ComparisonOperator.DISTINCT_FROM ? "<>" : "=" );
|
||||
rhs.accept( this );
|
||||
}
|
||||
else {
|
||||
// HSQL does not like parameters in the distinct from predicate
|
||||
render( lhs, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
|
||||
appendSql( operator.sqlText() );
|
||||
render( rhs, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
renderComparisonStandard( lhs, operator, rhs );
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* 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.dialect;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.BasicBinder;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.ObjectJdbcType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* Descriptor for {@link Types#ARRAY ARRAY} handling.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
* @author Jordan Gigov
|
||||
*/
|
||||
public class OracleArrayJdbcType extends ArrayJdbcType {
|
||||
|
||||
public static final OracleArrayJdbcType INSTANCE = new OracleArrayJdbcType( null, ObjectJdbcType.INSTANCE );
|
||||
private static final ClassValue<Method> NAME_BINDER = new ClassValue<Method>() {
|
||||
@Override
|
||||
protected Method computeValue(Class<?> type) {
|
||||
try {
|
||||
return type.getMethod( "setArray", String.class, java.sql.Array.class );
|
||||
}
|
||||
catch ( Exception ex ) {
|
||||
// add logging? Did we get NoSuchMethodException or SecurityException?
|
||||
// Doesn't matter which. We can't use it.
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
private static final Class<?> ORACLE_CONNECTION_CLASS;
|
||||
private static final Method CREATE_ARRAY_METHOD;
|
||||
|
||||
static {
|
||||
try {
|
||||
ORACLE_CONNECTION_CLASS = Class.forName( "oracle.jdbc.OracleConnection" );
|
||||
CREATE_ARRAY_METHOD = ORACLE_CONNECTION_CLASS.getMethod( "createOracleArray", String.class, Object.class );
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException( "Couldn't initialize OracleArrayJdbcType", e );
|
||||
}
|
||||
}
|
||||
|
||||
private final String typeName;
|
||||
|
||||
public OracleArrayJdbcType(String typeName, JdbcType elementJdbcType) {
|
||||
super( elementJdbcType );
|
||||
this.typeName = typeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaTypeDescriptor) {
|
||||
// No array literal support
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcType resolveType(
|
||||
TypeConfiguration typeConfiguration,
|
||||
Dialect dialect,
|
||||
JdbcType elementType,
|
||||
ColumnTypeInformation columnTypeInformation) {
|
||||
String typeName = columnTypeInformation.getTypeName();
|
||||
if ( typeName == null || typeName.isBlank() ) {
|
||||
typeName = dialect.getArrayTypeName(
|
||||
typeConfiguration.getDdlTypeRegistry().getTypeName(
|
||||
elementType.getDefaultSqlTypeCode(),
|
||||
dialect
|
||||
)
|
||||
);
|
||||
}
|
||||
if ( typeName == null ) {
|
||||
// Fallback to XML type for the representation of arrays as the native JSON type was only introduced in 21
|
||||
return typeConfiguration.getJdbcTypeRegistry().getDescriptor( SqlTypes.SQLXML );
|
||||
}
|
||||
return new OracleArrayJdbcType( typeName, elementType );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaTypeDescriptor) {
|
||||
//noinspection unchecked
|
||||
final BasicPluralJavaType<X> containerJavaType = (BasicPluralJavaType<X>) javaTypeDescriptor;
|
||||
return new BasicBinder<X>( javaTypeDescriptor, this ) {
|
||||
@Override
|
||||
protected void doBindNull(PreparedStatement st, int index, WrapperOptions options) throws SQLException {
|
||||
st.setNull( index, Types.ARRAY, typeName );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBindNull(CallableStatement st, String name, WrapperOptions options) throws SQLException {
|
||||
st.setNull( name, Types.ARRAY, typeName );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
|
||||
final java.sql.Array arr = getArray( value, containerJavaType, options );
|
||||
st.setArray( index, arr );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||
throws SQLException {
|
||||
final java.sql.Array arr = getArray( value, containerJavaType, options );
|
||||
final Method nameBinder = NAME_BINDER.get( st.getClass() );
|
||||
if ( nameBinder == null ) {
|
||||
try {
|
||||
st.setObject( name, arr, Types.ARRAY );
|
||||
return;
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
throw new HibernateException( "JDBC driver does not support named parameters for setArray. Use positional.", ex );
|
||||
}
|
||||
}
|
||||
// Not that it's supposed to have setArray(String,Array) by standard.
|
||||
// There are numerous missing methods that only have versions for positional parameter,
|
||||
// but not named ones.
|
||||
|
||||
try {
|
||||
nameBinder.invoke( st, name, arr );
|
||||
}
|
||||
catch ( Throwable t ) {
|
||||
throw new HibernateException( t );
|
||||
}
|
||||
}
|
||||
|
||||
private java.sql.Array getArray(
|
||||
X value,
|
||||
BasicPluralJavaType<X> containerJavaType,
|
||||
WrapperOptions options) throws SQLException {
|
||||
//noinspection unchecked
|
||||
final Class<Object[]> arrayClass = (Class<Object[]>) Array.newInstance(
|
||||
getElementJdbcType().getPreferredJavaTypeClass( options ),
|
||||
0
|
||||
).getClass();
|
||||
final Object[] objects = javaTypeDescriptor.unwrap( value, arrayClass, options );
|
||||
|
||||
final SharedSessionContractImplementor session = options.getSession();
|
||||
final Object oracleConnection = session.getJdbcCoordinator().getLogicalConnection().getPhysicalConnection()
|
||||
.unwrap( ORACLE_CONNECTION_CLASS );
|
||||
try {
|
||||
return (java.sql.Array) CREATE_ARRAY_METHOD.invoke( oracleConnection, typeName, objects );
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new HibernateException( "Couldn't create a java.sql.Array", e );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OracleArrayJdbcType that = (OracleArrayJdbcType) o;
|
||||
|
||||
return typeName.equals( that.typeName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return typeName.hashCode();
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ import java.util.Locale;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.QueryTimeoutException;
|
||||
import org.hibernate.boot.model.TypeContributions;
|
||||
|
@ -683,6 +684,20 @@ public class OracleDialect extends Dialect {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArrayTypeName(String elementTypeName) {
|
||||
// Return null to signal that there is no array type since Oracle only has named array types
|
||||
// TODO: discuss if it makes sense to parse a config parameter to a map which we can query here
|
||||
// e.g. `hibernate.oracle.array_types=numeric(10,0)=intarray,...`
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPreferredSqlTypeCodeForArray() {
|
||||
// Prefer to resolve to the OracleArrayJdbcType, since that will fall back to XML later if needed
|
||||
return ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||
super.contributeTypes( typeContributions, serviceRegistry );
|
||||
|
@ -705,6 +720,7 @@ public class OracleDialect extends Dialect {
|
|||
typeContributions.contributeJdbcType( descriptor );
|
||||
}
|
||||
|
||||
typeContributions.contributeJdbcType( OracleArrayJdbcType.INSTANCE );
|
||||
// Oracle requires a custom binder for binding untyped nulls with the NULL type
|
||||
typeContributions.contributeJdbcType( NullJdbcType.INSTANCE );
|
||||
typeContributions.contributeJdbcType( ObjectNullAsNullTypeJdbcType.INSTANCE );
|
||||
|
|
|
@ -10,6 +10,7 @@ import java.util.List;
|
|||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.util.collections.Stack;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.query.sqm.BinaryArithmeticOperator;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.query.sqm.FetchClauseType;
|
||||
|
@ -40,6 +41,7 @@ import org.hibernate.sql.ast.tree.select.QuerySpec;
|
|||
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||
import org.hibernate.sql.ast.tree.select.SortSpecification;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
|
||||
/**
|
||||
* A SQL AST translator for Oracle.
|
||||
|
@ -352,7 +354,56 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
|||
|
||||
@Override
|
||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||
if ( lhs.getExpressionType() == null ) {
|
||||
renderComparisonEmulateDecode( lhs, operator, rhs );
|
||||
return;
|
||||
}
|
||||
final JdbcMapping lhsMapping = lhs.getExpressionType().getJdbcMappings().get( 0 );
|
||||
switch ( lhsMapping.getJdbcType().getJdbcTypeCode() ) {
|
||||
case SqlTypes.SQLXML:
|
||||
// In Oracle, XMLTYPE is not "comparable", so we have to use the xmldiff function for this purpose
|
||||
switch ( operator ) {
|
||||
case EQUAL:
|
||||
case NOT_DISTINCT_FROM:
|
||||
appendSql( "0=" );
|
||||
break;
|
||||
case NOT_EQUAL:
|
||||
case DISTINCT_FROM:
|
||||
appendSql( "1=" );
|
||||
break;
|
||||
default:
|
||||
renderComparisonEmulateDecode( lhs, operator, rhs );
|
||||
return;
|
||||
}
|
||||
appendSql( "existsnode(xmldiff(" );
|
||||
lhs.accept( this );
|
||||
appendSql( ',' );
|
||||
rhs.accept( this );
|
||||
appendSql( "),'/*[local-name()=''xdiff'']/*')" );
|
||||
break;
|
||||
case SqlTypes.BLOB:
|
||||
// In Oracle, BLOB types are not "comparable", so we have to use the dbms_lob.compare function for this purpose
|
||||
switch ( operator ) {
|
||||
case EQUAL:
|
||||
appendSql( "0=" );
|
||||
break;
|
||||
case NOT_EQUAL:
|
||||
appendSql( "-1=" );
|
||||
break;
|
||||
default:
|
||||
renderComparisonEmulateDecode( lhs, operator, rhs );
|
||||
return;
|
||||
}
|
||||
appendSql( "dbms_lob.compare(" );
|
||||
lhs.accept( this );
|
||||
appendSql( ',' );
|
||||
rhs.accept( this );
|
||||
appendSql( ')' );
|
||||
break;
|
||||
default:
|
||||
renderComparisonEmulateDecode( lhs, operator, rhs );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,6 +16,7 @@ import java.util.Calendar;
|
|||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
@ -38,6 +39,7 @@ import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy;
|
|||
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
|
||||
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
|
||||
import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.exception.LockAcquisitionException;
|
||||
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||
|
@ -66,6 +68,7 @@ import org.hibernate.sql.ast.tree.Statement;
|
|||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
import org.hibernate.type.JavaObjectType;
|
||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.InstantAsTimestampWithTimeZoneJdbcType;
|
||||
|
@ -74,6 +77,7 @@ import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
|
|||
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.XmlJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
import org.hibernate.type.descriptor.sql.internal.CapacityDependentDdlType;
|
||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||
import org.hibernate.type.descriptor.sql.internal.Scale6IntervalSecondDdlType;
|
||||
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||
|
@ -85,11 +89,13 @@ import static org.hibernate.query.sqm.TemporalUnit.EPOCH;
|
|||
import static org.hibernate.query.sqm.TemporalUnit.MONTH;
|
||||
import static org.hibernate.query.sqm.TemporalUnit.QUARTER;
|
||||
import static org.hibernate.query.sqm.TemporalUnit.YEAR;
|
||||
import static org.hibernate.type.SqlTypes.ARRAY;
|
||||
import static org.hibernate.type.SqlTypes.BINARY;
|
||||
import static org.hibernate.type.SqlTypes.BLOB;
|
||||
import static org.hibernate.type.SqlTypes.CHAR;
|
||||
import static org.hibernate.type.SqlTypes.CLOB;
|
||||
import static org.hibernate.type.SqlTypes.GEOGRAPHY;
|
||||
import static org.hibernate.type.SqlTypes.FLOAT;
|
||||
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
||||
import static org.hibernate.type.SqlTypes.INET;
|
||||
import static org.hibernate.type.SqlTypes.JSON;
|
||||
|
@ -205,6 +211,16 @@ public class PostgreSQLDialect extends Dialect {
|
|||
super.registerColumnTypes( typeContributions, serviceRegistry );
|
||||
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
|
||||
|
||||
// Register this type to be able to support Float[]
|
||||
// The issue is that the JDBC driver can't handle createArrayOf( "float(24)", ... )
|
||||
// It requires the use of "real" or "float4"
|
||||
// Alternatively we could introduce a new API in Dialect for creating such base names
|
||||
ddlTypeRegistry.addDescriptor(
|
||||
CapacityDependentDdlType.builder( FLOAT, columnType( FLOAT ), castType( FLOAT ), this )
|
||||
.withTypeCapacity( 24, "float4" )
|
||||
.build()
|
||||
);
|
||||
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INET, "inet", this ) );
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "geometry", this ) );
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOGRAPHY, "geography", this ) );
|
||||
|
@ -249,7 +265,8 @@ public class PostgreSQLDialect extends Dialect {
|
|||
int precision,
|
||||
int scale,
|
||||
JdbcTypeRegistry jdbcTypeRegistry) {
|
||||
if ( jdbcTypeCode == OTHER ) {
|
||||
switch ( jdbcTypeCode ) {
|
||||
case OTHER:
|
||||
switch ( columnTypeName ) {
|
||||
case "uuid":
|
||||
jdbcTypeCode = UUID;
|
||||
|
@ -271,10 +288,49 @@ public class PostgreSQLDialect extends Dialect {
|
|||
jdbcTypeCode = GEOGRAPHY;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ARRAY:
|
||||
final JdbcType jdbcType = jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
|
||||
// PostgreSQL names array types by prepending an underscore to the base name
|
||||
if ( jdbcType instanceof ArrayJdbcType && columnTypeName.charAt( 0 ) == '_' ) {
|
||||
final String componentTypeName = columnTypeName.substring( 1 );
|
||||
final Integer sqlTypeCode = resolveSqlTypeCode( componentTypeName, jdbcTypeRegistry.getTypeConfiguration() );
|
||||
if ( sqlTypeCode != null ) {
|
||||
return ( (ArrayJdbcType) jdbcType ).resolveType(
|
||||
jdbcTypeRegistry.getTypeConfiguration(),
|
||||
jdbcTypeRegistry.getTypeConfiguration().getServiceRegistry()
|
||||
.getService( JdbcServices.class )
|
||||
.getDialect(),
|
||||
jdbcTypeRegistry.getDescriptor( sqlTypeCode ),
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
return jdbcType;
|
||||
}
|
||||
return jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer resolveSqlTypeCode(String columnTypeName, TypeConfiguration typeConfiguration) {
|
||||
switch ( columnTypeName ) {
|
||||
case "bool":
|
||||
return Types.BOOLEAN;
|
||||
case "float4":
|
||||
// Use REAL instead of FLOAT to get Float as recommended Java type
|
||||
return Types.REAL;
|
||||
case "float8":
|
||||
return Types.DOUBLE;
|
||||
case "int2":
|
||||
return Types.SMALLINT;
|
||||
case "int4":
|
||||
return Types.INTEGER;
|
||||
case "int8":
|
||||
return Types.BIGINT;
|
||||
}
|
||||
return super.resolveSqlTypeCode( columnTypeName, typeConfiguration );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String currentTime() {
|
||||
return "localtime";
|
||||
|
@ -507,6 +563,11 @@ public class PostgreSQLDialect extends Dialect {
|
|||
return "select current_schema()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsDistinctFromPredicate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsIfExistsBeforeTableName() {
|
||||
return getVersion().isSameOrAfter( 8, 2 );
|
||||
|
@ -835,6 +896,11 @@ public class PostgreSQLDialect extends Dialect {
|
|||
return 63;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsStandardArrays() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsJdbcConnectionLobCreation(DatabaseMetaData databaseMetaData) {
|
||||
return false;
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.hibernate.type.descriptor.WrapperOptions;
|
|||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.BasicBinder;
|
||||
import org.hibernate.type.descriptor.jdbc.BasicExtractor;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
/**
|
||||
|
@ -81,6 +82,12 @@ public abstract class PostgreSQLPGObjectJdbcType implements JdbcType {
|
|||
return javaType.unwrap( value, String.class, options );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
||||
// No literal support for now
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -155,6 +155,16 @@ public class SpannerDialect extends Dialect {
|
|||
return 10_485_760;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsStandardArrays() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArrayTypeName(String elementTypeName) {
|
||||
return "ARRAY<" + elementTypeName + ">";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
||||
super.initializeFunctionRegistry( queryEngine );
|
||||
|
|
|
@ -208,6 +208,11 @@ public class SybaseASEDialect extends SybaseDialect {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsDistinctFromPredicate() {
|
||||
return getVersion().isSameOrAfter( 16, 3 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||
super.contributeTypes( typeContributions, serviceRegistry );
|
||||
|
|
|
@ -236,11 +236,6 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsDistinctFromPredicate() {
|
||||
return getDialect().getVersion().isSameOrAfter( 16, 3 );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||
// I think intersect is only supported in 16.0 SP3
|
||||
|
|
|
@ -102,12 +102,24 @@ public final class TypeNames {
|
|||
* the default type name otherwise
|
||||
*/
|
||||
public String get(int typeCode, Long size, Integer precision, Integer scale) {
|
||||
final Long compareValue;
|
||||
if ( size != null ) {
|
||||
compareValue = size;
|
||||
}
|
||||
else if ( precision != null ) {
|
||||
// In case size is null, but a precision is available, use that to search a type for that capacity
|
||||
compareValue = precision.longValue();
|
||||
}
|
||||
else {
|
||||
compareValue = null;
|
||||
}
|
||||
if ( compareValue != null ) {
|
||||
final Map<Long, String> map = weighted.get( typeCode );
|
||||
if ( map != null && map.size() > 0 ) {
|
||||
final long value = compareValue;
|
||||
// iterate entries ordered by capacity to find first fit
|
||||
for ( Map.Entry<Long, String> entry : map.entrySet() ) {
|
||||
if ( size <= entry.getKey() ) {
|
||||
if ( value <= entry.getKey() ) {
|
||||
return replace( entry.getValue(), size, precision, scale );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -576,6 +576,24 @@ public final class ConfigurationHelper {
|
|||
return SqlTypes.TIMESTAMP_UTC;
|
||||
}
|
||||
|
||||
@Incubating
|
||||
public static synchronized int getPreferredSqlTypeCodeForArray(StandardServiceRegistry serviceRegistry) {
|
||||
final Integer typeCode = serviceRegistry.getService( ConfigurationService.class ).getSetting(
|
||||
AvailableSettings.PREFERRED_ARRAY_JDBC_TYPE,
|
||||
TypeCodeConverter.INSTANCE
|
||||
);
|
||||
if ( typeCode != null ) {
|
||||
INCUBATION_LOGGER.incubatingSetting( AvailableSettings.PREFERRED_ARRAY_JDBC_TYPE );
|
||||
return typeCode;
|
||||
}
|
||||
|
||||
// default to the Dialect answer
|
||||
return serviceRegistry.getService( JdbcServices.class )
|
||||
.getJdbcEnvironment()
|
||||
.getDialect()
|
||||
.getPreferredSqlTypeCodeForArray();
|
||||
}
|
||||
|
||||
private static class TypeCodeConverter implements ConfigurationService.Converter<Integer> {
|
||||
|
||||
public static final TypeCodeConverter INSTANCE = new TypeCodeConverter();
|
||||
|
|
|
@ -478,6 +478,7 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
|||
getColumn(),
|
||||
ownerName,
|
||||
propertyName,
|
||||
getMetadata().getDatabase().getDialect(),
|
||||
typeConfiguration
|
||||
);
|
||||
|
||||
|
@ -694,6 +695,11 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
|||
return getBuildingContext().getPreferredSqlTypeCodeForInstant();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPreferredSqlTypeCodeForArray() {
|
||||
return getBuildingContext().getPreferredSqlTypeCodeForArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() {
|
||||
if ( timeZoneStorageType != null ) {
|
||||
|
|
|
@ -7,10 +7,12 @@
|
|||
package org.hibernate.mapping;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.Types;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.boot.model.TruthValue;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.Size;
|
||||
import org.hibernate.engine.spi.Mapping;
|
||||
|
@ -19,9 +21,13 @@ import org.hibernate.loader.internal.AliasConstantsHelper;
|
|||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
|
||||
import org.hibernate.sql.Template;
|
||||
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||
import org.hibernate.type.BasicPluralType;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.ComponentType;
|
||||
import org.hibernate.type.EntityType;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
import static org.hibernate.internal.util.StringHelper.safeInterning;
|
||||
|
@ -31,7 +37,7 @@ import static org.hibernate.internal.util.StringHelper.safeInterning;
|
|||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class Column implements Selectable, Serializable, Cloneable {
|
||||
public class Column implements Selectable, Serializable, Cloneable, ColumnTypeInformation {
|
||||
|
||||
private Long length;
|
||||
private Integer precision;
|
||||
|
@ -224,6 +230,44 @@ public class Column implements Selectable, Serializable, Cloneable {
|
|||
}
|
||||
}
|
||||
|
||||
private String getSqlTypeName(TypeConfiguration typeConfiguration, Dialect dialect, Mapping mapping) {
|
||||
final Type type = getValue().getType();
|
||||
if ( type instanceof BasicPluralType<?, ?> && ( (BasicType<?>) type ).getJdbcType() instanceof ArrayJdbcType ) {
|
||||
final BasicPluralType<?, ?> containerType = (BasicPluralType<?, ?>) type;
|
||||
final BasicType<?> elementType = containerType.getElementType();
|
||||
final int sqlTypeCode;
|
||||
try {
|
||||
sqlTypeCode = elementType.getJdbcType().getDefaultSqlTypeCode();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new MappingException(
|
||||
"Could not determine type for column " +
|
||||
name +
|
||||
" of type " +
|
||||
type.getClass().getName() +
|
||||
": " +
|
||||
e.getClass().getName(),
|
||||
e
|
||||
);
|
||||
}
|
||||
|
||||
final String elementTypeName = typeConfiguration.getDdlTypeRegistry().getTypeName(
|
||||
sqlTypeCode,
|
||||
dialect.getSizeStrategy().resolveSize(
|
||||
elementType.getJdbcMapping().getJdbcType(),
|
||||
elementType.getJavaTypeDescriptor(),
|
||||
precision,
|
||||
scale,
|
||||
length
|
||||
)
|
||||
);
|
||||
return dialect.getArrayTypeName( elementTypeName );
|
||||
}
|
||||
else {
|
||||
return typeConfiguration.getDdlTypeRegistry().getTypeName( getSqlTypeCode( mapping ), getColumnSize( dialect, mapping ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying columns SqlTypeCode.
|
||||
* If null, it is because the SqlTypeCode is unknown.
|
||||
|
@ -244,7 +288,7 @@ public class Column implements Selectable, Serializable, Cloneable {
|
|||
public String getSqlType(TypeConfiguration typeConfiguration, Dialect dialect, Mapping mapping) throws HibernateException {
|
||||
if ( sqlType == null ) {
|
||||
try {
|
||||
sqlType = typeConfiguration.getDdlTypeRegistry().getTypeName( getSqlTypeCode( mapping ), getColumnSize( dialect, mapping ) );
|
||||
sqlType = getSqlTypeName( typeConfiguration, dialect, mapping );
|
||||
}
|
||||
catch (HibernateException cause) {
|
||||
throw new HibernateException(
|
||||
|
@ -261,6 +305,34 @@ public class Column implements Selectable, Serializable, Cloneable {
|
|||
return sqlType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
return sqlType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TruthValue getNullable() {
|
||||
return nullable ? TruthValue.TRUE : TruthValue.FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTypeCode() {
|
||||
return sqlTypeCode == null ? Types.OTHER : sqlTypeCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnSize() {
|
||||
if ( length == null ) {
|
||||
return precision == null ? 0 : precision;
|
||||
}
|
||||
return length.intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDecimalDigits() {
|
||||
return scale == null ? 0 : scale;
|
||||
}
|
||||
|
||||
public Size getColumnSize(Dialect dialect, Mapping mapping) {
|
||||
if ( columnSize != null ) {
|
||||
return columnSize;
|
||||
|
|
|
@ -26,19 +26,19 @@ import org.hibernate.type.descriptor.jdbc.JdbcType;
|
|||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class OrdinalEnumValueConverter<E extends Enum<E>> implements EnumValueConverter<E,Integer>, Serializable {
|
||||
public class OrdinalEnumValueConverter<E extends Enum<E>> implements EnumValueConverter<E, Number>, Serializable {
|
||||
|
||||
private final EnumJavaType<E> enumJavaType;
|
||||
private final JdbcType jdbcType;
|
||||
private final JavaType<Integer> relationalJavaType;
|
||||
private final JavaType<Number> relationalJavaType;
|
||||
|
||||
private transient ValueExtractor<Integer> valueExtractor;
|
||||
private transient ValueBinder<Integer> valueBinder;
|
||||
private transient ValueExtractor<Number> valueExtractor;
|
||||
private transient ValueBinder<Number> valueBinder;
|
||||
|
||||
public OrdinalEnumValueConverter(
|
||||
EnumJavaType<E> enumJavaType,
|
||||
JdbcType jdbcType,
|
||||
JavaType<Integer> relationalJavaType) {
|
||||
JavaType<Number> relationalJavaType) {
|
||||
this.enumJavaType = enumJavaType;
|
||||
this.jdbcType = jdbcType;
|
||||
this.relationalJavaType = relationalJavaType;
|
||||
|
@ -48,12 +48,12 @@ public class OrdinalEnumValueConverter<E extends Enum<E>> implements EnumValueCo
|
|||
}
|
||||
|
||||
@Override
|
||||
public E toDomainValue(Integer relationalForm) {
|
||||
return enumJavaType.fromOrdinal( relationalForm );
|
||||
public E toDomainValue(Number relationalForm) {
|
||||
return enumJavaType.fromOrdinal( relationalForm == null ? null : relationalForm.intValue() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer toRelationalValue(E domainForm) {
|
||||
public Number toRelationalValue(E domainForm) {
|
||||
return enumJavaType.toOrdinal( domainForm );
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ public class OrdinalEnumValueConverter<E extends Enum<E>> implements EnumValueCo
|
|||
}
|
||||
|
||||
@Override
|
||||
public JavaType<Integer> getRelationalJavaType() {
|
||||
public JavaType<Number> getRelationalJavaType() {
|
||||
return relationalJavaType;
|
||||
}
|
||||
|
||||
|
@ -86,8 +86,7 @@ public class OrdinalEnumValueConverter<E extends Enum<E>> implements EnumValueCo
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeValue(
|
||||
PreparedStatement statement, E value, int position, SharedSessionContractImplementor session)
|
||||
public void writeValue(PreparedStatement statement, E value, int position, SharedSessionContractImplementor session)
|
||||
throws SQLException {
|
||||
valueBinder.bind( statement, toRelationalValue( value ), position, session );
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ import org.hibernate.type.BasicType;
|
|||
import org.hibernate.type.ComponentType;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
|
@ -859,8 +860,8 @@ public class MappingMetamodelImpl implements MappingMetamodelImplementor, Metamo
|
|||
return managedType;
|
||||
}
|
||||
|
||||
final JavaType<T> javaType = getTypeConfiguration().getJavaTypeRegistry()
|
||||
.findDescriptor( javaClass );
|
||||
final JavaTypeRegistry javaTypeRegistry = getTypeConfiguration().getJavaTypeRegistry();
|
||||
final JavaType<T> javaType = javaTypeRegistry.findDescriptor( javaClass );
|
||||
if ( javaType != null ) {
|
||||
final JdbcType recommendedJdbcType = javaType.getRecommendedJdbcType( getTypeConfiguration().getCurrentBaseSqlTypeIndicators() );
|
||||
if ( recommendedJdbcType != null ) {
|
||||
|
@ -871,6 +872,17 @@ public class MappingMetamodelImpl implements MappingMetamodelImplementor, Metamo
|
|||
}
|
||||
}
|
||||
|
||||
if ( javaClass.isArray() && javaTypeRegistry.findDescriptor( javaClass.getComponentType() ) != null ) {
|
||||
final JavaType<T> resolvedJavaType = javaTypeRegistry.resolveDescriptor( javaClass );
|
||||
final JdbcType recommendedJdbcType = resolvedJavaType.getRecommendedJdbcType( getTypeConfiguration().getCurrentBaseSqlTypeIndicators() );
|
||||
if ( recommendedJdbcType != null ) {
|
||||
return getTypeConfiguration().getBasicTypeRegistry().resolve(
|
||||
resolvedJavaType,
|
||||
recommendedJdbcType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
|
|||
|
||||
@Override
|
||||
public void setBindValue(T value, boolean resolveJdbcTypeIfNecessary) {
|
||||
if ( handleAsMultiValue( value ) ) {
|
||||
if ( handleAsMultiValue( value, null ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
|
|||
return sqmExpressible.getExpressibleJavaType().coerce( value, this );
|
||||
}
|
||||
|
||||
private boolean handleAsMultiValue(T value) {
|
||||
private boolean handleAsMultiValue(T value, BindableType<T> bindableType) {
|
||||
if ( ! queryParameter.allowsMultiValuedBinding() ) {
|
||||
return false;
|
||||
}
|
||||
|
@ -158,7 +158,9 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
|
|||
return false;
|
||||
}
|
||||
|
||||
if ( value instanceof Collection && !isRegisteredAsBasicType( value.getClass() ) ) {
|
||||
if ( value instanceof Collection
|
||||
&& ( bindableType != null && !bindableType.getBindableJavaType().isInstance( value )
|
||||
|| ( bindableType == null && !isRegisteredAsBasicType( value.getClass() ) ) ) ) {
|
||||
//noinspection unchecked
|
||||
setBindValues( (Collection<T>) value );
|
||||
return true;
|
||||
|
@ -184,7 +186,7 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
|
|||
|
||||
@Override
|
||||
public void setBindValue(T value, BindableType<T> clarifiedType) {
|
||||
if ( handleAsMultiValue( value ) ) {
|
||||
if ( handleAsMultiValue( value, clarifiedType ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -206,7 +208,7 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T>, J
|
|||
|
||||
@Override
|
||||
public void setBindValue(T value, TemporalType temporalTypePrecision) {
|
||||
if ( handleAsMultiValue( value ) ) {
|
||||
if ( handleAsMultiValue( value, null ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -5953,7 +5953,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
|
||||
final EnumJavaType<?> enumJtd = sqmEnumLiteral.getExpressibleJavaType();
|
||||
final JdbcType jdbcType = getTypeConfiguration().getJdbcTypeRegistry().getDescriptor( SqlTypes.TINYINT );
|
||||
final BasicJavaType<Integer> relationalJtd = (BasicJavaType) getTypeConfiguration()
|
||||
final BasicJavaType<Number> relationalJtd = (BasicJavaType) getTypeConfiguration()
|
||||
.getJavaTypeRegistry()
|
||||
.getDescriptor( Integer.class );
|
||||
final BasicType<?> jdbcMappingType = getTypeConfiguration().getBasicTypeRegistry().resolve( relationalJtd, jdbcType );
|
||||
|
|
|
@ -172,6 +172,7 @@ import org.hibernate.sql.exec.spi.JdbcSelect;
|
|||
import org.hibernate.sql.exec.spi.JdbcUpdate;
|
||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||
import org.hibernate.sql.results.jdbc.internal.JdbcValuesMappingProducerStandard;
|
||||
import org.hibernate.type.BasicPluralType;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
|
@ -3582,6 +3583,9 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
}
|
||||
|
||||
protected void renderCasted(Expression expression) {
|
||||
if ( expression instanceof SqmParameterInterpretation ) {
|
||||
expression = ( (SqmParameterInterpretation) expression ).getResolvedExpression();
|
||||
}
|
||||
final List<SqlAstNode> arguments = new ArrayList<>( 2 );
|
||||
arguments.add( expression );
|
||||
if ( expression instanceof SqlTypedMappingJdbcParameter ) {
|
||||
|
@ -4296,6 +4300,23 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
}
|
||||
else {
|
||||
final SqlExpressible expressionType = (SqlExpressible) castTarget.getExpressionType();
|
||||
if ( expressionType instanceof BasicPluralType<?, ?> ) {
|
||||
final BasicPluralType<?, ?> containerType = (BasicPluralType<?, ?>) expressionType;
|
||||
final BasicType<?> elementType = containerType.getElementType();
|
||||
final String elementTypeName = sessionFactory.getTypeConfiguration().getDdlTypeRegistry()
|
||||
.getDescriptor( elementType.getJdbcType().getDefaultSqlTypeCode() )
|
||||
.getCastTypeName(
|
||||
elementType,
|
||||
castTarget.getLength(),
|
||||
castTarget.getPrecision(),
|
||||
castTarget.getScale()
|
||||
);
|
||||
final String arrayTypeName = dialect.getArrayTypeName( elementTypeName );
|
||||
if ( arrayTypeName != null ) {
|
||||
appendSql( arrayTypeName );
|
||||
return;
|
||||
}
|
||||
}
|
||||
final DdlTypeRegistry ddlTypeRegistry = getSessionFactory().getTypeConfiguration().getDdlTypeRegistry();
|
||||
DdlType ddlType = ddlTypeRegistry
|
||||
.getDescriptor( expressionType.getJdbcMapping().getJdbcType().getDefaultSqlTypeCode() );
|
||||
|
@ -5483,7 +5504,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
* @return True if this SQL dialect is known to support some kind of distinct from predicate; false otherwise
|
||||
*/
|
||||
protected boolean supportsDistinctFromPredicate() {
|
||||
return true;
|
||||
return dialect.supportsDistinctFromPredicate();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,7 +15,7 @@ import org.hibernate.boot.model.naming.Identifier;
|
|||
* @author Christoph Sturm
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface ColumnInformation {
|
||||
public interface ColumnInformation extends ColumnTypeInformation {
|
||||
/**
|
||||
* Access to the containing table.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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.tool.schema.extract.spi;
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.boot.model.TruthValue;
|
||||
|
||||
/**
|
||||
* Provides access to information about existing table columns
|
||||
*
|
||||
* @author Christoph Sturm
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
@Incubating
|
||||
public interface ColumnTypeInformation {
|
||||
|
||||
ColumnTypeInformation EMPTY = new ColumnTypeInformation() {
|
||||
@Override
|
||||
public TruthValue getNullable() {
|
||||
return TruthValue.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTypeCode() {
|
||||
return Types.OTHER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDecimalDigits() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Is the column nullable. The database is allowed to report unknown, hence the use of TruthValue
|
||||
*
|
||||
* @return nullability.
|
||||
*/
|
||||
TruthValue getNullable();
|
||||
|
||||
/**
|
||||
* The JDBC type-code.
|
||||
*
|
||||
* @return JDBC type-code
|
||||
*/
|
||||
int getTypeCode();
|
||||
|
||||
/**
|
||||
* The database specific type name.
|
||||
*
|
||||
* @return Type name
|
||||
*/
|
||||
public String getTypeName();
|
||||
|
||||
// todo : wrap these in org.hibernate.metamodel.spi.relational.Size ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
/**
|
||||
* The column size (length).
|
||||
*
|
||||
* @return The column length
|
||||
*/
|
||||
int getColumnSize();
|
||||
|
||||
/**
|
||||
* The precision, for numeric types
|
||||
*
|
||||
* @return The numeric precision
|
||||
*/
|
||||
int getDecimalDigits();
|
||||
}
|
|
@ -210,7 +210,7 @@ public abstract class AbstractStandardBasicType<T>
|
|||
}
|
||||
|
||||
protected void nullSafeSet(PreparedStatement st, T value, int index, WrapperOptions options) throws SQLException {
|
||||
jdbcType.getBinder( javaType ).bind( st, value, index, options );
|
||||
getJdbcValueBinder().bind( st, value, index, options );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -283,7 +283,7 @@ public abstract class AbstractStandardBasicType<T>
|
|||
|
||||
@Override
|
||||
public T extract(CallableStatement statement, int startIndex, final SharedSessionContractImplementor session) throws SQLException {
|
||||
return jdbcType.getExtractor( javaType ).extract(
|
||||
return getJdbcValueExtractor().extract(
|
||||
statement,
|
||||
startIndex,
|
||||
session
|
||||
|
@ -292,7 +292,7 @@ public abstract class AbstractStandardBasicType<T>
|
|||
|
||||
@Override
|
||||
public T extract(CallableStatement statement, String paramName, final SharedSessionContractImplementor session) throws SQLException {
|
||||
return jdbcType.getExtractor( javaType ).extract(
|
||||
return getJdbcValueExtractor().extract(
|
||||
statement,
|
||||
paramName,
|
||||
session
|
||||
|
@ -316,7 +316,7 @@ public abstract class AbstractStandardBasicType<T>
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected final void nullSafeSet(CallableStatement st, Object value, String name, WrapperOptions options) throws SQLException {
|
||||
jdbcType.getBinder( javaType ).bind( st, (T) value, name, options );
|
||||
getJdbcValueBinder().bind( st, (T) value, name, options );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.ValueExtractor;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* A type that maps between {@link java.sql.Types#ARRAY ARRAY} and {@code T[]}
|
||||
*
|
||||
* @author Jordan Gigov
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class BasicArrayType<T>
|
||||
extends AbstractSingleColumnStandardBasicType<T[]>
|
||||
implements AdjustableBasicType<T[]>, BasicPluralType<T[], T> {
|
||||
|
||||
private final BasicType<T> baseDescriptor;
|
||||
private final String name;
|
||||
private final ValueBinder<T[]> jdbcValueBinder;
|
||||
private final ValueExtractor<T[]> jdbcValueExtractor;
|
||||
|
||||
public BasicArrayType(BasicType<T> baseDescriptor, JdbcType arrayJdbcType, JavaType<T[]> arrayTypeDescriptor) {
|
||||
super( arrayJdbcType, arrayTypeDescriptor );
|
||||
this.baseDescriptor = baseDescriptor;
|
||||
this.name = baseDescriptor.getName() + "[]";
|
||||
final ValueBinder<T[]> jdbcValueBinder = super.getJdbcValueBinder();
|
||||
final ValueExtractor<T[]> jdbcValueExtractor = super.getJdbcValueExtractor();
|
||||
//noinspection unchecked
|
||||
final BasicValueConverter<T, Object> valueConverter = (BasicValueConverter<T, Object>) baseDescriptor.getValueConverter();
|
||||
if ( valueConverter != null ) {
|
||||
this.jdbcValueBinder = new ValueBinder<T[]>() {
|
||||
@Override
|
||||
public void bind(PreparedStatement st, T[] value, int index, WrapperOptions options)
|
||||
throws SQLException {
|
||||
jdbcValueBinder.bind( st, getValue( value, valueConverter, options ), index, options );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(CallableStatement st, T[] value, String name, WrapperOptions options)
|
||||
throws SQLException {
|
||||
jdbcValueBinder.bind( st, getValue( value, valueConverter, options ), name, options );
|
||||
}
|
||||
|
||||
private T[] getValue(
|
||||
T[] value,
|
||||
BasicValueConverter<T, Object> valueConverter,
|
||||
WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
final JdbcType elementJdbcType = baseDescriptor.getJdbcType();
|
||||
final TypeConfiguration typeConfiguration = options.getSessionFactory().getTypeConfiguration();
|
||||
final JdbcType underlyingJdbcType = typeConfiguration.getJdbcTypeRegistry()
|
||||
.getDescriptor( elementJdbcType.getDefaultSqlTypeCode() );
|
||||
final Class<?> preferredJavaTypeClass = underlyingJdbcType.getPreferredJavaTypeClass( options );
|
||||
final Class<?> elementJdbcJavaTypeClass;
|
||||
if ( preferredJavaTypeClass == null ) {
|
||||
elementJdbcJavaTypeClass = underlyingJdbcType.getJdbcRecommendedJavaTypeMapping(
|
||||
null,
|
||||
null,
|
||||
typeConfiguration
|
||||
).getJavaTypeClass();
|
||||
}
|
||||
else {
|
||||
elementJdbcJavaTypeClass = preferredJavaTypeClass;
|
||||
}
|
||||
|
||||
if ( value.getClass().getComponentType() == elementJdbcJavaTypeClass ) {
|
||||
return value;
|
||||
}
|
||||
final Object[] array = (Object[]) Array.newInstance( elementJdbcJavaTypeClass, value.length );
|
||||
for ( int i = 0; i < value.length; i++ ) {
|
||||
array[i] = valueConverter.getRelationalJavaType().unwrap(
|
||||
valueConverter.toRelationalValue( value[i] ),
|
||||
elementJdbcJavaTypeClass,
|
||||
options
|
||||
);
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (T[]) array;
|
||||
}
|
||||
};
|
||||
this.jdbcValueExtractor = new ValueExtractor<T[]>() {
|
||||
@Override
|
||||
public T[] extract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
|
||||
return getValue( jdbcValueExtractor.extract( rs, paramIndex, options ), valueConverter );
|
||||
}
|
||||
|
||||
@Override
|
||||
public T[] extract(CallableStatement statement, int paramIndex, WrapperOptions options)
|
||||
throws SQLException {
|
||||
return getValue( jdbcValueExtractor.extract( statement, paramIndex, options ), valueConverter );
|
||||
}
|
||||
|
||||
@Override
|
||||
public T[] extract(CallableStatement statement, String paramName, WrapperOptions options)
|
||||
throws SQLException {
|
||||
return getValue( jdbcValueExtractor.extract( statement, paramName, options ), valueConverter );
|
||||
}
|
||||
|
||||
private T[] getValue(T[] value, BasicValueConverter<T, Object> valueConverter) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
if ( value.getClass().getComponentType() == valueConverter.getDomainJavaType().getJavaTypeClass() ) {
|
||||
return value;
|
||||
}
|
||||
//noinspection unchecked
|
||||
final T[] array = (T[]) Array.newInstance(
|
||||
valueConverter.getDomainJavaType().getJavaTypeClass(),
|
||||
value.length
|
||||
);
|
||||
for ( int i = 0; i < value.length; i++ ) {
|
||||
array[i] = valueConverter.toDomainValue( value[i] );
|
||||
}
|
||||
return array;
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
this.jdbcValueBinder = jdbcValueBinder;
|
||||
this.jdbcValueExtractor = jdbcValueExtractor;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicType<T> getElementType() {
|
||||
return baseDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean registerUnderJavaType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueExtractor<T[]> getJdbcValueExtractor() {
|
||||
return jdbcValueExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueBinder<T[]> getJdbcValueBinder() {
|
||||
return jdbcValueBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> BasicType<X> resolveIndicatedType(JdbcTypeIndicators indicators, JavaType<X> domainJtd) {
|
||||
// TODO: maybe fallback to some encoding by default if the DB doesn't support arrays natively?
|
||||
// also, maybe move that logic into the ArrayJdbcType
|
||||
//noinspection unchecked
|
||||
return (BasicType<X>) this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.ValueExtractor;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.spi.BasicCollectionJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* A type that maps between {@link java.sql.Types#ARRAY ARRAY} and {@code Collection<T>}
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class BasicCollectionType<C extends Collection<E>, E>
|
||||
extends AbstractSingleColumnStandardBasicType<C>
|
||||
implements AdjustableBasicType<C>, BasicPluralType<C, E> {
|
||||
|
||||
private final BasicType<E> baseDescriptor;
|
||||
private final String name;
|
||||
private final ValueBinder<C> jdbcValueBinder;
|
||||
private final ValueExtractor<C> jdbcValueExtractor;
|
||||
|
||||
public BasicCollectionType(BasicType<E> baseDescriptor, JdbcType arrayJdbcType, BasicCollectionJavaType<C, E> collectionTypeDescriptor) {
|
||||
super( arrayJdbcType, collectionTypeDescriptor );
|
||||
this.baseDescriptor = baseDescriptor;
|
||||
this.name = determineName( collectionTypeDescriptor, baseDescriptor );
|
||||
final ValueBinder<C> jdbcValueBinder = super.getJdbcValueBinder();
|
||||
final ValueExtractor<C> jdbcValueExtractor = super.getJdbcValueExtractor();
|
||||
//noinspection unchecked
|
||||
final BasicValueConverter<E, Object> valueConverter = (BasicValueConverter<E, Object>) baseDescriptor.getValueConverter();
|
||||
if ( valueConverter != null ) {
|
||||
this.jdbcValueBinder = new ValueBinder<C>() {
|
||||
@Override
|
||||
public void bind(PreparedStatement st, C value, int index, WrapperOptions options)
|
||||
throws SQLException {
|
||||
jdbcValueBinder.bind( st, getValue( value, valueConverter, options ), index, options );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(CallableStatement st, C value, String name, WrapperOptions options)
|
||||
throws SQLException {
|
||||
jdbcValueBinder.bind( st, getValue( value, valueConverter, options ), name, options );
|
||||
}
|
||||
|
||||
private C getValue(
|
||||
C value,
|
||||
BasicValueConverter<E, Object> valueConverter,
|
||||
WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
final JdbcType elementJdbcType = baseDescriptor.getJdbcType();
|
||||
final TypeConfiguration typeConfiguration = options.getSessionFactory().getTypeConfiguration();
|
||||
final JdbcType underlyingJdbcType = typeConfiguration.getJdbcTypeRegistry()
|
||||
.getDescriptor( elementJdbcType.getDefaultSqlTypeCode() );
|
||||
final Class<?> preferredJavaTypeClass = underlyingJdbcType.getPreferredJavaTypeClass( options );
|
||||
final Class<?> elementJdbcJavaTypeClass;
|
||||
if ( preferredJavaTypeClass == null ) {
|
||||
elementJdbcJavaTypeClass = underlyingJdbcType.getJdbcRecommendedJavaTypeMapping(
|
||||
null,
|
||||
null,
|
||||
typeConfiguration
|
||||
).getJavaTypeClass();
|
||||
}
|
||||
else {
|
||||
elementJdbcJavaTypeClass = preferredJavaTypeClass;
|
||||
}
|
||||
|
||||
//noinspection unchecked
|
||||
final Collection<Object> converted = (Collection<Object>) collectionTypeDescriptor.getSemantics()
|
||||
.instantiateRaw( value.size(), null );
|
||||
for ( E element : value ) {
|
||||
converted.add(
|
||||
valueConverter.getRelationalJavaType().unwrap(
|
||||
valueConverter.toRelationalValue( element ),
|
||||
elementJdbcJavaTypeClass,
|
||||
options
|
||||
)
|
||||
);
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (C) converted;
|
||||
}
|
||||
};
|
||||
this.jdbcValueExtractor = new ValueExtractor<C>() {
|
||||
@Override
|
||||
public C extract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
|
||||
return getValue( jdbcValueExtractor.extract( rs, paramIndex, options ), valueConverter );
|
||||
}
|
||||
|
||||
@Override
|
||||
public C extract(CallableStatement statement, int paramIndex, WrapperOptions options)
|
||||
throws SQLException {
|
||||
return getValue( jdbcValueExtractor.extract( statement, paramIndex, options ), valueConverter );
|
||||
}
|
||||
|
||||
@Override
|
||||
public C extract(CallableStatement statement, String paramName, WrapperOptions options)
|
||||
throws SQLException {
|
||||
return getValue( jdbcValueExtractor.extract( statement, paramName, options ), valueConverter );
|
||||
}
|
||||
|
||||
private C getValue(C value, BasicValueConverter<E, Object> valueConverter) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
final C converted = collectionTypeDescriptor.getSemantics()
|
||||
.instantiateRaw( value.size(), null );
|
||||
for ( E element : value ) {
|
||||
converted.add( valueConverter.toDomainValue( element ) );
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
this.jdbcValueBinder = jdbcValueBinder;
|
||||
this.jdbcValueExtractor = jdbcValueExtractor;
|
||||
}
|
||||
}
|
||||
|
||||
private static String determineName(BasicCollectionJavaType<?, ?> collectionTypeDescriptor, BasicType<?> baseDescriptor) {
|
||||
switch ( collectionTypeDescriptor.getSemantics().getCollectionClassification() ) {
|
||||
case BAG:
|
||||
case ID_BAG:
|
||||
return "Collection<" + baseDescriptor.getName() + ">";
|
||||
case LIST:
|
||||
return "List<" + baseDescriptor.getName() + ">";
|
||||
case SET:
|
||||
return "Set<" + baseDescriptor.getName() + ">";
|
||||
case SORTED_SET:
|
||||
return "SortedSet<" + baseDescriptor.getName() + ">";
|
||||
case ORDERED_SET:
|
||||
return "OrderedSet<" + baseDescriptor.getName() + ">";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicType<E> getElementType() {
|
||||
return baseDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean registerUnderJavaType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueExtractor<C> getJdbcValueExtractor() {
|
||||
return jdbcValueExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueBinder<C> getJdbcValueBinder() {
|
||||
return jdbcValueBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> BasicType<X> resolveIndicatedType(JdbcTypeIndicators indicators, JavaType<X> domainJtd) {
|
||||
// TODO: maybe fallback to some encoding by default if the DB doesn't support arrays natively?
|
||||
// also, maybe move that logic into the ArrayJdbcType
|
||||
//noinspection unchecked
|
||||
return (BasicType<X>) this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
|
||||
/**
|
||||
* A basic plural type. Represents a type, that is mapped to a single column instead of multiple rows.
|
||||
* This is used for array or collection types, that are backed by e.g. SQL array or JSON/XML DDL types.
|
||||
*
|
||||
* @see BasicCollectionType
|
||||
* @see BasicArrayType
|
||||
*/
|
||||
@Incubating
|
||||
public interface BasicPluralType<C, E> extends BasicType<C> {
|
||||
/**
|
||||
* Get element type
|
||||
*/
|
||||
BasicType<E> getElementType();
|
||||
|
||||
}
|
|
@ -9,12 +9,14 @@ package org.hibernate.type;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.cache.internal.CacheKeyValueDescriptor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.MappingType;
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.metamodel.model.domain.BasicDomainType;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
|
@ -75,6 +77,15 @@ public interface BasicType<T> extends Type, BasicDomainType<T>, MappingType, Bas
|
|||
return getJavaTypeDescriptor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the converter that this basic type uses for transforming from the domain type, to the relational type,
|
||||
* or <code>null</code> if there is no conversion.
|
||||
*/
|
||||
@Incubating
|
||||
default BasicValueConverter<T, ?> getValueConverter() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default ValueExtractor<T> getJdbcValueExtractor() {
|
||||
return getJdbcType().getExtractor( this.getMappedJavaType() );
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.hibernate.engine.spi.Mapping;
|
|||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.ValueExtractor;
|
||||
import org.hibernate.type.descriptor.java.BasicJavaType;
|
||||
|
@ -30,6 +31,7 @@ import org.hibernate.type.internal.UserTypeSqlTypeAdapter;
|
|||
import org.hibernate.type.internal.UserTypeVersionJavaTypeWrapper;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
import org.hibernate.usertype.EnhancedUserType;
|
||||
import org.hibernate.usertype.LoggableUserType;
|
||||
import org.hibernate.usertype.UserType;
|
||||
import org.hibernate.usertype.UserVersionType;
|
||||
|
||||
|
@ -49,7 +51,7 @@ import org.hibernate.usertype.UserVersionType;
|
|||
*/
|
||||
public class CustomType<J>
|
||||
extends AbstractType
|
||||
implements BasicType<J>, ProcedureParameterNamedBinder<J>, ProcedureParameterExtractionAware<J> {
|
||||
implements ConvertedBasicType<J>, ProcedureParameterNamedBinder<J>, ProcedureParameterExtractionAware<J> {
|
||||
|
||||
private final UserType<J> userType;
|
||||
private final String[] registrationKeys;
|
||||
|
@ -205,6 +207,9 @@ public class CustomType<J>
|
|||
if ( value == null ) {
|
||||
return "null";
|
||||
}
|
||||
else if ( userType instanceof LoggableUserType ) {
|
||||
return ( (LoggableUserType) userType ).toLoggableString( value, factory );
|
||||
}
|
||||
else if ( userType instanceof EnhancedUserType<?> ) {
|
||||
return ( (EnhancedUserType<Object>) userType ).toString( value );
|
||||
}
|
||||
|
@ -313,4 +318,9 @@ public class CustomType<J>
|
|||
public JavaType<J> getJavaTypeDescriptor() {
|
||||
return this.getMappedJavaType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicValueConverter<J, Object> getValueConverter() {
|
||||
return userType.getValueConverter();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.hibernate.internal.util.ReflectHelper;
|
|||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||
import org.hibernate.metamodel.model.convert.internal.NamedEnumValueConverter;
|
||||
import org.hibernate.metamodel.model.convert.internal.OrdinalEnumValueConverter;
|
||||
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
|
||||
import org.hibernate.metamodel.model.convert.spi.EnumValueConverter;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.ValueExtractor;
|
||||
|
@ -106,6 +107,11 @@ public class EnumType<T extends Enum<T>>
|
|||
return enumValueConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicValueConverter<T, Object> getValueConverter() {
|
||||
return enumValueConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameterValues(Properties parameters) {
|
||||
// IMPL NOTE: we handle 2 distinct cases here:
|
||||
|
|
|
@ -8,11 +8,13 @@ package org.hibernate.type;
|
|||
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -21,11 +23,14 @@ import javax.xml.namespace.QName;
|
|||
import org.hibernate.internal.util.ReflectHelper;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.spi.UnknownBasicJavaType;
|
||||
|
||||
import jakarta.xml.bind.JAXBContext;
|
||||
import jakarta.xml.bind.JAXBElement;
|
||||
import jakarta.xml.bind.JAXBException;
|
||||
import jakarta.xml.bind.JAXBIntrospector;
|
||||
import jakarta.xml.bind.Marshaller;
|
||||
import jakarta.xml.bind.Unmarshaller;
|
||||
import jakarta.xml.bind.annotation.XmlAnyElement;
|
||||
|
@ -65,30 +70,31 @@ public class JaxbXmlFormatMapper implements FormatMapper {
|
|||
final Unmarshaller unmarshaller = context.createUnmarshaller();
|
||||
final MapWrapper mapWrapper = (MapWrapper) unmarshaller
|
||||
.unmarshal( new StringReader( charSequence.toString() ) );
|
||||
final List<Object> elements = mapWrapper.elements;
|
||||
final Collection<Object> elements = mapWrapper.elements;
|
||||
final Map<Object, Object> map = CollectionHelper.linkedMapOfSize( elements.size() >> 1 );
|
||||
for ( int i = 0; i < elements.size(); i += 2 ) {
|
||||
final Object keyElement = unmarshaller.unmarshal( (Node) elements.get( i ), keyClass ).getValue();
|
||||
final Object valueElement = unmarshaller.unmarshal( (Node) elements.get( i + 1 ), valueClass )
|
||||
.getValue();
|
||||
final Object key;
|
||||
final Object value;
|
||||
if ( keyElement instanceof Element ) {
|
||||
key = ( (Element) keyElement ).getFirstChild().getTextContent();
|
||||
final JAXBIntrospector jaxbIntrospector = context.createJAXBIntrospector();
|
||||
final JAXBElementTransformer keyTransformer;
|
||||
final JAXBElementTransformer valueTransformer;
|
||||
if ( javaType instanceof BasicPluralJavaType<?> ) {
|
||||
keyTransformer = createTransformer( keyClass, "key", null, jaxbIntrospector, wrapperOptions );
|
||||
valueTransformer = createTransformer(
|
||||
( (BasicPluralJavaType<?>) javaType ).getElementJavaType(),
|
||||
"value",
|
||||
null,
|
||||
jaxbIntrospector,
|
||||
wrapperOptions
|
||||
);
|
||||
}
|
||||
else {
|
||||
key = keyElement;
|
||||
}
|
||||
if ( valueElement instanceof Element ) {
|
||||
value = ( (Element) valueElement ).getFirstChild().getTextContent();
|
||||
}
|
||||
else {
|
||||
value = valueElement;
|
||||
keyTransformer = createTransformer( keyClass, "key", null, jaxbIntrospector, wrapperOptions );
|
||||
valueTransformer = createTransformer( valueClass, "value", null, jaxbIntrospector, wrapperOptions );
|
||||
}
|
||||
for ( final Iterator<Object> iterator = elements.iterator(); iterator.hasNext(); ) {
|
||||
final Object key = keyTransformer.fromJAXBElement( iterator.next(), unmarshaller );
|
||||
final Object value = valueTransformer.fromJAXBElement( iterator.next(), unmarshaller );
|
||||
map.put( key, value );
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (T) map;
|
||||
return javaType.wrap( map, wrapperOptions );
|
||||
}
|
||||
else if ( Collection.class.isAssignableFrom( javaType.getJavaTypeClass() ) ) {
|
||||
final JAXBContext context;
|
||||
|
@ -105,22 +111,72 @@ public class JaxbXmlFormatMapper implements FormatMapper {
|
|||
final Unmarshaller unmarshaller = context.createUnmarshaller();
|
||||
final CollectionWrapper collectionWrapper = (CollectionWrapper) unmarshaller
|
||||
.unmarshal( new StringReader( charSequence.toString() ) );
|
||||
final List<Object> elements = collectionWrapper.elements;
|
||||
final Collection<Object> collection = new ArrayList<>( elements.size() >> 1 );
|
||||
for ( int i = 0; i < elements.size(); i++ ) {
|
||||
final Object valueElement = unmarshaller.unmarshal( (Node) elements.get( i ), valueClass )
|
||||
.getValue();
|
||||
final Object value;
|
||||
if ( valueElement instanceof Element ) {
|
||||
value = ( (Element) valueElement ).getFirstChild().getTextContent();
|
||||
final Collection<Object> elements = collectionWrapper.elements;
|
||||
final Collection<Object> collection = new ArrayList<>( elements.size() );
|
||||
final JAXBIntrospector jaxbIntrospector = context.createJAXBIntrospector();
|
||||
final JAXBElementTransformer valueTransformer;
|
||||
if ( javaType instanceof BasicPluralJavaType<?> ) {
|
||||
valueTransformer = createTransformer(
|
||||
( (BasicPluralJavaType<?>) javaType ).getElementJavaType(),
|
||||
"value",
|
||||
null,
|
||||
jaxbIntrospector,
|
||||
wrapperOptions
|
||||
);
|
||||
}
|
||||
else {
|
||||
value = valueElement;
|
||||
valueTransformer = createTransformer( valueClass, "value", null, jaxbIntrospector, wrapperOptions );
|
||||
}
|
||||
for ( Object element : elements ) {
|
||||
final Object value = valueTransformer.fromJAXBElement( element, unmarshaller );
|
||||
collection.add( value );
|
||||
}
|
||||
return javaType.wrap( collection, wrapperOptions );
|
||||
}
|
||||
else if ( javaType.getJavaTypeClass().isArray() ) {
|
||||
final Class<?> valueClass = javaType.getJavaTypeClass().getComponentType();
|
||||
final JAXBContext context = JAXBContext.newInstance( CollectionWrapper.class, valueClass );
|
||||
final Unmarshaller unmarshaller = context.createUnmarshaller();
|
||||
final CollectionWrapper collectionWrapper = (CollectionWrapper) unmarshaller
|
||||
.unmarshal( new StringReader( charSequence.toString() ) );
|
||||
final Collection<Object> elements = collectionWrapper.elements;
|
||||
final JAXBIntrospector jaxbIntrospector = context.createJAXBIntrospector();
|
||||
final JAXBElementTransformer valueTransformer;
|
||||
if ( javaType instanceof BasicPluralJavaType<?> ) {
|
||||
valueTransformer = createTransformer(
|
||||
( (BasicPluralJavaType<?>) javaType ).getElementJavaType(),
|
||||
"value",
|
||||
null,
|
||||
jaxbIntrospector,
|
||||
wrapperOptions
|
||||
);
|
||||
}
|
||||
else {
|
||||
valueTransformer = createTransformer( valueClass, "value", null, jaxbIntrospector, wrapperOptions );
|
||||
}
|
||||
final int length = elements.size();
|
||||
if ( Object[].class.isAssignableFrom( javaType.getJavaTypeClass() ) ) {
|
||||
final Object[] array = (Object[]) Array.newInstance( valueClass, length );
|
||||
int i = 0;
|
||||
for ( Object element : elements ) {
|
||||
final Object value = valueTransformer.fromJAXBElement( element, unmarshaller );
|
||||
array[i] = value;
|
||||
i++;
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (T) collection;
|
||||
return (T) array;
|
||||
}
|
||||
else {
|
||||
//noinspection unchecked
|
||||
final T array = (T) Array.newInstance( valueClass, length );
|
||||
int i = 0;
|
||||
for ( Object element : elements ) {
|
||||
final Object value = valueTransformer.fromJAXBElement( element, unmarshaller );
|
||||
Array.set( array, i, value );
|
||||
i++;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
else {
|
||||
final JAXBContext context = JAXBContext.newInstance( javaType.getJavaTypeClass() );
|
||||
|
@ -164,21 +220,32 @@ public class JaxbXmlFormatMapper implements FormatMapper {
|
|||
context = JAXBContext.newInstance( MapWrapper.class, keyClass, valueClass );
|
||||
}
|
||||
}
|
||||
if ( !map.isEmpty() ) {
|
||||
Object exampleKey = null;
|
||||
Object exampleValue = null;
|
||||
for ( Map.Entry<?, ?> entry : map.entrySet() ) {
|
||||
mapWrapper.elements.add(
|
||||
new JAXBElement<>(
|
||||
new QName( "key" ),
|
||||
keyClass,
|
||||
entry.getKey()
|
||||
)
|
||||
);
|
||||
mapWrapper.elements.add(
|
||||
new JAXBElement<>(
|
||||
new QName( "value" ),
|
||||
valueClass,
|
||||
entry.getValue()
|
||||
)
|
||||
);
|
||||
final Object mapKey = entry.getKey();
|
||||
final Object mapValue = entry.getValue();
|
||||
if ( exampleKey == null && mapKey != null ) {
|
||||
exampleKey = mapKey;
|
||||
if ( exampleValue != null ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( exampleValue == null && mapValue != null ) {
|
||||
exampleValue = mapValue;
|
||||
if ( exampleKey != null ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
final JAXBIntrospector jaxbIntrospector = context.createJAXBIntrospector();
|
||||
final JAXBElementTransformer keyTransformer = createTransformer( keyClass, "key", exampleKey, jaxbIntrospector, wrapperOptions );
|
||||
final JAXBElementTransformer valueTransformer = createTransformer( valueClass, "value", exampleValue, jaxbIntrospector, wrapperOptions );
|
||||
for ( Map.Entry<?, ?> entry : map.entrySet() ) {
|
||||
mapWrapper.elements.add( keyTransformer.toJAXBElement( entry.getKey() ) );
|
||||
mapWrapper.elements.add( valueTransformer.toJAXBElement( entry.getValue() ) );
|
||||
}
|
||||
}
|
||||
createMarshaller( context ).marshal( mapWrapper, stringWriter );
|
||||
}
|
||||
|
@ -186,7 +253,6 @@ public class JaxbXmlFormatMapper implements FormatMapper {
|
|||
final JAXBContext context;
|
||||
final Class<Object> valueClass;
|
||||
final Collection<?> collection = (Collection<?>) value;
|
||||
final CollectionWrapper collectionWrapper = new CollectionWrapper( new ArrayList<>( collection ) );
|
||||
if ( javaType.getJavaType() instanceof ParameterizedType ) {
|
||||
final Type[] typeArguments = ( (ParameterizedType) javaType.getJavaType() ).getActualTypeArguments();
|
||||
valueClass = ReflectHelper.getClass( typeArguments[0] );
|
||||
|
@ -203,22 +269,96 @@ public class JaxbXmlFormatMapper implements FormatMapper {
|
|||
context = JAXBContext.newInstance( CollectionWrapper.class, valueClass );
|
||||
}
|
||||
}
|
||||
// for ( Object element : collection ) {
|
||||
// collectionWrapper.elements.add(
|
||||
// new JAXBElement<>(
|
||||
// new QName( "key" ),
|
||||
// keyClass,
|
||||
// entry.getKey()
|
||||
// )
|
||||
// );
|
||||
// collectionWrapper.elements.add(
|
||||
// new JAXBElement<>(
|
||||
// new QName( "value" ),
|
||||
// valueClass,
|
||||
// entry.getValue()
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
final CollectionWrapper collectionWrapper;
|
||||
if ( collection.isEmpty() ) {
|
||||
collectionWrapper = new CollectionWrapper();
|
||||
}
|
||||
else {
|
||||
collectionWrapper = new CollectionWrapper( new ArrayList<>( collection.size() ) );
|
||||
Object exampleValue = null;
|
||||
for ( Object o : collection ) {
|
||||
if ( o != null ) {
|
||||
exampleValue = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
final JAXBElementTransformer valueTransformer;
|
||||
if ( javaType instanceof BasicPluralJavaType<?> ) {
|
||||
valueTransformer = createTransformer(
|
||||
( (BasicPluralJavaType<?>) javaType ).getElementJavaType(),
|
||||
"value",
|
||||
exampleValue,
|
||||
context.createJAXBIntrospector(),
|
||||
wrapperOptions
|
||||
);
|
||||
}
|
||||
else {
|
||||
valueTransformer = createTransformer(
|
||||
valueClass,
|
||||
"value",
|
||||
exampleValue,
|
||||
context.createJAXBIntrospector(),
|
||||
wrapperOptions
|
||||
);
|
||||
}
|
||||
for ( Object o : collection ) {
|
||||
collectionWrapper.elements.add( valueTransformer.toJAXBElement( o ) );
|
||||
}
|
||||
}
|
||||
createMarshaller( context ).marshal( collectionWrapper, stringWriter );
|
||||
}
|
||||
else if ( javaType.getJavaTypeClass().isArray() ) {
|
||||
//noinspection unchecked
|
||||
final Class<Object> valueClass = (Class<Object>) javaType.getJavaTypeClass().getComponentType();
|
||||
final JAXBContext context = JAXBContext.newInstance( CollectionWrapper.class, valueClass );
|
||||
final CollectionWrapper collectionWrapper;
|
||||
if ( Object[].class.isAssignableFrom( javaType.getJavaTypeClass() ) ) {
|
||||
final Object[] array = (Object[]) value;
|
||||
final List<Object> list = new ArrayList<>( array.length );
|
||||
Object exampleElement = null;
|
||||
for ( Object o : array ) {
|
||||
if ( o != null ) {
|
||||
exampleElement = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
final JAXBElementTransformer transformer;
|
||||
if ( javaType instanceof BasicPluralJavaType<?> ) {
|
||||
transformer = createTransformer(
|
||||
( (BasicPluralJavaType<?>) javaType ).getElementJavaType(),
|
||||
"value",
|
||||
exampleElement,
|
||||
context.createJAXBIntrospector(),
|
||||
wrapperOptions
|
||||
);
|
||||
}
|
||||
else {
|
||||
transformer = createTransformer(
|
||||
valueClass,
|
||||
"value",
|
||||
exampleElement,
|
||||
context.createJAXBIntrospector(),
|
||||
wrapperOptions
|
||||
);
|
||||
}
|
||||
for ( Object o : array ) {
|
||||
list.add( transformer.toJAXBElement( o ) );
|
||||
}
|
||||
collectionWrapper = new CollectionWrapper( list );
|
||||
}
|
||||
else {
|
||||
// Primitive arrays get a special treatment
|
||||
final int length = Array.getLength( value );
|
||||
final List<Object> list = new ArrayList<>( length );
|
||||
final JavaTypeJAXBElementTransformer transformer = new JavaTypeJAXBElementTransformer(
|
||||
( (BasicPluralJavaType<?>) javaType ).getElementJavaType(),
|
||||
"value"
|
||||
);
|
||||
for ( int i = 0; i < length; i++ ) {
|
||||
list.add( transformer.toJAXBElement( Array.get( value, i ) ) );
|
||||
}
|
||||
collectionWrapper = new CollectionWrapper( list );
|
||||
}
|
||||
createMarshaller( context ).marshal( collectionWrapper, stringWriter );
|
||||
}
|
||||
else {
|
||||
|
@ -232,6 +372,54 @@ public class JaxbXmlFormatMapper implements FormatMapper {
|
|||
}
|
||||
}
|
||||
|
||||
private JAXBElementTransformer createTransformer(
|
||||
Class<?> elementClass,
|
||||
String tagName,
|
||||
Object exampleElement,
|
||||
JAXBIntrospector introspector,
|
||||
WrapperOptions wrapperOptions) {
|
||||
final JavaType<Object> elementJavaType = wrapperOptions.getSessionFactory()
|
||||
.getTypeConfiguration()
|
||||
.getJavaTypeRegistry()
|
||||
.findDescriptor( elementClass );
|
||||
if ( exampleElement == null && ( elementJavaType == null || elementJavaType instanceof UnknownBasicJavaType<?> ) ) {
|
||||
try {
|
||||
final Constructor<?> declaredConstructor = elementClass.getDeclaredConstructor();
|
||||
exampleElement = declaredConstructor.newInstance();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
final QName elementName = exampleElement == null ? null : introspector.getElementName( exampleElement );
|
||||
if ( elementName == null && elementClass != String.class && elementJavaType != null ) {
|
||||
return createTransformer( elementJavaType, tagName, exampleElement, introspector, wrapperOptions );
|
||||
}
|
||||
return new SimpleJAXBElementTransformer( elementClass, tagName );
|
||||
}
|
||||
|
||||
private JAXBElementTransformer createTransformer(
|
||||
JavaType<?> elementJavaType,
|
||||
String tagName,
|
||||
Object exampleElement,
|
||||
JAXBIntrospector introspector,
|
||||
WrapperOptions wrapperOptions) {
|
||||
if ( exampleElement == null && elementJavaType instanceof UnknownBasicJavaType<?> ) {
|
||||
try {
|
||||
final Constructor<?> declaredConstructor = elementJavaType.getJavaTypeClass().getDeclaredConstructor();
|
||||
exampleElement = declaredConstructor.newInstance();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
final QName elementName = exampleElement == null ? null : introspector.getElementName( exampleElement );
|
||||
if ( elementName == null && elementJavaType.getJavaTypeClass() != String.class ) {
|
||||
return new JavaTypeJAXBElementTransformer( elementJavaType, tagName );
|
||||
}
|
||||
return new SimpleJAXBElementTransformer( elementJavaType.getJavaTypeClass(), tagName );
|
||||
}
|
||||
|
||||
private Marshaller createMarshaller(JAXBContext context) throws JAXBException {
|
||||
final Marshaller marshaller = context.createMarshaller();
|
||||
marshaller.setProperty( Marshaller.JAXB_FRAGMENT, true );
|
||||
|
@ -241,19 +429,88 @@ public class JaxbXmlFormatMapper implements FormatMapper {
|
|||
@XmlRootElement(name = "Map")
|
||||
public static class MapWrapper {
|
||||
@XmlAnyElement
|
||||
List<Object> elements = new ArrayList<>();
|
||||
Collection<Object> elements;
|
||||
|
||||
public MapWrapper() {
|
||||
this.elements = new ArrayList<>();
|
||||
}
|
||||
|
||||
public MapWrapper(Collection<Object> elements) {
|
||||
this.elements = elements;
|
||||
}
|
||||
}
|
||||
|
||||
@XmlRootElement(name = "Collection")
|
||||
public static class CollectionWrapper {
|
||||
@XmlAnyElement
|
||||
List<Object> elements = new ArrayList<>();
|
||||
Collection<Object> elements;
|
||||
|
||||
public CollectionWrapper() {
|
||||
this.elements = new ArrayList<>();
|
||||
}
|
||||
|
||||
public CollectionWrapper(List<Object> elements) {
|
||||
public CollectionWrapper(Collection<Object> elements) {
|
||||
this.elements = elements;
|
||||
}
|
||||
}
|
||||
|
||||
private static interface JAXBElementTransformer {
|
||||
JAXBElement<?> toJAXBElement(Object o);
|
||||
Object fromJAXBElement(Object element, Unmarshaller unmarshaller) throws JAXBException;
|
||||
}
|
||||
|
||||
private static class SimpleJAXBElementTransformer implements JAXBElementTransformer {
|
||||
private final Class<Object> elementClass;
|
||||
private final QName tagName;
|
||||
|
||||
public SimpleJAXBElementTransformer(Class<?> elementClass, String tagName) {
|
||||
//noinspection unchecked
|
||||
this.elementClass = (Class<Object>) elementClass;
|
||||
this.tagName = new QName( tagName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public JAXBElement<?> toJAXBElement(Object o) {
|
||||
return new JAXBElement<>( tagName, elementClass, o );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fromJAXBElement(Object element, Unmarshaller unmarshaller) throws JAXBException {
|
||||
final Object valueElement = unmarshaller.unmarshal( (Node) element, elementClass ).getValue();
|
||||
final Object value;
|
||||
if ( valueElement instanceof Element ) {
|
||||
value = ( (Element) valueElement ).getFirstChild().getTextContent();
|
||||
}
|
||||
else {
|
||||
value = valueElement;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private static class JavaTypeJAXBElementTransformer implements JAXBElementTransformer {
|
||||
private final JavaType<Object> elementJavaType;
|
||||
private final QName tagName;
|
||||
|
||||
public JavaTypeJAXBElementTransformer(JavaType<?> elementJavaType, String tagName) {
|
||||
//noinspection unchecked
|
||||
this.elementJavaType = (JavaType<Object>) elementJavaType;
|
||||
this.tagName = new QName( tagName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public JAXBElement<?> toJAXBElement(Object o) {
|
||||
return new JAXBElement<>(
|
||||
tagName,
|
||||
String.class,
|
||||
o == null ? null : elementJavaType.toString( o )
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fromJAXBElement(Object element, Unmarshaller unmarshaller) throws JAXBException {
|
||||
final String value = unmarshaller.unmarshal( (Node) element, String.class ).getValue();
|
||||
return value == null ? null : elementJavaType.fromString( value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1252,9 +1252,8 @@ public final class StandardBasicTypes {
|
|||
basicTypeRegistry.primed();
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static void handle(
|
||||
BasicType type,
|
||||
BasicType<?> type,
|
||||
String legacyTypeClassName,
|
||||
BasicTypeRegistry basicTypeRegistry,
|
||||
String... registrationKeys) {
|
||||
|
|
|
@ -65,6 +65,11 @@ public class AttributeConverterJdbcTypeAdapter implements JdbcType {
|
|||
return "AttributeConverterSqlTypeDescriptorAdapter(" + converter.getClass().getName() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return delegate.getPreferredJavaTypeClass( options );
|
||||
}
|
||||
|
||||
|
||||
// Binding ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||
import org.hibernate.type.BasicArrayType;
|
||||
import org.hibernate.type.BasicPluralType;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
public abstract class AbstractArrayJavaType<T, E> extends AbstractClassJavaType<T>
|
||||
implements BasicPluralJavaType<E> {
|
||||
|
||||
private final JavaType<E> componentJavaType;
|
||||
|
||||
public AbstractArrayJavaType(Class<T> clazz, BasicType<E> baseDescriptor, MutabilityPlan<T> mutabilityPlan) {
|
||||
this( clazz, baseDescriptor.getJavaTypeDescriptor(), mutabilityPlan );
|
||||
}
|
||||
|
||||
public AbstractArrayJavaType(Class<T> clazz, JavaType<E> baseDescriptor, MutabilityPlan<T> mutabilityPlan) {
|
||||
super( clazz, mutabilityPlan );
|
||||
this.componentJavaType = baseDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<E> getElementJavaType() {
|
||||
return componentJavaType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) {
|
||||
final int preferredSqlTypeCodeForArray = indicators.getPreferredSqlTypeCodeForArray();
|
||||
// Always determine the recommended type to make sure this is a valid basic java type
|
||||
final JdbcType recommendedComponentJdbcType = componentJavaType.getRecommendedJdbcType( indicators );
|
||||
final TypeConfiguration typeConfiguration = indicators.getTypeConfiguration();
|
||||
final JdbcType jdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( preferredSqlTypeCodeForArray );
|
||||
if ( jdbcType instanceof ArrayJdbcType ) {
|
||||
return ( (ArrayJdbcType) jdbcType ).resolveType(
|
||||
typeConfiguration,
|
||||
typeConfiguration.getServiceRegistry()
|
||||
.getService( JdbcServices.class )
|
||||
.getDialect(),
|
||||
recommendedComponentJdbcType,
|
||||
ColumnTypeInformation.EMPTY
|
||||
);
|
||||
}
|
||||
return jdbcType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicType<?> resolveType(
|
||||
TypeConfiguration typeConfiguration,
|
||||
Dialect dialect,
|
||||
BasicType<E> elementType,
|
||||
ColumnTypeInformation columnTypeInformation) {
|
||||
final Class<?> elementJavaTypeClass = elementType.getJavaTypeDescriptor().getJavaTypeClass();
|
||||
if ( elementType instanceof BasicPluralType<?, ?> || elementJavaTypeClass != null && elementJavaTypeClass.isArray() ) {
|
||||
return null;
|
||||
}
|
||||
return typeConfiguration.standardBasicTypeForJavaType(
|
||||
getJavaType(),
|
||||
javaType -> {
|
||||
JdbcType arrayJdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.ARRAY );
|
||||
if ( arrayJdbcType instanceof ArrayJdbcType ) {
|
||||
arrayJdbcType = ( (ArrayJdbcType) arrayJdbcType ).resolveType(
|
||||
typeConfiguration,
|
||||
dialect,
|
||||
elementType,
|
||||
columnTypeInformation
|
||||
);
|
||||
}
|
||||
//noinspection unchecked,rawtypes
|
||||
return new BasicArrayType( elementType, arrayJdbcType, javaType );
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,368 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.SharedSessionContract;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.BinaryStream;
|
||||
import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
|
||||
import org.hibernate.internal.util.SerializationHelper;
|
||||
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||
import org.hibernate.type.BasicArrayType;
|
||||
import org.hibernate.type.BasicPluralType;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* Descriptor for {@code T[]} handling.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
* @author Jordan Gigov
|
||||
*/
|
||||
public class ArrayJavaType<T> extends AbstractArrayJavaType<T[], T> {
|
||||
|
||||
public ArrayJavaType(BasicType<T> baseDescriptor) {
|
||||
this( baseDescriptor.getJavaTypeDescriptor() );
|
||||
}
|
||||
|
||||
public ArrayJavaType(JavaType<T> baseDescriptor) {
|
||||
super(
|
||||
(Class<T[]>) Array.newInstance( baseDescriptor.getJavaTypeClass(), 0 ).getClass(),
|
||||
baseDescriptor,
|
||||
new ArrayMutabilityPlan<>( baseDescriptor )
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicType<?> resolveType(
|
||||
TypeConfiguration typeConfiguration,
|
||||
Dialect dialect,
|
||||
BasicType<T> elementType,
|
||||
ColumnTypeInformation columnTypeInformation) {
|
||||
final Class<?> elementJavaTypeClass = elementType.getJavaTypeDescriptor().getJavaTypeClass();
|
||||
if ( elementType instanceof BasicPluralType<?, ?> || elementJavaTypeClass != null && elementJavaTypeClass.isArray() ) {
|
||||
return null;
|
||||
}
|
||||
final ArrayJavaType<T> arrayJavaType;
|
||||
if ( getElementJavaType() == elementType.getJavaTypeDescriptor() ) {
|
||||
arrayJavaType = this;
|
||||
}
|
||||
else {
|
||||
arrayJavaType = new ArrayJavaType<>( elementType.getJavaTypeDescriptor() );
|
||||
// Register the array type as that will be resolved in the next step
|
||||
typeConfiguration.getJavaTypeRegistry().addDescriptor( arrayJavaType );
|
||||
}
|
||||
return typeConfiguration.standardBasicTypeForJavaType(
|
||||
arrayJavaType.getJavaType(),
|
||||
javaType -> {
|
||||
JdbcType arrayJdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.ARRAY );
|
||||
if ( arrayJdbcType instanceof ArrayJdbcType ) {
|
||||
arrayJdbcType = ( (ArrayJdbcType) arrayJdbcType ).resolveType(
|
||||
typeConfiguration,
|
||||
dialect,
|
||||
elementType,
|
||||
columnTypeInformation
|
||||
);
|
||||
}
|
||||
return new BasicArrayType<>( elementType, arrayJdbcType, javaType );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String extractLoggableRepresentation(T[] value) {
|
||||
if ( value == null ) {
|
||||
return "null";
|
||||
}
|
||||
int iMax = value.length - 1;
|
||||
if ( iMax == -1 ) {
|
||||
return "[]";
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append( '[' );
|
||||
for ( int i = 0; ; i++ ) {
|
||||
sb.append( getElementJavaType().extractLoggableRepresentation( value[i] ) );
|
||||
if ( i == iMax ) {
|
||||
return sb.append( ']' ).toString();
|
||||
}
|
||||
sb.append( ", " );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areEqual(T[] one, T[] another) {
|
||||
if ( one == null && another == null ) {
|
||||
return true;
|
||||
}
|
||||
if ( one == null || another == null ) {
|
||||
return false;
|
||||
}
|
||||
if ( one.length != another.length ) {
|
||||
return false;
|
||||
}
|
||||
int l = one.length;
|
||||
for ( int i = 0; i < l; i++ ) {
|
||||
if ( !getElementJavaType().areEqual( one[i], another[i] )) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extractHashCode(T[] value) {
|
||||
if ( value == null ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = 1;
|
||||
|
||||
for ( T element : value ) {
|
||||
result = 31 * result + ( element == null ? 0 : getElementJavaType().extractHashCode( element ) );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(T[] value) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append( '{' );
|
||||
String glue = "";
|
||||
for ( T v : value ) {
|
||||
sb.append( glue );
|
||||
if ( v == null ) {
|
||||
sb.append( "null" );
|
||||
glue = ",";
|
||||
continue;
|
||||
}
|
||||
sb.append( '"' );
|
||||
String valstr = getElementJavaType().toString( v );
|
||||
// using replaceAll is a shorter, but much slower way to do this
|
||||
for (int i = 0, len = valstr.length(); i < len; i ++ ) {
|
||||
char c = valstr.charAt( i );
|
||||
// Surrogate pairs. This is how they're done.
|
||||
if (c == '\\' || c == '"') {
|
||||
sb.append( '\\' );
|
||||
}
|
||||
sb.append( c );
|
||||
}
|
||||
sb.append( '"' );
|
||||
glue = ",";
|
||||
}
|
||||
sb.append( '}' );
|
||||
final String result = sb.toString();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T[] fromString(CharSequence charSequence) {
|
||||
if ( charSequence == null ) {
|
||||
return null;
|
||||
}
|
||||
java.util.ArrayList<String> lst = new java.util.ArrayList<>();
|
||||
StringBuilder sb = null;
|
||||
char lastChar = charSequence.charAt( charSequence.length() - 1 );
|
||||
char firstChar = charSequence.charAt( 0 );
|
||||
if ( firstChar != '{' || lastChar != '}' ) {
|
||||
throw new IllegalArgumentException( "Cannot parse given string into array of strings. First and last character must be { and }" );
|
||||
}
|
||||
int len = charSequence.length();
|
||||
boolean inquote = false;
|
||||
for ( int i = 1; i < len; i ++ ) {
|
||||
char c = charSequence.charAt( i );
|
||||
if ( c == '"' ) {
|
||||
if (inquote) {
|
||||
lst.add( sb.toString() );
|
||||
}
|
||||
else {
|
||||
sb = new StringBuilder();
|
||||
}
|
||||
inquote = !inquote;
|
||||
continue;
|
||||
}
|
||||
else if ( !inquote ) {
|
||||
if ( Character.isWhitespace( c ) ) {
|
||||
continue;
|
||||
}
|
||||
else if ( c == ',' ) {
|
||||
// treat no-value between commas to mean null
|
||||
if ( sb == null ) {
|
||||
lst.add( null );
|
||||
}
|
||||
else {
|
||||
sb = null;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
// i + 4, because there has to be a comma or closing brace after null
|
||||
if ( i + 4 < len
|
||||
&& charSequence.charAt( i ) == 'n'
|
||||
&& charSequence.charAt( i + 1 ) == 'u'
|
||||
&& charSequence.charAt( i + 2 ) == 'l'
|
||||
&& charSequence.charAt( i + 3 ) == 'l') {
|
||||
lst.add( null );
|
||||
i += 4;
|
||||
continue;
|
||||
}
|
||||
if (i + 1 == len) {
|
||||
break;
|
||||
}
|
||||
throw new IllegalArgumentException( "Cannot parse given string into array of strings."
|
||||
+ " Outside of quote, but neither whitespace, comma, array end, nor null found." );
|
||||
}
|
||||
}
|
||||
else if ( c == '\\' && i + 2 < len && (charSequence.charAt( i + 1 ) == '\\' || charSequence.charAt( i + 1 ) == '"')) {
|
||||
c = charSequence.charAt( ++i );
|
||||
}
|
||||
// If there is ever a null-pointer here, the if-else logic before is incomplete
|
||||
sb.append( c );
|
||||
}
|
||||
//noinspection unchecked
|
||||
final T[] result = (T[]) Array.newInstance( getElementJavaType().getJavaTypeClass(), lst.size() );
|
||||
for ( int i = 0; i < result.length; i ++ ) {
|
||||
if ( lst.get( i ) != null ) {
|
||||
result[i] = getElementJavaType().fromString( lst.get( i ) );
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X unwrap(T[] value, Class<X> type, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( type.isInstance( value ) ) {
|
||||
//noinspection unchecked
|
||||
return (X) value;
|
||||
}
|
||||
else if ( type == byte[].class ) {
|
||||
// byte[] can only be requested if the value should be serialized
|
||||
return (X) SerializationHelper.serialize( value );
|
||||
}
|
||||
else if ( type == BinaryStream.class ) {
|
||||
// BinaryStream can only be requested if the value should be serialized
|
||||
//noinspection unchecked
|
||||
return (X) new BinaryStreamImpl( SerializationHelper.serialize( value ) );
|
||||
}
|
||||
else if ( type.isArray() ) {
|
||||
final Class<?> preferredJavaTypeClass = type.getComponentType();
|
||||
final Object[] unwrapped = (Object[]) Array.newInstance( preferredJavaTypeClass, value.length );
|
||||
for ( int i = 0; i < value.length; i++ ) {
|
||||
unwrapped[i] = getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options );
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (X) unwrapped;
|
||||
}
|
||||
|
||||
throw unknownUnwrap( type );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> T[] wrap(X value, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( value instanceof java.sql.Array ) {
|
||||
try {
|
||||
//noinspection unchecked
|
||||
value = (X) ( (java.sql.Array) value ).getArray();
|
||||
}
|
||||
catch ( SQLException ex ) {
|
||||
// This basically shouldn't happen unless you've lost connection to the database.
|
||||
throw new HibernateException( ex );
|
||||
}
|
||||
}
|
||||
|
||||
if ( value instanceof Object[] ) {
|
||||
final Object[] raw = (Object[]) value;
|
||||
final Class<T> componentClass = getElementJavaType().getJavaTypeClass();
|
||||
//noinspection unchecked
|
||||
final T[] wrapped = (T[]) java.lang.reflect.Array.newInstance( componentClass, raw.length );
|
||||
if ( componentClass.isAssignableFrom( value.getClass().getComponentType() ) ) {
|
||||
for (int i = 0; i < raw.length; i++) {
|
||||
//noinspection unchecked
|
||||
wrapped[i] = (T) raw[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
for ( int i = 0; i < raw.length; i++ ) {
|
||||
wrapped[i] = getElementJavaType().wrap( raw[i], options );
|
||||
}
|
||||
}
|
||||
return wrapped;
|
||||
}
|
||||
else if ( value instanceof byte[] ) {
|
||||
// When the value is a byte[], this is a deserialization request
|
||||
//noinspection unchecked
|
||||
return (T[]) SerializationHelper.deserialize( (byte[]) value );
|
||||
}
|
||||
else if ( value instanceof BinaryStream ) {
|
||||
// When the value is a BinaryStream, this is a deserialization request
|
||||
//noinspection unchecked
|
||||
return (T[]) SerializationHelper.deserialize( ( (BinaryStream) value ).getBytes() );
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
||||
private static class ArrayMutabilityPlan<T> implements MutabilityPlan<T[]> {
|
||||
|
||||
private final Class<T> componentClass;
|
||||
private final MutabilityPlan<T> componentPlan;
|
||||
|
||||
public ArrayMutabilityPlan(JavaType<T> baseDescriptor) {
|
||||
this.componentClass = baseDescriptor.getJavaTypeClass();
|
||||
this.componentPlan = baseDescriptor.getMutabilityPlan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T[] deepCopy(T[] value) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
//noinspection unchecked
|
||||
T[] copy = (T[]) Array.newInstance( componentClass, value.length );
|
||||
for ( int i = 0; i < value.length; i ++ ) {
|
||||
copy[ i ] = componentPlan.deepCopy( value[ i ] );
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable disassemble(T[] value, SharedSessionContract session) {
|
||||
return deepCopy( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public T[] assemble(Serializable cached, SharedSessionContract session) {
|
||||
//noinspection unchecked
|
||||
return deepCopy( (T[]) cached );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.type.descriptor.java;
|
||||
|
||||
import org.hibernate.type.descriptor.jdbc.AdjustableJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeJavaClassMappings;
|
||||
|
@ -24,9 +25,13 @@ public interface BasicJavaType<T> extends JavaType<T> {
|
|||
*/
|
||||
default JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) {
|
||||
// match legacy behavior
|
||||
return indicators.getTypeConfiguration().getJdbcTypeRegistry().getDescriptor(
|
||||
final JdbcType descriptor = indicators.getTypeConfiguration().getJdbcTypeRegistry().getDescriptor(
|
||||
JdbcTypeJavaClassMappings.INSTANCE.determineJdbcTypeCodeForJavaClass( getJavaTypeClass() )
|
||||
);
|
||||
if ( descriptor instanceof AdjustableJdbcType ) {
|
||||
return ( (AdjustableJdbcType) descriptor ).resolveIndicatedType( indicators, this );
|
||||
}
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* Descriptor for a basic plural Java type.
|
||||
* A basic plural type represents a type, that is mapped to a single column instead of multiple rows.
|
||||
* This is used for array or collection types, that are backed by e.g. SQL array or JSON/XML DDL types.
|
||||
*
|
||||
* The interface can be implemented by a plural java type e.g. {@link org.hibernate.type.descriptor.java.spi.BasicCollectionJavaType}
|
||||
* and provides access to the element java type, as well as a hook to resolve the {@link BasicType} based on the element {@link BasicType},
|
||||
* in order to gain enough information to implement storage and retrieval of the composite data type via JDBC.
|
||||
*
|
||||
* @see org.hibernate.type.descriptor.java.spi.BasicCollectionJavaType
|
||||
*/
|
||||
@Incubating
|
||||
public interface BasicPluralJavaType<T> extends Serializable {
|
||||
/**
|
||||
* Get the Java type descriptor for the element type
|
||||
*/
|
||||
JavaType<T> getElementJavaType();
|
||||
/**
|
||||
* Creates a container type for the given element type
|
||||
*/
|
||||
BasicType<?> resolveType(
|
||||
TypeConfiguration typeConfiguration,
|
||||
Dialect dialect,
|
||||
BasicType<T> elementType,
|
||||
ColumnTypeInformation columnTypeInformation);
|
||||
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.SharedSessionContract;
|
||||
import org.hibernate.engine.jdbc.BinaryStream;
|
||||
import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
|
||||
import org.hibernate.internal.util.SerializationHelper;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
|
||||
/**
|
||||
* Descriptor for {@code boolean[]} handling.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class BooleanPrimitiveArrayJavaType extends AbstractArrayJavaType<boolean[], Boolean> {
|
||||
|
||||
public static final BooleanPrimitiveArrayJavaType INSTANCE = new BooleanPrimitiveArrayJavaType();
|
||||
|
||||
private BooleanPrimitiveArrayJavaType() {
|
||||
this( BooleanJavaType.INSTANCE );
|
||||
}
|
||||
|
||||
protected BooleanPrimitiveArrayJavaType(JavaType<Boolean> baseDescriptor) {
|
||||
super( boolean[].class, baseDescriptor, new ArrayMutabilityPlan() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String extractLoggableRepresentation(boolean[] value) {
|
||||
return value == null ? super.extractLoggableRepresentation( null ) : Arrays.toString( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areEqual(boolean[] one, boolean[] another) {
|
||||
return Arrays.equals( one, another );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extractHashCode(boolean[] value) {
|
||||
return Arrays.hashCode( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(boolean[] value) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append( '{' );
|
||||
sb.append( value[0] );
|
||||
for ( int i = 1; i < value.length; i++ ) {
|
||||
sb.append( value[i] );
|
||||
sb.append( ',' );
|
||||
}
|
||||
sb.append( '}' );
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean[] fromString(CharSequence charSequence) {
|
||||
if ( charSequence == null ) {
|
||||
return null;
|
||||
}
|
||||
final List<Boolean> list = new ArrayList<>();
|
||||
final char lastChar = charSequence.charAt( charSequence.length() - 1 );
|
||||
final char firstChar = charSequence.charAt( 0 );
|
||||
if ( firstChar != '{' || lastChar != '}' ) {
|
||||
throw new IllegalArgumentException( "Cannot parse given string into array of strings. First and last character must be { and }" );
|
||||
}
|
||||
final int len = charSequence.length();
|
||||
int elementStart = 1;
|
||||
for ( int i = elementStart; i < len; i ++ ) {
|
||||
final char c = charSequence.charAt( i );
|
||||
if ( c == ',' ) {
|
||||
list.add( Boolean.parseBoolean( charSequence.subSequence( elementStart, i ).toString() ) );
|
||||
elementStart = i + 1;
|
||||
}
|
||||
}
|
||||
final boolean[] result = new boolean[list.size()];
|
||||
for ( int i = 0; i < result.length; i ++ ) {
|
||||
result[ i ] = list.get( i );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X unwrap(boolean[] value, Class<X> type, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( type.isInstance( value ) ) {
|
||||
return (X) value;
|
||||
}
|
||||
else if ( Object[].class.isAssignableFrom( type ) ) {
|
||||
final Class<?> preferredJavaTypeClass = type.getComponentType();
|
||||
final Object[] unwrapped = (Object[]) Array.newInstance( preferredJavaTypeClass, value.length );
|
||||
for ( int i = 0; i < value.length; i++ ) {
|
||||
unwrapped[i] = getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options );
|
||||
}
|
||||
return (X) unwrapped;
|
||||
}
|
||||
else if ( type == byte[].class ) {
|
||||
// byte[] can only be requested if the value should be serialized
|
||||
return (X) SerializationHelper.serialize( value );
|
||||
}
|
||||
else if ( type == BinaryStream.class ) {
|
||||
// BinaryStream can only be requested if the value should be serialized
|
||||
//noinspection unchecked
|
||||
return (X) new BinaryStreamImpl( SerializationHelper.serialize( value ) );
|
||||
}
|
||||
else if ( type.isArray() ) {
|
||||
final Class<?> preferredJavaTypeClass = type.getComponentType();
|
||||
final Object unwrapped = Array.newInstance( preferredJavaTypeClass, value.length );
|
||||
for ( int i = 0; i < value.length; i++ ) {
|
||||
Array.set( unwrapped, i, getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ) );
|
||||
}
|
||||
return (X) unwrapped;
|
||||
}
|
||||
|
||||
throw unknownUnwrap( type );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> boolean[] wrap(X value, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( value instanceof java.sql.Array ) {
|
||||
try {
|
||||
//noinspection unchecked
|
||||
value = (X) ( (java.sql.Array) value ).getArray();
|
||||
}
|
||||
catch ( SQLException ex ) {
|
||||
// This basically shouldn't happen unless you've lost connection to the database.
|
||||
throw new HibernateException( ex );
|
||||
}
|
||||
}
|
||||
|
||||
if ( value instanceof boolean[] ) {
|
||||
return (boolean[]) value;
|
||||
}
|
||||
else if ( value instanceof byte[] ) {
|
||||
// When the value is a byte[], this is a deserialization request
|
||||
return (boolean[]) SerializationHelper.deserialize( (byte[]) value );
|
||||
}
|
||||
else if ( value instanceof BinaryStream ) {
|
||||
// When the value is a BinaryStream, this is a deserialization request
|
||||
return (boolean[]) SerializationHelper.deserialize( ( (BinaryStream) value ).getBytes() );
|
||||
}
|
||||
else if ( value.getClass().isArray() ) {
|
||||
final boolean[] wrapped = new boolean[Array.getLength( value )];
|
||||
for ( int i = 0; i < wrapped.length; i++ ) {
|
||||
wrapped[i] = getElementJavaType().wrap( Array.get( value, i ), options );
|
||||
}
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
||||
private static class ArrayMutabilityPlan implements MutabilityPlan<boolean[]> {
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean[] deepCopy(boolean[] value) {
|
||||
return value == null ? null : value.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable disassemble(boolean[] value, SharedSessionContract session) {
|
||||
return deepCopy( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean[] assemble(Serializable cached, SharedSessionContract session) {
|
||||
return deepCopy( (boolean[]) cached );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.SharedSessionContract;
|
||||
import org.hibernate.engine.jdbc.BinaryStream;
|
||||
import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
|
||||
import org.hibernate.internal.util.SerializationHelper;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
|
||||
/**
|
||||
* Descriptor for {@code double[]} handling.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class DoublePrimitiveArrayJavaType extends AbstractArrayJavaType<double[], Double> {
|
||||
|
||||
public static final DoublePrimitiveArrayJavaType INSTANCE = new DoublePrimitiveArrayJavaType();
|
||||
|
||||
private DoublePrimitiveArrayJavaType() {
|
||||
this( DoubleJavaType.INSTANCE );
|
||||
}
|
||||
|
||||
protected DoublePrimitiveArrayJavaType(JavaType<Double> baseDescriptor) {
|
||||
super( double[].class, baseDescriptor, new ArrayMutabilityPlan() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String extractLoggableRepresentation(double[] value) {
|
||||
return value == null ? super.extractLoggableRepresentation( null ) : Arrays.toString( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areEqual(double[] one, double[] another) {
|
||||
return Arrays.equals( one, another );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extractHashCode(double[] value) {
|
||||
return Arrays.hashCode( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(double[] value) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append( '{' );
|
||||
sb.append( value[0] );
|
||||
for ( int i = 1; i < value.length; i++ ) {
|
||||
sb.append( value[i] );
|
||||
sb.append( ',' );
|
||||
}
|
||||
sb.append( '}' );
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] fromString(CharSequence charSequence) {
|
||||
if ( charSequence == null ) {
|
||||
return null;
|
||||
}
|
||||
final List<Double> list = new ArrayList<>();
|
||||
final char lastChar = charSequence.charAt( charSequence.length() - 1 );
|
||||
final char firstChar = charSequence.charAt( 0 );
|
||||
if ( firstChar != '{' || lastChar != '}' ) {
|
||||
throw new IllegalArgumentException( "Cannot parse given string into array of strings. First and last character must be { and }" );
|
||||
}
|
||||
final int len = charSequence.length();
|
||||
int elementStart = 1;
|
||||
for ( int i = elementStart; i < len; i ++ ) {
|
||||
final char c = charSequence.charAt( i );
|
||||
if ( c == ',' ) {
|
||||
list.add( Double.parseDouble( charSequence.subSequence( elementStart, i ).toString() ) );
|
||||
elementStart = i + 1;
|
||||
}
|
||||
}
|
||||
final double[] result = new double[list.size()];
|
||||
for ( int i = 0; i < result.length; i ++ ) {
|
||||
result[ i ] = list.get( i );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X unwrap(double[] value, Class<X> type, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( type.isInstance( value ) ) {
|
||||
return (X) value;
|
||||
}
|
||||
else if ( Object[].class.isAssignableFrom( type ) ) {
|
||||
final Class<?> preferredJavaTypeClass = type.getComponentType();
|
||||
final Object[] unwrapped = (Object[]) Array.newInstance( preferredJavaTypeClass, value.length );
|
||||
for ( int i = 0; i < value.length; i++ ) {
|
||||
unwrapped[i] = getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options );
|
||||
}
|
||||
return (X) unwrapped;
|
||||
}
|
||||
else if ( type == byte[].class ) {
|
||||
// byte[] can only be requested if the value should be serialized
|
||||
return (X) SerializationHelper.serialize( value );
|
||||
}
|
||||
else if ( type == BinaryStream.class ) {
|
||||
// BinaryStream can only be requested if the value should be serialized
|
||||
//noinspection unchecked
|
||||
return (X) new BinaryStreamImpl( SerializationHelper.serialize( value ) );
|
||||
}
|
||||
else if ( type.isArray() ) {
|
||||
final Class<?> preferredJavaTypeClass = type.getComponentType();
|
||||
final Object unwrapped = Array.newInstance( preferredJavaTypeClass, value.length );
|
||||
for ( int i = 0; i < value.length; i++ ) {
|
||||
Array.set( unwrapped, i, getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ) );
|
||||
}
|
||||
return (X) unwrapped;
|
||||
}
|
||||
|
||||
throw unknownUnwrap( type );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> double[] wrap(X value, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( value instanceof java.sql.Array ) {
|
||||
try {
|
||||
//noinspection unchecked
|
||||
value = (X) ( (java.sql.Array) value ).getArray();
|
||||
}
|
||||
catch ( SQLException ex ) {
|
||||
// This basically shouldn't happen unless you've lost connection to the database.
|
||||
throw new HibernateException( ex );
|
||||
}
|
||||
}
|
||||
|
||||
if ( value instanceof double[] ) {
|
||||
return (double[]) value;
|
||||
}
|
||||
else if ( value instanceof byte[] ) {
|
||||
// When the value is a byte[], this is a deserialization request
|
||||
return (double[]) SerializationHelper.deserialize( (byte[]) value );
|
||||
}
|
||||
else if ( value instanceof BinaryStream ) {
|
||||
// When the value is a BinaryStream, this is a deserialization request
|
||||
return (double[]) SerializationHelper.deserialize( ( (BinaryStream) value ).getBytes() );
|
||||
}
|
||||
else if ( value.getClass().isArray() ) {
|
||||
final double[] wrapped = new double[Array.getLength( value )];
|
||||
for ( int i = 0; i < wrapped.length; i++ ) {
|
||||
wrapped[i] = getElementJavaType().wrap( Array.get( value, i ), options );
|
||||
}
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
||||
private static class ArrayMutabilityPlan implements MutabilityPlan<double[]> {
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] deepCopy(double[] value) {
|
||||
return value == null ? null : value.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable disassemble(double[] value, SharedSessionContract session) {
|
||||
return deepCopy( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[] assemble(Serializable cached, SharedSessionContract session) {
|
||||
return deepCopy( (double[]) cached );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.SharedSessionContract;
|
||||
import org.hibernate.engine.jdbc.BinaryStream;
|
||||
import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
|
||||
import org.hibernate.internal.util.SerializationHelper;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
|
||||
/**
|
||||
* Descriptor for {@code float[]} handling.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class FloatPrimitiveArrayJavaType extends AbstractArrayJavaType<float[], Float> {
|
||||
|
||||
public static final FloatPrimitiveArrayJavaType INSTANCE = new FloatPrimitiveArrayJavaType();
|
||||
|
||||
private FloatPrimitiveArrayJavaType() {
|
||||
this( FloatJavaType.INSTANCE );
|
||||
}
|
||||
|
||||
protected FloatPrimitiveArrayJavaType(JavaType<Float> baseDescriptor) {
|
||||
super( float[].class, baseDescriptor, new ArrayMutabilityPlan() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String extractLoggableRepresentation(float[] value) {
|
||||
return value == null ? super.extractLoggableRepresentation( null ) : Arrays.toString( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areEqual(float[] one, float[] another) {
|
||||
return Arrays.equals( one, another );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extractHashCode(float[] value) {
|
||||
return Arrays.hashCode( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(float[] value) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append( '{' );
|
||||
sb.append( value[0] );
|
||||
for ( int i = 1; i < value.length; i++ ) {
|
||||
sb.append( value[i] );
|
||||
sb.append( ',' );
|
||||
}
|
||||
sb.append( '}' );
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] fromString(CharSequence charSequence) {
|
||||
if ( charSequence == null ) {
|
||||
return null;
|
||||
}
|
||||
final List<Float> list = new ArrayList<>();
|
||||
final char lastChar = charSequence.charAt( charSequence.length() - 1 );
|
||||
final char firstChar = charSequence.charAt( 0 );
|
||||
if ( firstChar != '{' || lastChar != '}' ) {
|
||||
throw new IllegalArgumentException( "Cannot parse given string into array of strings. First and last character must be { and }" );
|
||||
}
|
||||
final int len = charSequence.length();
|
||||
int elementStart = 1;
|
||||
for ( int i = elementStart; i < len; i ++ ) {
|
||||
final char c = charSequence.charAt( i );
|
||||
if ( c == ',' ) {
|
||||
list.add( Float.parseFloat( charSequence.subSequence( elementStart, i ).toString() ) );
|
||||
elementStart = i + 1;
|
||||
}
|
||||
}
|
||||
final float[] result = new float[list.size()];
|
||||
for ( int i = 0; i < result.length; i ++ ) {
|
||||
result[ i ] = list.get( i );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X unwrap(float[] value, Class<X> type, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( type.isInstance( value ) ) {
|
||||
return (X) value;
|
||||
}
|
||||
else if ( Object[].class.isAssignableFrom( type ) ) {
|
||||
final Class<?> preferredJavaTypeClass = type.getComponentType();
|
||||
final Object[] unwrapped = (Object[]) Array.newInstance( preferredJavaTypeClass, value.length );
|
||||
for ( int i = 0; i < value.length; i++ ) {
|
||||
unwrapped[i] = getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options );
|
||||
}
|
||||
return (X) unwrapped;
|
||||
}
|
||||
else if ( type == byte[].class ) {
|
||||
// byte[] can only be requested if the value should be serialized
|
||||
return (X) SerializationHelper.serialize( value );
|
||||
}
|
||||
else if ( type == BinaryStream.class ) {
|
||||
// BinaryStream can only be requested if the value should be serialized
|
||||
//noinspection unchecked
|
||||
return (X) new BinaryStreamImpl( SerializationHelper.serialize( value ) );
|
||||
}
|
||||
else if ( type.isArray() ) {
|
||||
final Class<?> preferredJavaTypeClass = type.getComponentType();
|
||||
final Object unwrapped = Array.newInstance( preferredJavaTypeClass, value.length );
|
||||
for ( int i = 0; i < value.length; i++ ) {
|
||||
Array.set( unwrapped, i, getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ) );
|
||||
}
|
||||
return (X) unwrapped;
|
||||
}
|
||||
|
||||
throw unknownUnwrap( type );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> float[] wrap(X value, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( value instanceof java.sql.Array ) {
|
||||
try {
|
||||
//noinspection unchecked
|
||||
value = (X) ( (java.sql.Array) value ).getArray();
|
||||
}
|
||||
catch ( SQLException ex ) {
|
||||
// This basically shouldn't happen unless you've lost connection to the database.
|
||||
throw new HibernateException( ex );
|
||||
}
|
||||
}
|
||||
|
||||
if ( value instanceof float[] ) {
|
||||
return (float[]) value;
|
||||
}
|
||||
else if ( value instanceof byte[] ) {
|
||||
// When the value is a byte[], this is a deserialization request
|
||||
return (float[]) SerializationHelper.deserialize( (byte[]) value );
|
||||
}
|
||||
else if ( value instanceof BinaryStream ) {
|
||||
// When the value is a BinaryStream, this is a deserialization request
|
||||
return (float[]) SerializationHelper.deserialize( ( (BinaryStream) value ).getBytes() );
|
||||
}
|
||||
else if ( value.getClass().isArray() ) {
|
||||
final float[] wrapped = new float[Array.getLength( value )];
|
||||
for ( int i = 0; i < wrapped.length; i++ ) {
|
||||
wrapped[i] = getElementJavaType().wrap( Array.get( value, i ), options );
|
||||
}
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
||||
private static class ArrayMutabilityPlan implements MutabilityPlan<float[]> {
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] deepCopy(float[] value) {
|
||||
return value == null ? null : value.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable disassemble(float[] value, SharedSessionContract session) {
|
||||
return deepCopy( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] assemble(Serializable cached, SharedSessionContract session) {
|
||||
return deepCopy( (float[]) cached );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.SharedSessionContract;
|
||||
import org.hibernate.engine.jdbc.BinaryStream;
|
||||
import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
|
||||
import org.hibernate.internal.util.SerializationHelper;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
|
||||
/**
|
||||
* Descriptor for {@code int[]} handling.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class IntegerPrimitiveArrayJavaType extends AbstractArrayJavaType<int[], Integer> {
|
||||
|
||||
public static final IntegerPrimitiveArrayJavaType INSTANCE = new IntegerPrimitiveArrayJavaType();
|
||||
|
||||
private IntegerPrimitiveArrayJavaType() {
|
||||
this( IntegerJavaType.INSTANCE );
|
||||
}
|
||||
|
||||
protected IntegerPrimitiveArrayJavaType(JavaType<Integer> baseDescriptor) {
|
||||
super( int[].class, baseDescriptor, new ArrayMutabilityPlan() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String extractLoggableRepresentation(int[] value) {
|
||||
return value == null ? super.extractLoggableRepresentation( null ) : Arrays.toString( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areEqual(int[] one, int[] another) {
|
||||
return Arrays.equals( one, another );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extractHashCode(int[] value) {
|
||||
return Arrays.hashCode( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(int[] value) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append( '{' );
|
||||
sb.append( value[0] );
|
||||
for ( int i = 1; i < value.length; i++ ) {
|
||||
sb.append( value[i] );
|
||||
sb.append( ',' );
|
||||
}
|
||||
sb.append( '}' );
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] fromString(CharSequence charSequence) {
|
||||
if ( charSequence == null ) {
|
||||
return null;
|
||||
}
|
||||
final List<Integer> list = new ArrayList<>();
|
||||
final char lastChar = charSequence.charAt( charSequence.length() - 1 );
|
||||
final char firstChar = charSequence.charAt( 0 );
|
||||
if ( firstChar != '{' || lastChar != '}' ) {
|
||||
throw new IllegalArgumentException( "Cannot parse given string into array of strings. First and last character must be { and }" );
|
||||
}
|
||||
final int len = charSequence.length();
|
||||
int elementStart = 1;
|
||||
for ( int i = elementStart; i < len; i ++ ) {
|
||||
final char c = charSequence.charAt( i );
|
||||
if ( c == ',' ) {
|
||||
list.add( Integer.parseInt( charSequence, elementStart, i, 10 ) );
|
||||
elementStart = i + 1;
|
||||
}
|
||||
}
|
||||
final int[] result = new int[list.size()];
|
||||
for ( int i = 0; i < result.length; i ++ ) {
|
||||
result[ i ] = list.get( i );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X unwrap(int[] value, Class<X> type, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( type.isInstance( value ) ) {
|
||||
return (X) value;
|
||||
}
|
||||
else if ( Object[].class.isAssignableFrom( type ) ) {
|
||||
final Class<?> preferredJavaTypeClass = type.getComponentType();
|
||||
final Object[] unwrapped = (Object[]) Array.newInstance( preferredJavaTypeClass, value.length );
|
||||
for ( int i = 0; i < value.length; i++ ) {
|
||||
unwrapped[i] = getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options );
|
||||
}
|
||||
return (X) unwrapped;
|
||||
}
|
||||
else if ( type == byte[].class ) {
|
||||
// byte[] can only be requested if the value should be serialized
|
||||
return (X) SerializationHelper.serialize( value );
|
||||
}
|
||||
else if ( type == BinaryStream.class ) {
|
||||
// BinaryStream can only be requested if the value should be serialized
|
||||
//noinspection unchecked
|
||||
return (X) new BinaryStreamImpl( SerializationHelper.serialize( value ) );
|
||||
}
|
||||
else if ( type.isArray() ) {
|
||||
final Class<?> preferredJavaTypeClass = type.getComponentType();
|
||||
final Object unwrapped = Array.newInstance( preferredJavaTypeClass, value.length );
|
||||
for ( int i = 0; i < value.length; i++ ) {
|
||||
Array.set( unwrapped, i, getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ) );
|
||||
}
|
||||
return (X) unwrapped;
|
||||
}
|
||||
|
||||
throw unknownUnwrap( type );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> int[] wrap(X value, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( value instanceof java.sql.Array ) {
|
||||
try {
|
||||
//noinspection unchecked
|
||||
value = (X) ( (java.sql.Array) value ).getArray();
|
||||
}
|
||||
catch ( SQLException ex ) {
|
||||
// This basically shouldn't happen unless you've lost connection to the database.
|
||||
throw new HibernateException( ex );
|
||||
}
|
||||
}
|
||||
|
||||
if ( value instanceof int[] ) {
|
||||
return (int[]) value;
|
||||
}
|
||||
else if ( value instanceof byte[] ) {
|
||||
// When the value is a byte[], this is a deserialization request
|
||||
return (int[]) SerializationHelper.deserialize( (byte[]) value );
|
||||
}
|
||||
else if ( value instanceof BinaryStream ) {
|
||||
// When the value is a BinaryStream, this is a deserialization request
|
||||
return (int[]) SerializationHelper.deserialize( ( (BinaryStream) value ).getBytes() );
|
||||
}
|
||||
else if ( value.getClass().isArray() ) {
|
||||
final int[] wrapped = new int[Array.getLength( value )];
|
||||
for ( int i = 0; i < wrapped.length; i++ ) {
|
||||
wrapped[i] = getElementJavaType().wrap( Array.get( value, i ), options );
|
||||
}
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
||||
private static class ArrayMutabilityPlan implements MutabilityPlan<int[]> {
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] deepCopy(int[] value) {
|
||||
return value == null ? null : value.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable disassemble(int[] value, SharedSessionContract session) {
|
||||
return deepCopy( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] assemble(Serializable cached, SharedSessionContract session) {
|
||||
return deepCopy( (int[]) cached );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ import java.lang.reflect.Type;
|
|||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.Size;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
@ -253,8 +254,24 @@ public interface JavaType<T> extends Serializable {
|
|||
/**
|
||||
* Creates the {@link JavaType} for the given {@link ParameterizedType} based on this {@link JavaType} registered
|
||||
* for the raw type.
|
||||
*
|
||||
* @deprecated Use {@link #createJavaType(ParameterizedType, TypeConfiguration)} instead
|
||||
*/
|
||||
@Deprecated(since = "6.1")
|
||||
default JavaType<T> createJavaType(ParameterizedType parameterizedType) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link JavaType} for the given {@link ParameterizedType} based on this {@link JavaType} registered
|
||||
* for the raw type.
|
||||
*
|
||||
* @since 6.1
|
||||
*/
|
||||
@Incubating
|
||||
default JavaType<T> createJavaType(
|
||||
ParameterizedType parameterizedType,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
return createJavaType( parameterizedType );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.SharedSessionContract;
|
||||
import org.hibernate.engine.jdbc.BinaryStream;
|
||||
import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
|
||||
import org.hibernate.internal.util.SerializationHelper;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
|
||||
/**
|
||||
* Descriptor for {@code long[]} handling.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class LongPrimitiveArrayJavaType extends AbstractArrayJavaType<long[], Long> {
|
||||
|
||||
public static final LongPrimitiveArrayJavaType INSTANCE = new LongPrimitiveArrayJavaType();
|
||||
|
||||
private LongPrimitiveArrayJavaType() {
|
||||
this( LongJavaType.INSTANCE );
|
||||
}
|
||||
|
||||
protected LongPrimitiveArrayJavaType(JavaType<Long> baseDescriptor) {
|
||||
super( long[].class, baseDescriptor, new ArrayMutabilityPlan() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String extractLoggableRepresentation(long[] value) {
|
||||
return value == null ? super.extractLoggableRepresentation( null ) : Arrays.toString( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areEqual(long[] one, long[] another) {
|
||||
return Arrays.equals( one, another );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extractHashCode(long[] value) {
|
||||
return Arrays.hashCode( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(long[] value) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append( '{' );
|
||||
sb.append( value[0] );
|
||||
for ( int i = 1; i < value.length; i++ ) {
|
||||
sb.append( value[i] );
|
||||
sb.append( ',' );
|
||||
}
|
||||
sb.append( '}' );
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long[] fromString(CharSequence charSequence) {
|
||||
if ( charSequence == null ) {
|
||||
return null;
|
||||
}
|
||||
final List<Long> list = new ArrayList<>();
|
||||
final char lastChar = charSequence.charAt( charSequence.length() - 1 );
|
||||
final char firstChar = charSequence.charAt( 0 );
|
||||
if ( firstChar != '{' || lastChar != '}' ) {
|
||||
throw new IllegalArgumentException( "Cannot parse given string into array of strings. First and last character must be { and }" );
|
||||
}
|
||||
final int len = charSequence.length();
|
||||
int elementStart = 1;
|
||||
for ( int i = elementStart; i < len; i ++ ) {
|
||||
final char c = charSequence.charAt( i );
|
||||
if ( c == ',' ) {
|
||||
list.add( Long.parseLong( charSequence, elementStart, i, 10 ) );
|
||||
elementStart = i + 1;
|
||||
}
|
||||
}
|
||||
final long[] result = new long[list.size()];
|
||||
for ( int i = 0; i < result.length; i ++ ) {
|
||||
result[ i ] = list.get( i );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X unwrap(long[] value, Class<X> type, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( type.isInstance( value ) ) {
|
||||
return (X) value;
|
||||
}
|
||||
else if ( Object[].class.isAssignableFrom( type ) ) {
|
||||
final Class<?> preferredJavaTypeClass = type.getComponentType();
|
||||
final Object[] unwrapped = (Object[]) Array.newInstance( preferredJavaTypeClass, value.length );
|
||||
for ( int i = 0; i < value.length; i++ ) {
|
||||
unwrapped[i] = getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options );
|
||||
}
|
||||
return (X) unwrapped;
|
||||
}
|
||||
else if ( type == byte[].class ) {
|
||||
// byte[] can only be requested if the value should be serialized
|
||||
return (X) SerializationHelper.serialize( value );
|
||||
}
|
||||
else if ( type == BinaryStream.class ) {
|
||||
// BinaryStream can only be requested if the value should be serialized
|
||||
//noinspection unchecked
|
||||
return (X) new BinaryStreamImpl( SerializationHelper.serialize( value ) );
|
||||
}
|
||||
else if ( type.isArray() ) {
|
||||
final Class<?> preferredJavaTypeClass = type.getComponentType();
|
||||
final Object unwrapped = Array.newInstance( preferredJavaTypeClass, value.length );
|
||||
for ( int i = 0; i < value.length; i++ ) {
|
||||
Array.set( unwrapped, i, getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ) );
|
||||
}
|
||||
return (X) unwrapped;
|
||||
}
|
||||
|
||||
throw unknownUnwrap( type );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> long[] wrap(X value, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( value instanceof java.sql.Array ) {
|
||||
try {
|
||||
//noinspection unchecked
|
||||
value = (X) ( (java.sql.Array) value ).getArray();
|
||||
}
|
||||
catch ( SQLException ex ) {
|
||||
// This basically shouldn't happen unless you've lost connection to the database.
|
||||
throw new HibernateException( ex );
|
||||
}
|
||||
}
|
||||
|
||||
if ( value instanceof long[] ) {
|
||||
return (long[]) value;
|
||||
}
|
||||
else if ( value instanceof byte[] ) {
|
||||
// When the value is a byte[], this is a deserialization request
|
||||
return (long[]) SerializationHelper.deserialize( (byte[]) value );
|
||||
}
|
||||
else if ( value instanceof BinaryStream ) {
|
||||
// When the value is a BinaryStream, this is a deserialization request
|
||||
return (long[]) SerializationHelper.deserialize( ( (BinaryStream) value ).getBytes() );
|
||||
}
|
||||
else if ( value.getClass().isArray() ) {
|
||||
final long[] wrapped = new long[Array.getLength( value )];
|
||||
for ( int i = 0; i < wrapped.length; i++ ) {
|
||||
wrapped[i] = getElementJavaType().wrap( Array.get( value, i ), options );
|
||||
}
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
||||
private static class ArrayMutabilityPlan implements MutabilityPlan<long[]> {
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long[] deepCopy(long[] value) {
|
||||
return value == null ? null : value.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable disassemble(long[] value, SharedSessionContract session) {
|
||||
return deepCopy( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public long[] assemble(Serializable cached, SharedSessionContract session) {
|
||||
return deepCopy( (long[]) cached );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.SharedSessionContract;
|
||||
import org.hibernate.engine.jdbc.BinaryStream;
|
||||
import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
|
||||
import org.hibernate.internal.util.SerializationHelper;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
|
||||
/**
|
||||
* Descriptor for {@code short[]} handling.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class ShortPrimitiveArrayJavaType extends AbstractArrayJavaType<short[], Short> {
|
||||
|
||||
public static final ShortPrimitiveArrayJavaType INSTANCE = new ShortPrimitiveArrayJavaType();
|
||||
|
||||
private ShortPrimitiveArrayJavaType() {
|
||||
this( ShortJavaType.INSTANCE );
|
||||
}
|
||||
|
||||
protected ShortPrimitiveArrayJavaType(JavaType<Short> baseDescriptor) {
|
||||
super( short[].class, baseDescriptor, new ArrayMutabilityPlan() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String extractLoggableRepresentation(short[] value) {
|
||||
return value == null ? super.extractLoggableRepresentation( null ) : Arrays.toString( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areEqual(short[] one, short[] another) {
|
||||
return Arrays.equals( one, another );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extractHashCode(short[] value) {
|
||||
return Arrays.hashCode( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(short[] value) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append( '{' );
|
||||
sb.append( value[0] );
|
||||
for ( int i = 1; i < value.length; i++ ) {
|
||||
sb.append( value[i] );
|
||||
sb.append( ',' );
|
||||
}
|
||||
sb.append( '}' );
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public short[] fromString(CharSequence charSequence) {
|
||||
if ( charSequence == null ) {
|
||||
return null;
|
||||
}
|
||||
final List<Short> list = new ArrayList<>();
|
||||
final char lastChar = charSequence.charAt( charSequence.length() - 1 );
|
||||
final char firstChar = charSequence.charAt( 0 );
|
||||
if ( firstChar != '{' || lastChar != '}' ) {
|
||||
throw new IllegalArgumentException( "Cannot parse given string into array of strings. First and last character must be { and }" );
|
||||
}
|
||||
final int len = charSequence.length();
|
||||
int elementStart = 1;
|
||||
for ( int i = elementStart; i < len; i ++ ) {
|
||||
final char c = charSequence.charAt( i );
|
||||
if ( c == ',' ) {
|
||||
list.add( Short.parseShort( charSequence.subSequence( elementStart, i ).toString(), 10 ) );
|
||||
elementStart = i + 1;
|
||||
}
|
||||
}
|
||||
final short[] result = new short[list.size()];
|
||||
for ( int i = 0; i < result.length; i ++ ) {
|
||||
result[ i ] = list.get( i );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X unwrap(short[] value, Class<X> type, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( type.isInstance( value ) ) {
|
||||
return (X) value;
|
||||
}
|
||||
else if ( Object[].class.isAssignableFrom( type ) ) {
|
||||
final Class<?> preferredJavaTypeClass = type.getComponentType();
|
||||
final Object[] unwrapped = (Object[]) Array.newInstance( preferredJavaTypeClass, value.length );
|
||||
for ( int i = 0; i < value.length; i++ ) {
|
||||
unwrapped[i] = getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options );
|
||||
}
|
||||
return (X) unwrapped;
|
||||
}
|
||||
else if ( type == byte[].class ) {
|
||||
// byte[] can only be requested if the value should be serialized
|
||||
return (X) SerializationHelper.serialize( value );
|
||||
}
|
||||
else if ( type == BinaryStream.class ) {
|
||||
// BinaryStream can only be requested if the value should be serialized
|
||||
//noinspection unchecked
|
||||
return (X) new BinaryStreamImpl( SerializationHelper.serialize( value ) );
|
||||
}
|
||||
else if ( type.isArray() ) {
|
||||
final Class<?> preferredJavaTypeClass = type.getComponentType();
|
||||
final Object unwrapped = Array.newInstance( preferredJavaTypeClass, value.length );
|
||||
for ( int i = 0; i < value.length; i++ ) {
|
||||
Array.set( unwrapped, i, getElementJavaType().unwrap( value[i], preferredJavaTypeClass, options ) );
|
||||
}
|
||||
return (X) unwrapped;
|
||||
}
|
||||
|
||||
throw unknownUnwrap( type );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> short[] wrap(X value, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( value instanceof java.sql.Array ) {
|
||||
try {
|
||||
//noinspection unchecked
|
||||
value = (X) ( (java.sql.Array) value ).getArray();
|
||||
}
|
||||
catch ( SQLException ex ) {
|
||||
// This basically shouldn't happen unless you've lost connection to the database.
|
||||
throw new HibernateException( ex );
|
||||
}
|
||||
}
|
||||
|
||||
if ( value instanceof short[] ) {
|
||||
return (short[]) value;
|
||||
}
|
||||
else if ( value instanceof byte[] ) {
|
||||
// When the value is a byte[], this is a deserialization request
|
||||
return (short[]) SerializationHelper.deserialize( (byte[]) value );
|
||||
}
|
||||
else if ( value instanceof BinaryStream ) {
|
||||
// When the value is a BinaryStream, this is a deserialization request
|
||||
return (short[]) SerializationHelper.deserialize( ( (BinaryStream) value ).getBytes() );
|
||||
}
|
||||
else if ( value.getClass().isArray() ) {
|
||||
final short[] wrapped = new short[Array.getLength( value )];
|
||||
for ( int i = 0; i < wrapped.length; i++ ) {
|
||||
wrapped[i] = getElementJavaType().wrap( Array.get( value, i ), options );
|
||||
}
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
||||
private static class ArrayMutabilityPlan implements MutabilityPlan<short[]> {
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short[] deepCopy(short[] value) {
|
||||
return value == null ? null : value.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable disassemble(short[] value, SharedSessionContract session) {
|
||||
return deepCopy( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public short[] assemble(Serializable cached, SharedSessionContract session) {
|
||||
return deepCopy( (short[]) cached );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,473 @@
|
|||
/*
|
||||
* 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.Array;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.SharedSessionContract;
|
||||
import org.hibernate.collection.spi.CollectionSemantics;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.BinaryStream;
|
||||
import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.internal.util.SerializationHelper;
|
||||
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||
import org.hibernate.type.BasicCollectionType;
|
||||
import org.hibernate.type.BasicPluralType;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.AbstractJavaType;
|
||||
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.MutabilityPlan;
|
||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* Descriptor for {@code Collection<T>} handling.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
@Incubating
|
||||
public class BasicCollectionJavaType<C extends Collection<E>, E> extends AbstractJavaType<C> implements
|
||||
BasicPluralJavaType<E> {
|
||||
|
||||
private final CollectionSemantics<C, E> semantics;
|
||||
private final JavaType<E> componentJavaType;
|
||||
|
||||
public BasicCollectionJavaType(ParameterizedType type, JavaType<E> componentJavaType, CollectionSemantics<C, E> semantics) {
|
||||
super( type, new CollectionMutabilityPlan<>( componentJavaType, semantics ) );
|
||||
this.semantics = semantics;
|
||||
this.componentJavaType = componentJavaType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaType<E> getElementJavaType() {
|
||||
return componentJavaType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) {
|
||||
final int preferredSqlTypeCodeForArray = indicators.getPreferredSqlTypeCodeForArray();
|
||||
// Always determine the recommended type to make sure this is a valid basic java type
|
||||
final JdbcType recommendedComponentJdbcType = componentJavaType.getRecommendedJdbcType( indicators );
|
||||
final TypeConfiguration typeConfiguration = indicators.getTypeConfiguration();
|
||||
final JdbcType jdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( preferredSqlTypeCodeForArray );
|
||||
if ( jdbcType instanceof ArrayJdbcType ) {
|
||||
return ( (ArrayJdbcType) jdbcType ).resolveType(
|
||||
typeConfiguration,
|
||||
typeConfiguration.getServiceRegistry()
|
||||
.getService( JdbcServices.class )
|
||||
.getDialect(),
|
||||
recommendedComponentJdbcType,
|
||||
ColumnTypeInformation.EMPTY
|
||||
);
|
||||
}
|
||||
return indicators.getTypeConfiguration().getJdbcTypeRegistry().getDescriptor( preferredSqlTypeCodeForArray );
|
||||
}
|
||||
|
||||
public CollectionSemantics<C, E> getSemantics() {
|
||||
return semantics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicType<?> resolveType(
|
||||
TypeConfiguration typeConfiguration,
|
||||
Dialect dialect,
|
||||
BasicType<E> elementType,
|
||||
ColumnTypeInformation columnTypeInformation) {
|
||||
final Class<?> elementJavaTypeClass = elementType.getJavaTypeDescriptor().getJavaTypeClass();
|
||||
if ( elementType instanceof BasicPluralType<?, ?> || elementJavaTypeClass != null && elementJavaTypeClass.isArray() ) {
|
||||
return null;
|
||||
}
|
||||
final BasicCollectionJavaType<C, E> collectionJavaType;
|
||||
if ( componentJavaType == elementType.getJavaTypeDescriptor() ) {
|
||||
collectionJavaType = this;
|
||||
}
|
||||
else {
|
||||
collectionJavaType = new BasicCollectionJavaType<>(
|
||||
(ParameterizedType) getJavaType(),
|
||||
elementType.getJavaTypeDescriptor(),
|
||||
semantics
|
||||
);
|
||||
// Register the collection type as that will be resolved in the next step
|
||||
typeConfiguration.getJavaTypeRegistry().addDescriptor( collectionJavaType );
|
||||
}
|
||||
return typeConfiguration.standardBasicTypeForJavaType(
|
||||
collectionJavaType.getJavaType(),
|
||||
javaType -> {
|
||||
JdbcType arrayJdbcType = typeConfiguration.getJdbcTypeRegistry().getDescriptor( Types.ARRAY );
|
||||
if ( arrayJdbcType instanceof ArrayJdbcType ) {
|
||||
arrayJdbcType = ( (ArrayJdbcType) arrayJdbcType ).resolveType(
|
||||
typeConfiguration,
|
||||
dialect,
|
||||
elementType,
|
||||
columnTypeInformation
|
||||
);
|
||||
}
|
||||
//noinspection unchecked,rawtypes
|
||||
return new BasicCollectionType( elementType, arrayJdbcType, collectionJavaType );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String extractLoggableRepresentation(C value) {
|
||||
if ( value == null ) {
|
||||
return "null";
|
||||
}
|
||||
final Iterator<E> iterator = value.iterator();
|
||||
if ( !iterator.hasNext() ) {
|
||||
return "[]";
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append( '[' );
|
||||
do {
|
||||
final E element = iterator.next();
|
||||
sb.append( componentJavaType.toString( element ) );
|
||||
if ( !iterator.hasNext() ) {
|
||||
return sb.append( ']' ).toString();
|
||||
}
|
||||
sb.append( ", " );
|
||||
} while ( true );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areEqual(C one, C another) {
|
||||
if ( one == null && another == null ) {
|
||||
return true;
|
||||
}
|
||||
if ( one == null || another == null ) {
|
||||
return false;
|
||||
}
|
||||
if ( one.size() != another.size() ) {
|
||||
return false;
|
||||
}
|
||||
switch ( semantics.getCollectionClassification() ) {
|
||||
case ARRAY:
|
||||
case LIST:
|
||||
case ORDERED_SET:
|
||||
case SORTED_SET:
|
||||
final Iterator<E> iterator1 = one.iterator();
|
||||
final Iterator<E> iterator2 = another.iterator();
|
||||
while ( iterator1.hasNext() ) {
|
||||
if ( !componentJavaType.areEqual( iterator1.next(), iterator2.next() ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
default: {
|
||||
OUTER: for ( E e1 : one ) {
|
||||
for ( E e2 : another ) {
|
||||
if ( componentJavaType.areEqual( e1, e2 ) ) {
|
||||
continue OUTER;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extractHashCode(C value) {
|
||||
int result = 0;
|
||||
if ( value != null && !value.isEmpty() ) {
|
||||
for ( E element : value ) {
|
||||
if ( element != null ) {
|
||||
result += componentJavaType.extractHashCode( element );
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(C value) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append( '{' );
|
||||
String glue = "";
|
||||
for ( E v : value ) {
|
||||
sb.append( glue );
|
||||
if ( v == null ) {
|
||||
sb.append( "null" );
|
||||
glue = ",";
|
||||
continue;
|
||||
}
|
||||
sb.append( '"' );
|
||||
String valstr = this.componentJavaType.toString( v );
|
||||
// using replaceAll is a shorter, but much slower way to do this
|
||||
for (int i = 0, len = valstr.length(); i < len; i ++ ) {
|
||||
char c = valstr.charAt( i );
|
||||
// Surrogate pairs. This is how they're done.
|
||||
if (c == '\\' || c == '"') {
|
||||
sb.append( '\\' );
|
||||
}
|
||||
sb.append( c );
|
||||
}
|
||||
sb.append( '"' );
|
||||
glue = ",";
|
||||
}
|
||||
sb.append( '}' );
|
||||
final String result = sb.toString();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public C fromString(CharSequence charSequence) {
|
||||
if ( charSequence == null ) {
|
||||
return null;
|
||||
}
|
||||
java.util.ArrayList<String> lst = new java.util.ArrayList<>();
|
||||
StringBuilder sb = null;
|
||||
char lastChar = charSequence.charAt( charSequence.length() - 1 );
|
||||
char firstChar = charSequence.charAt( 0 );
|
||||
if ( firstChar != '{' || lastChar != '}' ) {
|
||||
throw new IllegalArgumentException( "Cannot parse given string into array of strings. First and last character must be { and }" );
|
||||
}
|
||||
int len = charSequence.length();
|
||||
boolean inquote = false;
|
||||
for ( int i = 1; i < len; i ++ ) {
|
||||
char c = charSequence.charAt( i );
|
||||
if ( c == '"' ) {
|
||||
if (inquote) {
|
||||
lst.add( sb.toString() );
|
||||
}
|
||||
else {
|
||||
sb = new StringBuilder();
|
||||
}
|
||||
inquote = !inquote;
|
||||
continue;
|
||||
}
|
||||
else if ( !inquote ) {
|
||||
if ( Character.isWhitespace( c ) ) {
|
||||
continue;
|
||||
}
|
||||
else if ( c == ',' ) {
|
||||
// treat no-value between commas to mean null
|
||||
if ( sb == null ) {
|
||||
lst.add( null );
|
||||
}
|
||||
else {
|
||||
sb = null;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
// i + 4, because there has to be a comma or closing brace after null
|
||||
if ( i + 4 < len
|
||||
&& charSequence.charAt( i ) == 'n'
|
||||
&& charSequence.charAt( i + 1 ) == 'u'
|
||||
&& charSequence.charAt( i + 2 ) == 'l'
|
||||
&& charSequence.charAt( i + 3 ) == 'l') {
|
||||
lst.add( null );
|
||||
i += 4;
|
||||
continue;
|
||||
}
|
||||
if (i + 1 == len) {
|
||||
break;
|
||||
}
|
||||
throw new IllegalArgumentException( "Cannot parse given string into array of strings."
|
||||
+ " Outside of quote, but neither whitespace, comma, array end, nor null found." );
|
||||
}
|
||||
}
|
||||
else if ( c == '\\' && i + 2 < len && (charSequence.charAt( i + 1 ) == '\\' || charSequence.charAt( i + 1 ) == '"')) {
|
||||
c = charSequence.charAt( ++i );
|
||||
}
|
||||
// If there is ever a null-pointer here, the if-else logic before is incomplete
|
||||
sb.append( c );
|
||||
}
|
||||
final C result = semantics.instantiateRaw( lst.size(), null );
|
||||
for ( int i = 0; i < lst.size(); i ++ ) {
|
||||
if ( lst.get( i ) != null ) {
|
||||
result.add( componentJavaType.fromString( lst.get( i ) ) );
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> X unwrap(C value, Class<X> type, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( type.isInstance( value ) ) {
|
||||
//noinspection unchecked
|
||||
return (X) value;
|
||||
}
|
||||
else if ( type == byte[].class ) {
|
||||
// byte[] can only be requested if the value should be serialized
|
||||
return (X) SerializationHelper.serialize( asArrayList( value ) );
|
||||
}
|
||||
else if ( type == BinaryStream.class ) {
|
||||
// BinaryStream can only be requested if the value should be serialized
|
||||
//noinspection unchecked
|
||||
return (X) new BinaryStreamImpl( SerializationHelper.serialize( asArrayList( value ) ) );
|
||||
}
|
||||
else if ( Object[].class.isAssignableFrom( type ) ) {
|
||||
final Class<?> preferredJavaTypeClass = type.getComponentType();
|
||||
final Object[] unwrapped = (Object[]) Array.newInstance( preferredJavaTypeClass, value.size() );
|
||||
int i = 0;
|
||||
for ( E element : value ) {
|
||||
unwrapped[i] = componentJavaType.unwrap( element, preferredJavaTypeClass, options );
|
||||
i++;
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (X) unwrapped;
|
||||
}
|
||||
else if ( type.isArray() ) {
|
||||
final Class<?> preferredJavaTypeClass = type.getComponentType();
|
||||
//noinspection unchecked
|
||||
final X unwrapped = (X) Array.newInstance( preferredJavaTypeClass, value.size() );
|
||||
int i = 0;
|
||||
for ( E element : value ) {
|
||||
Array.set( unwrapped, i, componentJavaType.unwrap( element, preferredJavaTypeClass, options ) );
|
||||
i++;
|
||||
}
|
||||
return unwrapped;
|
||||
}
|
||||
|
||||
throw unknownUnwrap( type );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> C wrap(X value, WrapperOptions options) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( value instanceof java.sql.Array ) {
|
||||
try {
|
||||
//noinspection unchecked
|
||||
value = (X) ( (java.sql.Array) value ).getArray();
|
||||
}
|
||||
catch ( SQLException ex ) {
|
||||
// This basically shouldn't happen unless you've lost connection to the database.
|
||||
throw new HibernateException( ex );
|
||||
}
|
||||
}
|
||||
|
||||
if ( value instanceof Object[] ) {
|
||||
final Object[] raw = (Object[]) value;
|
||||
final C wrapped = semantics.instantiateRaw( raw.length, null );
|
||||
if ( componentJavaType.getJavaTypeClass().isAssignableFrom( value.getClass().getComponentType() ) ) {
|
||||
for ( Object o : raw ) {
|
||||
//noinspection unchecked
|
||||
wrapped.add( (E) o );
|
||||
}
|
||||
}
|
||||
else {
|
||||
for ( Object o : raw ) {
|
||||
wrapped.add( componentJavaType.wrap( o, options ) );
|
||||
}
|
||||
}
|
||||
return wrapped;
|
||||
}
|
||||
else if ( value instanceof byte[] ) {
|
||||
// When the value is a byte[], this is a deserialization request
|
||||
//noinspection unchecked
|
||||
return fromCollection( (ArrayList<E>) SerializationHelper.deserialize( (byte[]) value ) );
|
||||
}
|
||||
else if ( value instanceof BinaryStream ) {
|
||||
// When the value is a BinaryStream, this is a deserialization request
|
||||
//noinspection unchecked
|
||||
return fromCollection( (ArrayList<E>) SerializationHelper.deserialize( ( (BinaryStream) value ).getBytes() ) );
|
||||
}
|
||||
else if ( value instanceof Collection<?> ) {
|
||||
//noinspection unchecked
|
||||
return fromCollection( (Collection<E>) value );
|
||||
}
|
||||
else if ( value.getClass().isArray() ) {
|
||||
final int length = Array.getLength( value );
|
||||
final C wrapped = semantics.instantiateRaw( length, null );
|
||||
for ( int i = 0; i < length; i++ ) {
|
||||
wrapped.add( componentJavaType.wrap( Array.get( value, i ), options ) );
|
||||
}
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
throw unknownWrap( value.getClass() );
|
||||
}
|
||||
|
||||
private ArrayList<E> asArrayList(C value) {
|
||||
if ( value instanceof ArrayList ) {
|
||||
//noinspection unchecked
|
||||
return (ArrayList<E>) value;
|
||||
}
|
||||
return new ArrayList<>( value );
|
||||
}
|
||||
|
||||
private C fromCollection(Collection<E> value) {
|
||||
switch ( semantics.getCollectionClassification() ) {
|
||||
case LIST:
|
||||
case BAG:
|
||||
if ( value instanceof ArrayList<?> ) {
|
||||
//noinspection unchecked
|
||||
return (C) value;
|
||||
}
|
||||
default:
|
||||
final C collection = semantics.instantiateRaw( value.size(), null );
|
||||
collection.addAll( value );
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CollectionMutabilityPlan<C extends Collection<E>, E> implements MutabilityPlan<C> {
|
||||
|
||||
private final CollectionSemantics<C, E> semantics;
|
||||
private final MutabilityPlan<E> componentPlan;
|
||||
|
||||
public CollectionMutabilityPlan(JavaType<E> baseDescriptor, CollectionSemantics<C, E> semantics) {
|
||||
this.semantics = semantics;
|
||||
this.componentPlan = baseDescriptor.getMutabilityPlan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public C deepCopy(C value) {
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
final C copy = semantics.instantiateRaw( value.size(), null );
|
||||
for ( E element : value ) {
|
||||
copy.add( componentPlan.deepCopy( element ) );
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable disassemble(C value, SharedSessionContract session) {
|
||||
return (Serializable) deepCopy( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public C assemble(Serializable cached, SharedSessionContract session) {
|
||||
//noinspection unchecked
|
||||
return deepCopy( (C) cached );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ import org.hibernate.type.descriptor.java.MutabilityPlan;
|
|||
import org.hibernate.type.descriptor.java.MutableMutabilityPlan;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* Extension of the general JavaType for "collection types"
|
||||
|
@ -47,9 +48,27 @@ public class CollectionJavaType<C> extends AbstractClassJavaType<C> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public JavaType<C> createJavaType(ParameterizedType parameterizedType) {
|
||||
//noinspection unchecked
|
||||
public JavaType<C> createJavaType(
|
||||
ParameterizedType parameterizedType,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
switch ( semantics.getCollectionClassification() ) {
|
||||
case ARRAY:
|
||||
case BAG:
|
||||
case ID_BAG:
|
||||
case LIST:
|
||||
case SET:
|
||||
case SORTED_SET:
|
||||
case ORDERED_SET:
|
||||
//noinspection unchecked,rawtypes
|
||||
return new BasicCollectionJavaType(
|
||||
parameterizedType,
|
||||
typeConfiguration.getJavaTypeRegistry()
|
||||
.resolveDescriptor( parameterizedType.getActualTypeArguments()[0] ),
|
||||
semantics
|
||||
);
|
||||
}
|
||||
// Construct a basic java type that knows its parametrization
|
||||
//noinspection unchecked
|
||||
return new UnknownBasicJavaType<>( parameterizedType, (MutabilityPlan<C>) MutableMutabilityPlan.INSTANCE );
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.hibernate.type.descriptor.java.BigDecimalJavaType;
|
|||
import org.hibernate.type.descriptor.java.BigIntegerJavaType;
|
||||
import org.hibernate.type.descriptor.java.BlobJavaType;
|
||||
import org.hibernate.type.descriptor.java.BooleanJavaType;
|
||||
import org.hibernate.type.descriptor.java.BooleanPrimitiveArrayJavaType;
|
||||
import org.hibernate.type.descriptor.java.ByteArrayJavaType;
|
||||
import org.hibernate.type.descriptor.java.ByteJavaType;
|
||||
import org.hibernate.type.descriptor.java.CalendarJavaType;
|
||||
|
@ -44,12 +45,16 @@ import org.hibernate.type.descriptor.java.ClobJavaType;
|
|||
import org.hibernate.type.descriptor.java.CurrencyJavaType;
|
||||
import org.hibernate.type.descriptor.java.DateJavaType;
|
||||
import org.hibernate.type.descriptor.java.DoubleJavaType;
|
||||
import org.hibernate.type.descriptor.java.DoublePrimitiveArrayJavaType;
|
||||
import org.hibernate.type.descriptor.java.DurationJavaType;
|
||||
import org.hibernate.type.descriptor.java.FloatJavaType;
|
||||
import org.hibernate.type.descriptor.java.FloatPrimitiveArrayJavaType;
|
||||
import org.hibernate.type.descriptor.java.InetAddressJavaType;
|
||||
import org.hibernate.type.descriptor.java.InstantJavaType;
|
||||
import org.hibernate.type.descriptor.java.IntegerPrimitiveArrayJavaType;
|
||||
import org.hibernate.type.descriptor.java.IntegerJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.LongPrimitiveArrayJavaType;
|
||||
import org.hibernate.type.descriptor.java.ObjectJavaType;
|
||||
import org.hibernate.type.descriptor.java.JdbcDateJavaType;
|
||||
import org.hibernate.type.descriptor.java.JdbcTimeJavaType;
|
||||
|
@ -65,6 +70,7 @@ import org.hibernate.type.descriptor.java.OffsetTimeJavaType;
|
|||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||
import org.hibernate.type.descriptor.java.PrimitiveCharacterArrayJavaType;
|
||||
import org.hibernate.type.descriptor.java.ShortJavaType;
|
||||
import org.hibernate.type.descriptor.java.ShortPrimitiveArrayJavaType;
|
||||
import org.hibernate.type.descriptor.java.StringJavaType;
|
||||
import org.hibernate.type.descriptor.java.TimeZoneJavaType;
|
||||
import org.hibernate.type.descriptor.java.UUIDJavaType;
|
||||
|
@ -96,7 +102,6 @@ public class JavaTypeBaseline {
|
|||
/**
|
||||
* The process of registering all the baseline registrations
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void prime(BaselineTarget target) {
|
||||
primePrimitive( target, ByteJavaType.INSTANCE );
|
||||
primePrimitive( target, BooleanJavaType.INSTANCE );
|
||||
|
@ -123,6 +128,14 @@ public class JavaTypeBaseline {
|
|||
target.addBaselineDescriptor( PrimitiveByteArrayJavaType.INSTANCE );
|
||||
target.addBaselineDescriptor( PrimitiveCharacterArrayJavaType.INSTANCE );
|
||||
|
||||
// Register special ArrayJavaType implementations for primitive types
|
||||
target.addBaselineDescriptor( BooleanPrimitiveArrayJavaType.INSTANCE );
|
||||
target.addBaselineDescriptor( ShortPrimitiveArrayJavaType.INSTANCE );
|
||||
target.addBaselineDescriptor( IntegerPrimitiveArrayJavaType.INSTANCE );
|
||||
target.addBaselineDescriptor( LongPrimitiveArrayJavaType.INSTANCE );
|
||||
target.addBaselineDescriptor( FloatPrimitiveArrayJavaType.INSTANCE );
|
||||
target.addBaselineDescriptor( DoublePrimitiveArrayJavaType.INSTANCE );
|
||||
|
||||
target.addBaselineDescriptor( DurationJavaType.INSTANCE );
|
||||
target.addBaselineDescriptor( InstantJavaType.INSTANCE );
|
||||
target.addBaselineDescriptor( LocalDateJavaType.INSTANCE );
|
||||
|
@ -173,8 +186,8 @@ public class JavaTypeBaseline {
|
|||
target.addBaselineDescriptor( new CollectionJavaType( LinkedHashMap.class, StandardOrderedMapSemantics.INSTANCE ) );
|
||||
}
|
||||
|
||||
private static void primePrimitive(BaselineTarget target, JavaType descriptor) {
|
||||
private static void primePrimitive(BaselineTarget target, JavaType<?> descriptor) {
|
||||
target.addBaselineDescriptor( descriptor );
|
||||
target.addBaselineDescriptor( ( (PrimitiveJavaType) descriptor ).getPrimitiveClass(), descriptor );
|
||||
target.addBaselineDescriptor( ( (PrimitiveJavaType<?>) descriptor ).getPrimitiveClass(), descriptor );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.lang.reflect.Type;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.type.descriptor.java.ArrayJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.java.MutabilityPlan;
|
||||
import org.hibernate.type.descriptor.java.MutableMutabilityPlan;
|
||||
|
@ -136,13 +137,27 @@ public class JavaTypeRegistry implements JavaTypeBaseline.BaselineTarget, Serial
|
|||
final ParameterizedType parameterizedType = (ParameterizedType) javaType;
|
||||
final JavaType<J> rawType = findDescriptor( ( parameterizedType ).getRawType() );
|
||||
if ( rawType != null ) {
|
||||
return rawType.createJavaType( parameterizedType );
|
||||
return rawType.createJavaType( parameterizedType, typeConfiguration );
|
||||
}
|
||||
}
|
||||
return RegistryHelper.INSTANCE.createTypeDescriptor(
|
||||
javaType,
|
||||
final Type elementJavaType;
|
||||
JavaType<J> elementTypeDescriptor;
|
||||
if ( javaType instanceof Class<?> && ( (Class<?>) javaType ).isArray() ) {
|
||||
elementJavaType = ( (Class<?>) javaType ).getComponentType();
|
||||
elementTypeDescriptor = findDescriptor( elementJavaType );
|
||||
}
|
||||
else {
|
||||
elementJavaType = javaType;
|
||||
elementTypeDescriptor = null;
|
||||
}
|
||||
if ( elementTypeDescriptor == null ) {
|
||||
elementTypeDescriptor = RegistryHelper.INSTANCE.createTypeDescriptor(
|
||||
elementJavaType,
|
||||
() -> {
|
||||
final MutabilityPlan<J> determinedPlan = RegistryHelper.INSTANCE.determineMutabilityPlan( javaType, typeConfiguration );
|
||||
final MutabilityPlan<J> determinedPlan = RegistryHelper.INSTANCE.determineMutabilityPlan(
|
||||
elementJavaType,
|
||||
typeConfiguration
|
||||
);
|
||||
if ( determinedPlan != null ) {
|
||||
return determinedPlan;
|
||||
}
|
||||
|
@ -154,6 +169,12 @@ public class JavaTypeRegistry implements JavaTypeBaseline.BaselineTarget, Serial
|
|||
typeConfiguration
|
||||
);
|
||||
}
|
||||
if ( javaType != elementJavaType ) {
|
||||
//noinspection unchecked
|
||||
return (JavaType<J>) new ArrayJavaType<>( elementTypeDescriptor );
|
||||
}
|
||||
return elementTypeDescriptor;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,10 @@ public class UnknownBasicJavaType<T> extends AbstractJavaType<T> {
|
|||
|
||||
@Override
|
||||
public <X> X unwrap(T value, Class<X> type, WrapperOptions options) {
|
||||
if ( type.isAssignableFrom( getJavaTypeClass() ) ) {
|
||||
//noinspection unchecked
|
||||
return (X) value;
|
||||
}
|
||||
throw new UnsupportedOperationException(
|
||||
"Unwrap strategy not known for this Java type : " + getJavaType().getTypeName()
|
||||
);
|
||||
|
@ -59,6 +63,10 @@ public class UnknownBasicJavaType<T> extends AbstractJavaType<T> {
|
|||
|
||||
@Override
|
||||
public <X> T wrap(X value, WrapperOptions options) {
|
||||
if ( getJavaTypeClass().isInstance( value ) ) {
|
||||
//noinspection unchecked
|
||||
return (T) value;
|
||||
}
|
||||
throw new UnsupportedOperationException(
|
||||
"Wrap strategy not known for this Java type : " + getJavaType().getTypeName()
|
||||
);
|
||||
|
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* 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.jdbc;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.Size;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.ValueExtractor;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
|
||||
import org.hibernate.type.descriptor.java.BasicJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.internal.JdbcLiteralFormatterArray;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
* Descriptor for {@link Types#ARRAY ARRAY} handling.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
* @author Jordan Gigov
|
||||
*/
|
||||
public class ArrayJdbcType implements JdbcType {
|
||||
|
||||
public static final ArrayJdbcType INSTANCE = new ArrayJdbcType( ObjectJdbcType.INSTANCE );
|
||||
private static final ClassValue<Method> NAME_BINDER = new ClassValue<Method>() {
|
||||
@Override
|
||||
protected Method computeValue(Class<?> type) {
|
||||
try {
|
||||
return type.getMethod( "setArray", String.class, java.sql.Array.class );
|
||||
}
|
||||
catch ( Exception ex ) {
|
||||
// add logging? Did we get NoSuchMethodException or SecurityException?
|
||||
// Doesn't matter which. We can't use it.
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
private final JdbcType elementJdbcType;
|
||||
|
||||
public ArrayJdbcType(JdbcType elementJdbcType) {
|
||||
this.elementJdbcType = elementJdbcType;
|
||||
}
|
||||
|
||||
public JdbcType resolveType(
|
||||
TypeConfiguration typeConfiguration,
|
||||
Dialect dialect,
|
||||
BasicType<?> elementType,
|
||||
ColumnTypeInformation columnTypeInformation) {
|
||||
return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation );
|
||||
}
|
||||
|
||||
public JdbcType resolveType(
|
||||
TypeConfiguration typeConfiguration,
|
||||
Dialect dialect,
|
||||
JdbcType elementType,
|
||||
ColumnTypeInformation columnTypeInformation) {
|
||||
return new ArrayJdbcType( elementType );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getJdbcTypeCode() {
|
||||
return Types.ARRAY;
|
||||
}
|
||||
|
||||
public JdbcType getElementJdbcType() {
|
||||
return elementJdbcType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> BasicJavaType<T> getJdbcRecommendedJavaTypeMapping(
|
||||
Integer precision,
|
||||
Integer scale,
|
||||
TypeConfiguration typeConfiguration) {
|
||||
final BasicJavaType<Object> elementJavaType = elementJdbcType.getJdbcRecommendedJavaTypeMapping(
|
||||
precision,
|
||||
scale,
|
||||
typeConfiguration
|
||||
);
|
||||
return (BasicJavaType<T>) typeConfiguration.getJavaTypeRegistry().resolveDescriptor(
|
||||
Array.newInstance( elementJavaType.getJavaTypeClass(), 0 ).getClass()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaTypeDescriptor) {
|
||||
//noinspection unchecked
|
||||
final BasicPluralJavaType<T> basicPluralJavaType = (BasicPluralJavaType<T>) javaTypeDescriptor;
|
||||
final JdbcLiteralFormatter<T> elementFormatter = elementJdbcType.getJdbcLiteralFormatter( basicPluralJavaType.getElementJavaType() );
|
||||
return new JdbcLiteralFormatterArray<>( javaTypeDescriptor, elementFormatter );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return java.sql.Array.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaTypeDescriptor) {
|
||||
//noinspection unchecked
|
||||
final BasicPluralJavaType<X> containerJavaType = (BasicPluralJavaType<X>) javaTypeDescriptor;
|
||||
return new BasicBinder<X>( javaTypeDescriptor, this ) {
|
||||
|
||||
@Override
|
||||
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
|
||||
final java.sql.Array arr = getArray( value, containerJavaType, options );
|
||||
st.setArray( index, arr );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
|
||||
throws SQLException {
|
||||
final java.sql.Array arr = getArray( value, containerJavaType, options );
|
||||
final Method nameBinder = NAME_BINDER.get( st.getClass() );
|
||||
if ( nameBinder == null ) {
|
||||
try {
|
||||
st.setObject( name, arr, java.sql.Types.ARRAY );
|
||||
return;
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
throw new HibernateException( "JDBC driver does not support named parameters for setArray. Use positional.", ex );
|
||||
}
|
||||
}
|
||||
// Not that it's supposed to have setArray(String,Array) by standard.
|
||||
// There are numerous missing methods that only have versions for positional parameter,
|
||||
// but not named ones.
|
||||
|
||||
try {
|
||||
nameBinder.invoke( st, name, arr );
|
||||
}
|
||||
catch ( Throwable t ) {
|
||||
throw new HibernateException( t );
|
||||
}
|
||||
}
|
||||
|
||||
private java.sql.Array getArray(
|
||||
X value,
|
||||
BasicPluralJavaType<X> containerJavaType,
|
||||
WrapperOptions options) throws SQLException {
|
||||
final TypeConfiguration typeConfiguration = options.getSessionFactory().getTypeConfiguration();
|
||||
final JdbcType underlyingJdbcType = typeConfiguration.getJdbcTypeRegistry()
|
||||
.getDescriptor( elementJdbcType.getDefaultSqlTypeCode() );
|
||||
final Class<?> preferredJavaTypeClass = underlyingJdbcType.getPreferredJavaTypeClass( options );
|
||||
final Class<?> elementJdbcJavaTypeClass;
|
||||
if ( preferredJavaTypeClass == null ) {
|
||||
elementJdbcJavaTypeClass = underlyingJdbcType.getJdbcRecommendedJavaTypeMapping(
|
||||
null,
|
||||
null,
|
||||
typeConfiguration
|
||||
).getJavaTypeClass();
|
||||
}
|
||||
else {
|
||||
elementJdbcJavaTypeClass = preferredJavaTypeClass;
|
||||
}
|
||||
//noinspection unchecked
|
||||
final Class<Object[]> arrayClass = (Class<Object[]>) Array.newInstance(
|
||||
elementJdbcJavaTypeClass,
|
||||
0
|
||||
).getClass();
|
||||
final Object[] objects = javaTypeDescriptor.unwrap( value, arrayClass, options );
|
||||
|
||||
final SharedSessionContractImplementor session = options.getSession();
|
||||
// TODO: ideally, we would have the actual size or the actual type/column accessible
|
||||
// this is something that we would need for supporting composite types anyway
|
||||
final Size size = session.getJdbcServices()
|
||||
.getDialect()
|
||||
.getSizeStrategy()
|
||||
.resolveSize( elementJdbcType, containerJavaType.getElementJavaType(), null, null, null );
|
||||
String typeName = session.getTypeConfiguration()
|
||||
.getDdlTypeRegistry()
|
||||
.getDescriptor( elementJdbcType.getDefaultSqlTypeCode() )
|
||||
.getTypeName( size );
|
||||
int cutIndex = typeName.indexOf( '(' );
|
||||
if ( cutIndex > 0 ) {
|
||||
// getTypeName for this case required length, etc, parameters.
|
||||
// Cut them out and use database defaults.
|
||||
typeName = typeName.substring( 0, cutIndex );
|
||||
}
|
||||
return session.getJdbcCoordinator().getLogicalConnection().getPhysicalConnection().createArrayOf( typeName, objects );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaTypeDescriptor) {
|
||||
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
|
||||
@Override
|
||||
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
|
||||
return javaTypeDescriptor.wrap( rs.getArray( paramIndex ), options );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
|
||||
return javaTypeDescriptor.wrap( statement.getArray( index ), options );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
|
||||
return javaTypeDescriptor.wrap( statement.getArray( name ), options );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFriendlyName() {
|
||||
return "ARRAY";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ArrayTypeDescriptor(" + getFriendlyName() + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ArrayJdbcType that = (ArrayJdbcType) o;
|
||||
|
||||
return elementJdbcType.equals( that.elementJdbcType );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return elementJdbcType.hashCode();
|
||||
}
|
||||
}
|
|
@ -49,6 +49,11 @@ public class BigIntJdbcType implements JdbcType {
|
|||
return new JdbcLiteralFormatterNumericData<>( javaType, Long.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return Long.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -89,6 +89,11 @@ public abstract class BlobJdbcType implements JdbcType {
|
|||
return "BlobTypeDescriptor(DEFAULT)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return byte[].class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> BasicBinder<X> getBlobBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
@ -129,6 +134,11 @@ public abstract class BlobJdbcType implements JdbcType {
|
|||
return "BlobTypeDescriptor(PRIMITIVE_ARRAY_BINDING)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return byte[].class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> BasicBinder<X> getBlobBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
@ -153,6 +163,11 @@ public abstract class BlobJdbcType implements JdbcType {
|
|||
return "BlobTypeDescriptor(BLOB_BINDING)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return Blob.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> BasicBinder<X> getBlobBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
@ -177,6 +192,11 @@ public abstract class BlobJdbcType implements JdbcType {
|
|||
return "BlobTypeDescriptor(STREAM_BINDING)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return BinaryStream.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> BasicBinder<X> getBlobBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -70,6 +70,11 @@ public class BooleanJdbcType implements AdjustableJdbcType {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return Boolean.class;
|
||||
}
|
||||
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
@Override
|
||||
|
|
|
@ -90,6 +90,13 @@ public abstract class ClobJdbcType implements AdjustableJdbcType {
|
|||
return "ClobTypeDescriptor(DEFAULT)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return options.useStreamForLobBinding() ?
|
||||
STREAM_BINDING.getPreferredJavaTypeClass( options ) :
|
||||
CLOB_BINDING.getPreferredJavaTypeClass( options );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> BasicBinder<X> getClobBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
@ -124,6 +131,11 @@ public abstract class ClobJdbcType implements AdjustableJdbcType {
|
|||
return "ClobTypeDescriptor(STRING_BINDING)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> BasicBinder<X> getClobBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
@ -170,6 +182,11 @@ public abstract class ClobJdbcType implements AdjustableJdbcType {
|
|||
return "ClobTypeDescriptor(CLOB_BINDING)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return Clob.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> BasicBinder<X> getClobBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
@ -194,6 +211,11 @@ public abstract class ClobJdbcType implements AdjustableJdbcType {
|
|||
return "ClobTypeDescriptor(STREAM_BINDING)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return CharacterStream.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> BasicBinder<X> getClobBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
@ -228,6 +250,11 @@ public abstract class ClobJdbcType implements AdjustableJdbcType {
|
|||
return "ClobTypeDescriptor(STREAM_BINDING_EXTRACTING)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return CharacterStream.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> BasicBinder<X> getClobBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -63,6 +63,11 @@ public class DateJdbcType implements JdbcType {
|
|||
return new JdbcLiteralFormatterTemporal<>( javaType, TemporalType.DATE );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return Date.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -60,6 +60,11 @@ public class DecimalJdbcType implements JdbcType {
|
|||
return new JdbcLiteralFormatterNumericData<>( javaType, BigDecimal.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return BigDecimal.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -70,6 +70,11 @@ public class DoubleJdbcType implements JdbcType {
|
|||
return new JdbcLiteralFormatterNumericData<>( javaType, Double.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return Double.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -63,6 +63,11 @@ public class FloatJdbcType implements JdbcType {
|
|||
return new JdbcLiteralFormatterNumericData<>( javaType, Float.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return Float.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -59,6 +59,11 @@ public class IntegerJdbcType implements JdbcType {
|
|||
return new JdbcLiteralFormatterNumericData<>( javaType, Integer.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return Integer.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -9,11 +9,13 @@ package org.hibernate.type.descriptor.jdbc;
|
|||
import java.io.Serializable;
|
||||
import java.sql.Types;
|
||||
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.engine.jdbc.Size;
|
||||
import org.hibernate.query.sqm.CastType;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
import org.hibernate.type.descriptor.ValueExtractor;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.BasicJavaType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||
|
@ -109,6 +111,14 @@ public interface JdbcType extends Serializable {
|
|||
*/
|
||||
<X> ValueExtractor<X> getExtractor(JavaType<X> javaType);
|
||||
|
||||
/**
|
||||
* The Java type class that is preferred by the binder or null.
|
||||
*/
|
||||
@Incubating
|
||||
default Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default boolean isInteger() {
|
||||
return isInteger( getJdbcTypeCode() );
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ public interface JdbcTypeIndicators {
|
|||
* {@link JdbcTypeRegistry}.
|
||||
*/
|
||||
default int getPreferredSqlTypeCodeForBoolean() {
|
||||
return Types.BOOLEAN;
|
||||
return getTypeConfiguration().getSessionFactory().getSessionFactoryOptions().getPreferredSqlTypeCodeForBoolean();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,7 +80,7 @@ public interface JdbcTypeIndicators {
|
|||
* {@link JdbcTypeRegistry}.
|
||||
*/
|
||||
default int getPreferredSqlTypeCodeForDuration() {
|
||||
return SqlTypes.INTERVAL_SECOND;
|
||||
return getTypeConfiguration().getSessionFactory().getSessionFactoryOptions().getPreferredSqlTypeCodeForDuration();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,7 +90,7 @@ public interface JdbcTypeIndicators {
|
|||
* {@link JdbcTypeRegistry}.
|
||||
*/
|
||||
default int getPreferredSqlTypeCodeForUuid() {
|
||||
return SqlTypes.UUID;
|
||||
return getTypeConfiguration().getSessionFactory().getSessionFactoryOptions().getPreferredSqlTypeCodeForUuid();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,7 +100,17 @@ public interface JdbcTypeIndicators {
|
|||
* {@link JdbcTypeRegistry}.
|
||||
*/
|
||||
default int getPreferredSqlTypeCodeForInstant() {
|
||||
return SqlTypes.TIMESTAMP_UTC;
|
||||
return getTypeConfiguration().getSessionFactory().getSessionFactoryOptions().getPreferredSqlTypeCodeForInstant();
|
||||
}
|
||||
|
||||
/**
|
||||
* When mapping a basic array or collection type to the database what is the preferred SQL type code to use?
|
||||
* <p/>
|
||||
* Specifically names the key into the
|
||||
* {@link JdbcTypeRegistry}.
|
||||
*/
|
||||
default int getPreferredSqlTypeCodeForArray() {
|
||||
return getTypeConfiguration().getSessionFactory().getSessionFactoryOptions().getPreferredSqlTypeCodeForArray();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -43,6 +43,12 @@ public class JsonJdbcType implements JdbcType {
|
|||
return "JsonJdbcType";
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
||||
// No literal support for now
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -77,6 +77,13 @@ public abstract class NClobJdbcType implements JdbcType {
|
|||
return "NClobTypeDescriptor(DEFAULT)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return options.useStreamForLobBinding() ?
|
||||
STREAM_BINDING.getPreferredJavaTypeClass( options ) :
|
||||
NCLOB_BINDING.getPreferredJavaTypeClass( options );
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> BasicBinder<X> getNClobBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
@ -111,6 +118,11 @@ public abstract class NClobJdbcType implements JdbcType {
|
|||
return "NClobTypeDescriptor(NCLOB_BINDING)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return NClob.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> BasicBinder<X> getNClobBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
@ -135,6 +147,11 @@ public abstract class NClobJdbcType implements JdbcType {
|
|||
return "NClobTypeDescriptor(STREAM_BINDING)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return CharacterStream.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> BasicBinder<X> getNClobBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -84,6 +84,11 @@ public class NVarcharJdbcType implements AdjustableJdbcType {
|
|||
return jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.type.descriptor.jdbc;
|
|||
|
||||
import java.sql.Types;
|
||||
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.BasicJavaType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
|
@ -47,4 +48,10 @@ public class RealJdbcType extends FloatJdbcType {
|
|||
TypeConfiguration typeConfiguration) {
|
||||
return (BasicJavaType<T>) typeConfiguration.getJavaTypeRegistry().getDescriptor( Float.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return Float.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -59,6 +59,11 @@ public class SmallIntJdbcType implements JdbcType {
|
|||
return new JdbcLiteralFormatterNumericData<>( javaType, Short.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return Short.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -63,6 +63,11 @@ public class TimeJdbcType implements JdbcType {
|
|||
return new JdbcLiteralFormatterTemporal<>( javaType, TemporalType.TIME );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return Time.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -63,6 +63,11 @@ public class TimestampJdbcType implements JdbcType {
|
|||
return new JdbcLiteralFormatterTemporal<>( javaType, TemporalType.TIMESTAMP );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return Timestamp.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -62,6 +62,11 @@ public class TimestampWithTimeZoneJdbcType implements JdbcType {
|
|||
return new JdbcLiteralFormatterTemporal<>( javaType, TemporalType.TIMESTAMP );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return OffsetDateTime.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -62,6 +62,11 @@ public class TinyIntJdbcType implements JdbcType {
|
|||
return new JdbcLiteralFormatterNumericData<>( javaType, Byte.class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return Byte.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -45,6 +45,11 @@ public class UUIDJdbcType implements JdbcType {
|
|||
return "UUIDJdbcType";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return UUID.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -62,6 +62,11 @@ public class VarbinaryJdbcType implements AdjustableJdbcType {
|
|||
return (BasicJavaType<T>) typeConfiguration.getJavaTypeRegistry().getDescriptor( byte[].class );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return byte[].class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
||||
return supportsLiterals ? new JdbcLiteralFormatterBinary<>( javaType ) : null;
|
||||
|
|
|
@ -84,6 +84,11 @@ public class VarcharJdbcType implements AdjustableJdbcType {
|
|||
return jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -43,6 +43,12 @@ public class XmlAsStringJdbcType implements JdbcType {
|
|||
return "XmlAsStringJdbcType";
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
||||
// No literal support for now
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
|
||||
return new BasicBinder<>( javaType, this ) {
|
||||
|
|
|
@ -39,6 +39,12 @@ public class XmlJdbcType implements JdbcType {
|
|||
return "XmlJdbcType";
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
|
||||
// No literal support for now
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
|
||||
return new XmlValueBinder<>( javaType, this );
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.jdbc.internal;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.BasicJdbcLiteralFormatter;
|
||||
|
||||
public class JdbcLiteralFormatterArray<T> extends BasicJdbcLiteralFormatter<T> {
|
||||
|
||||
private final JdbcLiteralFormatter<Object> elementFormatter;
|
||||
|
||||
public JdbcLiteralFormatterArray(JavaType<T> javaType, JdbcLiteralFormatter<?> elementFormatter) {
|
||||
super( javaType );
|
||||
//noinspection unchecked
|
||||
this.elementFormatter = (JdbcLiteralFormatter<Object>) elementFormatter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendJdbcLiteral(SqlAppender appender, Object value, Dialect dialect, WrapperOptions wrapperOptions) {
|
||||
dialect.appendArrayLiteral(
|
||||
appender,
|
||||
unwrap( value, Object[].class, wrapperOptions ),
|
||||
elementFormatter,
|
||||
wrapperOptions
|
||||
);
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ import org.hibernate.type.descriptor.jdbc.TimestampWithTimeZoneJdbcType;
|
|||
import org.hibernate.type.descriptor.jdbc.TinyIntJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.VarbinaryJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.VarcharJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
|
||||
|
||||
/**
|
||||
* Registers the base {@link JdbcType} instances.
|
||||
|
|
|
@ -29,14 +29,18 @@ import org.jboss.logging.Logger;
|
|||
public class JdbcTypeRegistry implements JdbcTypeBaseline.BaselineTarget, Serializable {
|
||||
private static final Logger log = Logger.getLogger( JdbcTypeRegistry.class );
|
||||
|
||||
private final TypeConfiguration typeConfiguration;
|
||||
private final ConcurrentHashMap<Integer, JdbcType> descriptorMap = new ConcurrentHashMap<>();
|
||||
|
||||
public JdbcTypeRegistry(TypeConfiguration typeConfiguration) {
|
||||
// this.typeConfiguration = typeConfiguration;
|
||||
this.typeConfiguration = typeConfiguration;
|
||||
JdbcTypeBaseline.prime( this );
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
public TypeConfiguration getTypeConfiguration() {
|
||||
return typeConfiguration;
|
||||
}
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// baseline descriptors
|
||||
|
||||
@Override
|
||||
|
@ -63,6 +67,10 @@ public class JdbcTypeRegistry implements JdbcTypeBaseline.BaselineTarget, Serial
|
|||
descriptorMap.putIfAbsent( typeCode, jdbcType );
|
||||
}
|
||||
|
||||
public JdbcType findDescriptor(int jdbcTypeCode) {
|
||||
return descriptorMap.get( jdbcTypeCode );
|
||||
}
|
||||
|
||||
public JdbcType getDescriptor(int jdbcTypeCode) {
|
||||
JdbcType descriptor = descriptorMap.get( jdbcTypeCode );
|
||||
if ( descriptor != null ) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue