Skip to content

Commit

Permalink
* Add SqlTypes as analogy to java.sql.Types containing constants for …
Browse files Browse the repository at this point in the history
…Hibernate specified types

* Add FormatMapper for a pluggable JSON serialization and deserialization strategy
* Add native UUID type support for H2, Cockroach, PostgreSQL
* Add native INET type support for Cockroach, PostgreSQL
* Add native JSON type support for MySQL, Cockroach, PostgreSQL
* Add native INTERVAL SECOND type support for H2, Cockroach, PostgreSQL
* Add fallback JdbcTypes for new SqlTypes
* Register column types for new SqlTypes
* Add support for BasicTypeReference in TypedParameterValue
* Fix a lot of method signatures with respect to type parameter issues
* Fix CustomType, UserType and EnhancedUserType with respect to type parameters
* Get rid of StringRepresentableType and some other unused deprecated methods
  • Loading branch information
beikov committed Oct 19, 2021
1 parent 541302a commit a4e406a
Show file tree
Hide file tree
Showing 198 changed files with 3,148 additions and 1,301 deletions.
Expand Up @@ -125,10 +125,10 @@ Java and SQL data types. Hibernate attempts to determine the correct conversion
type attribute is not specified in the mapping, by using Java reflection to determine the Java type of the declared
property and using a default mapping type for that Java type.

In some cases this automatic detection might not chose the default you expect or need, as seen with the
`date` property. Hibernate cannot know if the property, which is of type `java.util.Date`, should map to a SQL
In some cases this automatic detection might not choose the default you expect or need, as seen with the
`date` property. Hibernate cannot know if the property, which is of type `java.util.Date`, should map to an SQL
_DATE_, _TIME_, or _TIMESTAMP_ datatype. Full date and time information is preserved by mapping the property to
the _timestamp_ converter, which identifies the converter class `org.hibernate.type.TimestampType`.
the _timestamp_ converter, which identifies the converter as declared by `org.hibernate.type.StandardBasicTypes.TIMESTAMP`.

TIP: Hibernate determines the mapping type using reflection when the mapping files are processed. This process adds
overhead in terms of time and resources. If startup performance is important, consider explicitly defining the type
Expand Down
Expand Up @@ -406,6 +406,9 @@ Unless specified, the JDBC Driver uses the default JVM time zone. If a different
`*hibernate.dialect.oracle.prefer_long_raw*` (e.g. `true` or `false` (default value))::
This setting applies to Oracle Dialect only, and it specifies whether `byte[]` or `Byte[]` arrays should be mapped to the deprecated `LONG RAW` (when this configuration property value is `true`) or to a `BLOB` column type (when this configuration property value is `false`).

`*hibernate.type.preferred_boolean_jdbc_type_code*` (e.g. `-7` for `java.sql.Types.BIT`)::
Global setting identifying the preferred JDBC type code for storing boolean values. The fallback is to ask the Dialect.

==== Bean Validation options
`*jakarta.persistence.validation.factory*` (e.g. `jakarta.validation.ValidationFactory` implementation)::
Specify the `javax.validation.ValidationFactory` implementation to use for Bean Validation.
Expand Down Expand Up @@ -435,6 +438,16 @@ Setting to identify an `org.hibernate.CustomEntityDirtinessStrategy` to use.
`*hibernate.default_entity_mode*` (e.g. `pojo` (default value) or `dynamic-map`)::
Default `EntityMode` for entity representation for all sessions opened from this `SessionFactory`, defaults to `pojo`.

`*hibernate.type.json_format_mapper*` (e.g. A fully-qualified class name, an instance, or a `Class` object reference)::
Names a https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/type/FormatMapper.html[`FormatMapper`] implementation to be applied to the `SessionFactory` for JSON serialization and deserialization.
+
Can reference a
`FormatMapper` instance,
`FormatMapper` implementation `Class` reference,
`FormatMapper` implementation class name (fully-qualified class name) or
one of the following short hand constants `jackson` or `jsonb`.
By default the first of the possible providers that is available in the runtime is used, according to the listing order.

[[configurations-bytecode-enhancement]]
=== Bytecode Enhancement Properties

Expand Down
Expand Up @@ -115,11 +115,11 @@ See the <<chapters/query/spatial/Spatial.adoc#spatial,Spatial>> chapter for more

