Do some renaming for consistency and update documentation for new SQL types

This commit is contained in:
Christian Beikov 2022-03-29 17:10:52 +02:00
parent 814c164c81
commit a9d1035806
16 changed files with 263 additions and 43 deletions

View File

@ -61,6 +61,8 @@ dependencies {
requireCapability 'org.ehcache.modules:ehcache-xml-jakarta'
}
}
// Needed for JSON tests
testRuntimeOnly libraries.jackson
}

View File

@ -408,6 +408,7 @@ In other words, the database schema will reflect the Bean Validation constraints
To disable constraint propagation to DDL, set up `hibernate.validator.apply_to_ddl` to `false` in the configuration file.
Such a need is very uncommon and not recommended.
[[misc-options]]
==== Misc options
`*hibernate.create_empty_composites.enabled*` (e.g. `true` or `false` (default value))::

View File

@ -190,10 +190,8 @@ map basic value to the database.
This includes removal of the following deprecated legacy annotations:
* `@Type`
* `@TypeDef`
* `@TypeDefs`
* `@MapKeyType`
* `@CollectionId#type`
* `@AnyMetaDef#metaType`
* `@AnyMetaDef#idType`
@ -982,7 +980,7 @@ include::{sourcedir}/basic/BlobByteArrayTest.java[tags=basic-blob-byte-array-exa
==== Duration
By default, Hibernate will map `Duration` to the `NUMERIC` JDBC type.
By default, Hibernate will map `Duration` to the `INTERVAL_SECOND` SQL type and fallback to `NUMERIC` if necessary.
[[basic-duration-example]]
.Mapping Duration
@ -999,7 +997,7 @@ include::{sourcedir}/basic/DurationMappingTests.java[tags=basic-duration-example
==== Instant
`Instant` is mapped to the `TIMESTAMP` JDBC type.
`Instant` is mapped to the `TIMESTAMP_UTC` SQL type.
[[basic-instant-example]]
@ -1299,7 +1297,8 @@ include::{sourcedir}/basic/LocaleMappingTests.java[tags=basic-Locale-example]
==== UUID
Hibernate allows mapping UUID values in a number of ways. By default, Hibernate will
store UUID values in their binary form.
store UUID values in the native form by using the SQL type `UUID` or in binary form with the `BINARY` JDBC type
if the database does not have a native UUID type.
[NOTE]
@ -1308,8 +1307,7 @@ The default uses the binary representation because it uses a more efficient colu
However, many applications prefer the readability of the character-based column storage.
// todo (6.0) : develop a better way to expose this to users
To switch the default mapping, simply call `MetadataBuilder.applyBasicType( UUIDCharType.INSTANCE, UUID.class.getName() )`.
To switch the default mapping, set the `*hibernate.type.preferred_uuid_jdbc_type*` configuration to `CHAR`.
====
===== UUID as binary
@ -1323,23 +1321,41 @@ Chosen as the default simply because it is generally more efficient from a stora
Maps the UUID to a String using `java.util.UUID#toString` and `java.util.UUID#fromString` and stores that as `CHAR` or `VARCHAR` data.
===== PostgreSQL-specific UUID
[IMPORTANT]
====
When using one of the PostgreSQL Dialects, the PostgreSQL-specific UUID Hibernate type becomes the default UUID mapping.
====
Maps the UUID using the PostgreSQL-specific UUID data type.
The PostgreSQL JDBC driver chooses to map its UUID type to the `OTHER` code.
Note that this can cause difficulty as the driver chooses to map many different data types to `OTHER`.
===== UUID as identifier
Hibernate supports using UUID values as identifiers, and they can even be generated on the user's behalf.
For details, see the discussion of generators in <<chapters/domain/identifiers.adoc#identifiers,_Identifiers_>>.
==== InetAddress
By default, Hibernate will map `InetAddress` to the `INET` SQL type and fallback to `BINARY` if necessary.
[[basic-inet-address-example]]
.Mapping InetAddress
====
[source, JAVA, indent=0]
----
include::{sourcedir}/basic/InetAddressMappingTests.java[tags=basic-inet-address-example]
----
====
[[basic-mapping-json]]
==== JSON mapping
Hibernate will only use the `JSON` type if explicitly configured through `@JdbcTypeCode( SqlTypes.JSON )`.
The JSON library used for serialization/deserialization is detected automatically,
but can be overridden by setting `hibernate.type.json_format_mapper`
as can be read in the <<appendices/Configurations.adoc#misc-options,Configurations>> section.
[[basic-json-example]]
.Mapping JSON
====
[source, JAVA, indent=0]
----
include::{sourcedir}/basic/JsonMappingTests.java[tags=basic-json-example]
----
====
[[basic-mapping-composition]]
@ -1530,9 +1546,8 @@ Another approach is to supply the implementation of the `org.hibernate.usertype.
There are also corresponding, specialized forms of `@Type` for specific model parts:
* When mapping a Map, `@Type` describes the Map value while `@MapKeyCustomType` describe the Map key
* When mapping a List or array, `@Type` describes the elements while `@ListIndexCustomType` describes the index
* When mapping an id-bag, `@Type` describes the elements while `CollectionIdType` describes the collection-id
* When mapping a Map, `@Type` describes the Map value while `@MapKeyType` describe the Map key
* When mapping an id-bag, `@Type` describes the elements while `@CollectionIdType` describes the collection-id
* For other collection mappings, `@Type` describes the elements
* For discriminated association mappings (`@Any` and `@ManyToAny`), `@Type` describes the discriminator value

View File

@ -0,0 +1,99 @@
/*
* 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.net.InetAddress;
import java.net.UnknownHostException;
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.type.SqlTypes;
import org.hibernate.type.descriptor.jdbc.AdjustableJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
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.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;
import static org.hamcrest.Matchers.is;
/**
* @author Christian Beikov
*/
@DomainModel(annotatedClasses = InetAddressMappingTests.EntityWithInetAddress.class)
@SessionFactory
public class InetAddressMappingTests {
@Test
public void verifyMappings(SessionFactoryScope scope) {
final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory()
.getRuntimeMetamodels()
.getMappingMetamodel();
final EntityPersister entityDescriptor = mappingMetamodel.findEntityDescriptor( EntityWithInetAddress.class);
final JdbcTypeRegistry jdbcTypeRegistry = mappingMetamodel.getTypeConfiguration().getJdbcTypeRegistry();
final BasicAttributeMapping duration = (BasicAttributeMapping) entityDescriptor.findAttributeMapping("address");
final JdbcMapping jdbcMapping = duration.getJdbcMapping();
assertThat(jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo(InetAddress.class));
final JdbcType intervalType = jdbcTypeRegistry.getDescriptor(SqlTypes.INET);
final JdbcType realType;
if (intervalType instanceof AdjustableJdbcType) {
realType = ((AdjustableJdbcType) intervalType).resolveIndicatedType(
() -> mappingMetamodel.getTypeConfiguration(),
jdbcMapping.getJavaTypeDescriptor()
);
}
else {
realType = intervalType;
}
assertThat( jdbcMapping.getJdbcType(), is( realType));
scope.inTransaction(
(session) -> {
try {
session.persist( new EntityWithInetAddress( 1, InetAddress.getLocalHost() ) );
}
catch (UnknownHostException e) {
throw new RuntimeException( e );
}
}
);
scope.inTransaction(
(session) -> session.find( EntityWithInetAddress.class, 1)
);
}
@Entity(name = "EntityWithInetAddress")
@Table(name = "EntityWithInetAddress")
public static class EntityWithInetAddress {
@Id
private Integer id;
//tag::basic-inet-address-example[]
private InetAddress address;
//end::basic-inet-address-example[]
public EntityWithInetAddress() {
}
public EntityWithInetAddress(Integer id, InetAddress address) {
this.id = id;
this.address = address;
}
}
}

View File

@ -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 java.util.Map;
import org.hibernate.annotations.JdbcTypeCode;
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.type.SqlTypes;
import org.hibernate.type.descriptor.jdbc.AdjustableJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
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.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;
import static org.hamcrest.Matchers.is;
/**
* @author Christian Beikov
*/
@DomainModel(annotatedClasses = JsonMappingTests.EntityWithJson.class)
@SessionFactory
public class JsonMappingTests {
@Test
public void verifyMappings(SessionFactoryScope scope) {
final MappingMetamodelImplementor mappingMetamodel = scope.getSessionFactory()
.getRuntimeMetamodels()
.getMappingMetamodel();
final EntityPersister entityDescriptor = mappingMetamodel.findEntityDescriptor( EntityWithJson.class);
final JdbcTypeRegistry jdbcTypeRegistry = mappingMetamodel.getTypeConfiguration().getJdbcTypeRegistry();
final BasicAttributeMapping duration = (BasicAttributeMapping) entityDescriptor.findAttributeMapping("payload");
final JdbcMapping jdbcMapping = duration.getJdbcMapping();
assertThat(jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo(Map.class));
final JdbcType intervalType = jdbcTypeRegistry.getDescriptor(SqlTypes.JSON);
final JdbcType realType;
if (intervalType instanceof AdjustableJdbcType) {
realType = ((AdjustableJdbcType) intervalType).resolveIndicatedType(
() -> mappingMetamodel.getTypeConfiguration(),
jdbcMapping.getJavaTypeDescriptor()
);
}
else {
realType = intervalType;
}
assertThat( jdbcMapping.getJdbcType(), is( realType));
scope.inTransaction(
(session) -> {
session.persist( new EntityWithJson( 1, Map.of( "name", "ABC" ) ) );
}
);
scope.inTransaction(
(session) -> session.find( EntityWithJson.class, 1)
);
}
@Entity(name = "EntityWithJson")
@Table(name = "EntityWithJson")
public static class EntityWithJson {
@Id
private Integer id;
//tag::basic-json-example[]
@JdbcTypeCode( SqlTypes.JSON )
private Map<String, String> payload;
//end::basic-json-example[]
public EntityWithJson() {
}
public EntityWithJson(Integer id, Map<String, String> payload) {
this.id = id;
this.payload = payload;
}
}
}

View File

@ -93,10 +93,10 @@ public class MimerSQLDialect extends Dialect {
return columnType( LONG32NVARCHAR );
//default length is 1M, which is quite low
case BLOB:
return "blob($l)";
return "blob(2G)";
case CLOB:
case NCLOB:
return "nclob($l)";
return "nclob(2G)";
}
return super.columnType( sqlTypeCode );
}

View File

@ -13,7 +13,7 @@ import org.hibernate.usertype.CompositeUserType;
*
* @since 6.0
*/
public @interface MapKeyCustomCompositeType {
public @interface MapKeyCompositeType {
/**
* The custom type implementor class
*

View File

@ -13,7 +13,7 @@ import org.hibernate.usertype.UserType;
*
* @since 6.0
*/
public @interface MapKeyCustomType {
public @interface MapKeyType {
/**
* The custom type implementor class
*

View File

@ -12,7 +12,6 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -73,7 +72,7 @@ import org.hibernate.annotations.LazyToOne;
import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.annotations.ListIndexBase;
import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.MapKeyCustomType;
import org.hibernate.annotations.MapKeyType;
import org.hibernate.annotations.MapKeyJavaType;
import org.hibernate.annotations.MapKeyJdbcType;
import org.hibernate.annotations.MapKeyJdbcTypeCode;
@ -2593,7 +2592,7 @@ public final class AnnotationBinder {
|| property.isAnnotationPresent(MapKeyJdbcTypeCode.class)
|| property.isAnnotationPresent(MapKeyMutability.class)
|| property.isAnnotationPresent(MapKey.class)
|| property.isAnnotationPresent(MapKeyCustomType.class);
|| property.isAnnotationPresent( MapKeyType.class);
}
private static void bindAny(

View File

@ -13,7 +13,7 @@ import java.util.Map;
import org.hibernate.AssertionFailure;
import org.hibernate.annotations.CollectionType;
import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.MapKeyCustomType;
import org.hibernate.annotations.MapKeyType;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
@ -347,7 +347,7 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
else if ( collectionProperty.isAnnotationPresent( MapKeyClass.class ) ) {
canKeyBeConverted = false;
}
else if ( collectionProperty.isAnnotationPresent( MapKeyCustomType.class ) ) {
else if ( collectionProperty.isAnnotationPresent( MapKeyType.class ) ) {
canKeyBeConverted = false;
}
}

View File

@ -35,7 +35,7 @@ import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.annotations.ListIndexJavaType;
import org.hibernate.annotations.ListIndexJdbcType;
import org.hibernate.annotations.ListIndexJdbcTypeCode;
import org.hibernate.annotations.MapKeyCustomType;
import org.hibernate.annotations.MapKeyType;
import org.hibernate.annotations.MapKeyJavaType;
import org.hibernate.annotations.MapKeyJdbcType;
import org.hibernate.annotations.MapKeyJdbcTypeCode;
@ -1347,7 +1347,7 @@ public class BasicValueBinder implements JdbcTypeIndicators {
@Override
public Class<? extends UserType<?>> customType(XProperty xProperty) {
final MapKeyCustomType customType = findAnnotation( xProperty, MapKeyCustomType.class );
final MapKeyType customType = findAnnotation( xProperty, MapKeyType.class );
if ( customType == null ) {
return null;
}
@ -1357,7 +1357,7 @@ public class BasicValueBinder implements JdbcTypeIndicators {
@Override
public Parameter[] customTypeParameters(XProperty xProperty) {
final MapKeyCustomType customType = findAnnotation( xProperty, MapKeyCustomType.class );
final MapKeyType customType = findAnnotation( xProperty, MapKeyType.class );
if ( customType == null ) {
return null;
}

View File

@ -15,7 +15,7 @@ import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.FetchMode;
import org.hibernate.MappingException;
import org.hibernate.annotations.MapKeyCustomCompositeType;
import org.hibernate.annotations.MapKeyCompositeType;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
@ -383,7 +383,7 @@ public class MapBinder extends CollectionBinder {
XProperty property,
XClass returnedClass,
MetadataBuildingContext context) {
final MapKeyCustomCompositeType compositeType = property.getAnnotation( MapKeyCustomCompositeType.class );
final MapKeyCompositeType compositeType = property.getAnnotation( MapKeyCompositeType.class );
if ( compositeType != null ) {
return compositeType.value();
}

View File

@ -144,9 +144,9 @@ public class DB2Dialect extends Dialect {
// Note that 31 is the maximum precision DB2 supports
return columnType( DECIMAL );
case BLOB:
return "blob($l)";
return "blob";
case CLOB:
return "clob($l)";
return "clob";
case TIMESTAMP_WITH_TIMEZONE:
return "timestamp($p)";
case TIME_WITH_TIMEZONE:

View File

@ -136,10 +136,10 @@ public class DerbyDialect extends Dialect {
case LONG32VARCHAR:
return "long varchar";
case BLOB:
return "blob($l)";
return "blob";
case CLOB:
case NCLOB:
return "clob($l)";
return "clob";
case TIMESTAMP:
case TIMESTAMP_WITH_TIMEZONE:
return "timestamp";

View File

@ -61,7 +61,7 @@ public class InetAddressJavaType extends AbstractClassJavaType<InetAddress> {
return (X) value.getAddress();
}
if ( String.class.isAssignableFrom( type ) ) {
return (X) value.toString();
return (X) value.getHostAddress();
}
throw unknownUnwrap( type );
}

View File

@ -110,7 +110,7 @@ We decided this is the right time since 6.0 is a major release and most of the t
contracts were already changing to implement the <<read-jdbc,read-by-position>> changes.
One part of this work was the removal of various String-based approaches for specifying Types to use from annotations, including
the removal of `@Type`, `@AnyMetaDef`, `@AnyMetaDefs`, `@MapKeyType`, @TypeDef` and `@TypeDefs`, as well as
the removal of `@AnyMetaDef`, `@AnyMetaDefs`, `@TypeDef` and `@TypeDefs`, as well as
removing annotation attributes accepting the type to use as a String (e.g. `org.hibernate.annotations.CollectionType#type`)
The https://docs.jboss.org/hibernate/orm/6.0/userguide/html_single/Hibernate_User_Guide.html#domain-model[User Guide]
@ -280,8 +280,17 @@ In either case, schema validation errors could occur as 5.x used the type code `
Migration to `numeric(21)` should be easy. The migration to `interval second` might require a migration expression like
`cast(cast(old as numeric(21,9) / 1000000000) as interval second(9))`.
To retain backwards compatibility, configure the setting `hibernate.type.preferred_duration_jdbc_type` to `2`
which stands for `Types.NUMERIC`.
To retain backwards compatibility, configure the setting `hibernate.type.preferred_duration_jdbc_type` to `NUMERIC`.
=== UUID mapping changes
UUID now maps to the type code `SqlType.UUID` by default, which maps to the SQL type `uuid`
if possible, and falls back to `binary(16)`.
Due to the change to the native `uuid` type, schema validation errors could occur on database with native data type support.
The migration to `uuid` might require a migration expression like `cast(old as uuid)`.
To retain backwards compatibility, configure the setting `hibernate.type.preferred_uuid_jdbc_type` to `BINARY`.
[[query]]
== Query