We said before that a Hibernate type is not a Java type, nor an SQL type, but that it understands both and performs the marshalling between them.
But looking at the basic type mappings from the previous examples,
how did Hibernate know to use its `org.hibernate.type.StringType` for mapping for `java.lang.String` attributes,
or its `org.hibernate.type.IntegerType` for mapping `java.lang.Integer` attributes?
how did Hibernate know to use its `org.hibernate.type.StandardBasicTypes.STRING` for mapping for `java.lang.String` attributes,
or its `org.hibernate.type.StandardBasicTypes.INTEGER` for mapping `java.lang.Integer` attributes?

The answer lies in a service inside Hibernate called the `org.hibernate.type.BasicTypeRegistry`, which maintains a
map of `org.hibernate.type.BasicType` instances keyed by a name.
map of `org.hibernate.type.BasicType` and `org.hibernate.type.BasicTypeReference` instances keyed by a name.

We will see later, in the <<basic-type-annotation>> section, that we can explicitly tell Hibernate which BasicType to use for a particular attribute.
But first, let's explore how implicit resolution works and how applications can adjust the implicit resolution.
Expand Down
Expand Up @@ -272,7 +272,7 @@ In other words, `INSERT` statements are inherently non-polymorphic.
The SELECT statement can be any valid HQL select query, but the return types must match the types expected by the INSERT.
Hibernate verifies the return types during query compilation, instead of expecting the database to check it.
Problems might result from Hibernate types which are equivalent, rather than equal.
One such example is a mismatch between a property defined as an `org.hibernate.type.DateType` and a property defined as an `org.hibernate.type.TimestampType`,
One such example is a mismatch between a property defined as an `org.hibernate.type.StandardBasicTypes.DATE` and a property defined as an `org.hibernate.type.StandardBasicTypes.TIMESTAMP`,
even though the database may not make a distinction, or may be capable of handling the conversion.

If id property is not specified in the `properties_list`,
Expand Down
Expand Up @@ -612,7 +612,7 @@ In other words, `INSERT` statements are inherently non-polymorphic.
`select_statement` can be any valid HQL select query, with the caveat that the return types must match the types expected by the insert.
Currently, this is checked during query compilation rather than allowing the check to delegate to the database.
This may cause problems between Hibernate Types which are _equivalent_ as opposed to __equal__.
For example, this might lead to issues with mismatches between an attribute mapped as a `org.hibernate.type.DateType` and an attribute defined as a `org.hibernate.type.TimestampType`,
For example, this might lead to issues with mismatches between an attribute mapped as a `org.hibernate.type.StandardBasicTypes.DATE` and an attribute defined as a `org.hibernate.type.StandardBasicTypes.TIMESTAMP`,
even though the database might not make a distinction or might be able to handle the conversion.

For the id attribute, the insert statement gives you two options.
Expand Down
Expand Up @@ -6,18 +6,21 @@
*/
package org.hibernate.userguide.mapping.basic;

import java.sql.Types;
import java.time.Duration;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

import org.hibernate.metamodel.MappingMetamodel;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
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.JdbcTypeDescriptorIndicators;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeDescriptorRegistry;
import org.hibernate.type.spi.TypeConfiguration;

import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
Expand Down Expand Up @@ -45,7 +48,18 @@ public void verifyMappings(SessionFactoryScope scope) {
final BasicAttributeMapping duration = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "duration" );
final JdbcMapping jdbcMapping = duration.getJdbcMapping();
assertThat( jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo( Duration.class ) );
assertThat( jdbcMapping.getJdbcTypeDescriptor(), is( jdbcTypeRegistry.getDescriptor( Types.NUMERIC ) ) );
final JdbcType intervalType = jdbcTypeRegistry.getDescriptor( SqlTypes.INTERVAL_SECOND );
final JdbcType realType;
if ( intervalType instanceof AdjustableJdbcType ) {
realType = ((AdjustableJdbcType) intervalType).resolveIndicatedType(
() -> domainModel.getTypeConfiguration(),
jdbcMapping.getJavaTypeDescriptor()
);
}
else {
realType = intervalType;
}
assertThat( jdbcMapping.getJdbcTypeDescriptor(), is( realType ) );

scope.inTransaction(
(session) -> {
Expand Down
2 changes: 2 additions & 0 deletions gradle/libraries.gradle
Expand Up @@ -90,6 +90,7 @@ ext {
jakarta_validator: "org.hibernate.validator:hibernate-validator:${hibernateValidatorJakartaVersion}",
jakarta_el: 'org.glassfish:jakarta.el:4.0.1',
jakarta_inject: 'jakarta.inject:jakarta.inject-api:2.0.0',
jakarta_jsonb: 'jakarta.json.bind:jakarta.json.bind-api:2.0.0',

// javax
jpa: "javax.persistence:javax.persistence-api:${project.jpaVersion}",
Expand Down Expand Up @@ -121,6 +122,7 @@ ext {

// jaxb task

jackson: 'com.fasterxml.jackson.core:jackson-databind:2.13.0',
geolatte: "org.geolatte:geolatte-geom:${geolatteVersion}",

// Animal Sniffer Ant Task and Java 1.6 API signature file
Expand Down
Expand Up @@ -20,6 +20,7 @@
import org.hibernate.dialect.sequence.SequenceSupport;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.IntervalType;
import org.hibernate.query.SemanticException;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
Expand Down Expand Up @@ -378,7 +379,7 @@ public TimeZoneSupport getTimeZoneSupport() {
}

@Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType) {
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
switch (unit) {
case NANOSECOND:
return "adddate(?3,interval (?2)/1e6 millisecond)";
Expand Down
Expand Up @@ -26,6 +26,7 @@
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.persister.entity.Lockable;
import org.hibernate.query.IntervalType;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.sql.ast.SqlAstTranslator;
Expand Down Expand Up @@ -193,7 +194,7 @@ public String extractPattern(TemporalUnit unit) {
}

@Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType) {
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
switch (unit) {
case NANOSECOND:
case NATIVE:
Expand Down
Expand Up @@ -11,6 +11,7 @@
import org.hibernate.dialect.BooleanDecoder;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.TimeZoneSupport;
import org.hibernate.query.IntervalType;
import org.hibernate.query.NullOrdering;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.CommonFunctionFactory;
Expand Down Expand Up @@ -426,7 +427,7 @@ public String extractPattern(TemporalUnit unit) {
}

@Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType) {
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
switch ( unit ) {
case NATIVE:
return "dateadd((?2) millisecond to ?3)";
Expand Down
Expand Up @@ -25,6 +25,7 @@
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.FetchClauseType;
import org.hibernate.query.IntervalType;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.spi.QueryOptions;
Expand Down Expand Up @@ -308,7 +309,7 @@ protected <T extends JdbcOperation> SqlAstTranslator<T> buildTranslator(
}

@Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType) {
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
return "timestampadd(?1,?2,?3)";

}
Expand Down
Expand Up @@ -20,6 +20,7 @@
import org.hibernate.dialect.sequence.SequenceSupport;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.IntervalType;
import org.hibernate.query.SemanticException;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
Expand Down Expand Up @@ -207,7 +208,7 @@ public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalT
}

@Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType) {
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
switch ( unit ) {
case NATIVE:
case NANOSECOND:
Expand Down
Expand Up @@ -19,6 +19,7 @@
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.persister.entity.Lockable;
import org.hibernate.query.IntervalType;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.TrimSpec;
import org.hibernate.query.spi.QueryEngine;
Expand Down Expand Up @@ -226,7 +227,7 @@ public String extractPattern(TemporalUnit unit) {
}

@Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType) {
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
switch (unit) {
case NANOSECOND:
return "timestampadd('SQL_TSI_FRAC_SECOND',(?2)/1e3,?3)";
Expand Down
Expand Up @@ -36,6 +36,7 @@
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.mapping.Column;
import org.hibernate.query.IntervalType;
import org.hibernate.query.NullOrdering;
import org.hibernate.query.SemanticException;
import org.hibernate.query.TemporalUnit;
Expand Down Expand Up @@ -166,7 +167,7 @@ public String extractPattern(TemporalUnit unit) {
}

@Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType) {
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
final String function = temporalType == TemporalType.DATE ? "date" : "datetime";
switch ( unit ) {
case NANOSECOND:
Expand Down
Expand Up @@ -27,6 +27,7 @@
import org.hibernate.mapping.Index;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.IntervalType;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.spi.QueryOptions;
Expand Down Expand Up @@ -217,7 +218,7 @@ public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalT
}

@Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType) {
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
//TODO: TOTALLY UNTESTED CODE!
switch ( unit ) {
case NANOSECOND:
Expand Down
Expand Up @@ -21,6 +21,7 @@
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.persister.entity.Lockable;
import org.hibernate.query.IntervalType;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
Expand Down Expand Up @@ -169,7 +170,7 @@ protected <T extends JdbcOperation> SqlAstTranslator<T> buildTranslator(
}

@Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType) {
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
switch (unit) {
case NANOSECOND:
case NATIVE:
Expand Down
5 changes: 5 additions & 0 deletions hibernate-core/hibernate-core.gradle
Expand Up @@ -43,6 +43,9 @@ dependencies {
compileOnly libraries.jakarta_validation
compileOnly libraries.jakarta_cdi

compileOnly libraries.jackson
compileOnly libraries.jakarta_jsonb

testImplementation project(':hibernate-testing')
testImplementation project(':hibernate-ant')
testImplementation libraries.shrinkwrap_api
Expand All @@ -65,6 +68,7 @@ dependencies {
testRuntimeOnly libraries.byteBuddy
testRuntimeOnly libraries.jakarta_weld
testRuntimeOnly libraries.wildfly_transaction_client
testRuntimeOnly libraries.jackson

testAnnotationProcessor project( ':hibernate-jpamodelgen' )

Expand Down Expand Up @@ -212,5 +216,6 @@ tasks.withType( Test.class ).each { test ->
test.include 'org/hibernate/bytecode/internal/bytebuddy/**'
test.include 'org/hibernate/event/service/internal/**'
test.include 'org/hibernate/tool/schema/internal/**'
// test.include '**/*'
}

Expand Up @@ -74,6 +74,9 @@
import org.hibernate.stat.Statistics;
import org.hibernate.tuple.entity.EntityTuplizer;
import org.hibernate.tuple.entity.EntityTuplizerFactory;
import org.hibernate.type.FormatMapper;
import org.hibernate.type.JacksonJsonFormatMapper;
import org.hibernate.type.JsonBJsonFormatMapper;

import static org.hibernate.cfg.AvailableSettings.ACQUIRE_CONNECTIONS;
import static org.hibernate.cfg.AvailableSettings.ALLOW_JTA_TRANSACTION_ACCESS;
Expand Down Expand Up @@ -158,6 +161,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
// integration
private Object beanManagerReference;
private Object validatorFactoryReference;
private FormatMapper jsonFormatMapper;

// SessionFactory behavior
private boolean jpaBootstrap;
Expand Down Expand Up @@ -301,6 +305,10 @@ public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, Boo
AvailableSettings.JPA_VALIDATION_FACTORY,
configurationSettings.get( AvailableSettings.JAKARTA_VALIDATION_FACTORY )
);
this.jsonFormatMapper = determineJsonFormatMapper(
configurationSettings.get( AvailableSettings.JSON_FORMAT_MAPPER ),
strategySelector
);

this.sessionFactoryName = (String) configurationSettings.get( SESSION_FACTORY_NAME );
this.sessionFactoryNameAlsoJndiName = cfgService.getSetting(
Expand Down Expand Up @@ -799,7 +807,31 @@ private PhysicalConnectionHandlingMode interpretConnectionHandlingMode(
return PhysicalConnectionHandlingMode.interpret( effectiveAcquisitionMode, effectiveReleaseMode );
}


private static FormatMapper determineJsonFormatMapper(Object setting, StrategySelector strategySelector) {
return strategySelector.resolveDefaultableStrategy(
FormatMapper.class,
setting,
(Callable<FormatMapper>) () -> {
try {
// Force initialization of the instance
JacksonJsonFormatMapper.INSTANCE.hashCode();
return JacksonJsonFormatMapper.INSTANCE;
}
catch (NoClassDefFoundError ex) {
// Ignore
}
try {
// Force initialization of the instance
JsonBJsonFormatMapper.INSTANCE.hashCode();
return JsonBJsonFormatMapper.INSTANCE;
}
catch (NoClassDefFoundError ex) {
// Ignore
}
return null;
}
);
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -1220,6 +1252,11 @@ public int getPreferredSqlTypeCodeForBoolean() {
public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() {
return defaultTimeZoneStorageStrategy;
}

@Override
public FormatMapper getJsonFormatMapper() {
return jsonFormatMapper;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// In-flight mutation access

Expand Down
Expand Up @@ -177,8 +177,8 @@ private static BasicValue.Resolution<?> createResolution(
injectParameters( typeInstance, combinedTypeParameters );

if ( typeInstance instanceof UserType ) {
final UserType userType = (UserType) typeInstance;
final CustomType customType = new CustomType( userType, typeConfiguration );
final UserType<Object> userType = (UserType<Object>) typeInstance;
final CustomType<Object> customType = new CustomType<>( userType, typeConfiguration );

return new UserTypeResolution( customType, null, combinedTypeParameters );
}
Expand Down

0 comments on commit a4e406a

Please sign in to comment.