Skip to content

Commit 43c3737

Browse files
authored
Merge branch 'hibernate:main' into HHH-18855
2 parents d8d2826 + 223855a commit 43c3737

File tree

39 files changed

+1193
-321
lines changed

39 files changed

+1193
-321
lines changed

documentation/src/main/asciidoc/introduction/Entities.adoc

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -656,10 +656,20 @@ But in the country I was born, `SUNDAY` is the _first_ day of the week!
656656
So we prefer `@Enumerated(STRING)` for most `enum` attributes.
657657
====
658658

659-
An interesting special case is PostgreSQL.
660-
Postgres supports _named_ `ENUM` types, which must be declared using a DDL `CREATE TYPE` statement.
661-
Sadly, these `ENUM` types aren't well-integrated with the language nor well-supported by the Postgres JDBC driver, so Hibernate doesn't use them by default.
662-
But if you would like to use a named enumerated type on Postgres, just annotate your `enum` attribute like this:
659+
An interesting special case arises on PostgreSQL and Oracle.
660+
661+
[[named-enums]]
662+
.Named enumerated types
663+
****
664+
Some databases support _named_ `ENUM` types, which must be declared using in DDL using:
665+
666+
- `CREATE TYPE ... AS ENUM` on PostgreSQL, or
667+
- `CREATE DOMAIN ... AS ENUM` on Oracle.
668+
669+
These look like a perfect match for Java ``enum``s, which also have names!
670+
671+
Sadly, these `ENUM` types aren't well-integrated with the SQL language, nor well-supported by the JDBC drivers, so Hibernate doesn't use them by default.
672+
But if you would like to use a named enumerated type on Postgres or Oracle, just annotate your `enum` attribute like this:
663673
664674
[source,java]
665675
----
@@ -668,6 +678,9 @@ But if you would like to use a named enumerated type on Postgres, just annotate
668678
Status status;
669679
----
670680
681+
Alternatively, you may enable the configuration property link:{doc-javadoc-url}org/hibernate/cfg/MappingSettings.html#PREFER_NATIVE_ENUM_TYPES[`hibernate.type.prefer_native_enum_types`].
682+
****
683+
671684
The limited set of pre-defined basic attribute types can be stretched a bit further by supplying a _converter_.
672685

673686
[[converters]]
@@ -740,6 +753,7 @@ Hibernate considers a "basic type" to be formed by the marriage of two objects:
740753

741754
When mapping a basic attribute, we may explicitly specify a `JavaType`, a `JdbcType`, or both.
742755

756+
[[java-type]]
743757
[discrete]
744758
==== JavaType
745759

@@ -771,6 +785,7 @@ BitSet bitSet;
771785

772786
Alternatively, the `@JavaTypeRegistration` annotation may be used to register `BitSetJavaType` as the default `JavaType` for `BitSet`.
773787

788+
[[jdbc-type]]
774789
[discrete]
775790
==== JdbcType
776791

@@ -827,6 +842,82 @@ long currentTimeMillis;
827842

828843
Let's abandon our analogy right here, before we start calling this basic type a "throuple".
829844

845+
[[datetime-types]]
846+
=== Date and time types, and time zones
847+
848+
Dates and times should always be represented using the types defined in `java.time`.
849+
850+
[WARNING]
851+
====
852+
Never use the legacy types `java.sql.Date`, `java.sql.Time`, `java.sql.Timestamp`, or `java.util.Date`.
853+
At our urging, support for these types has even been https://in.relation.to/2024/04/22/stop-using-date/[officially deprecated in JPA 3.2].
854+
Eventually, we hope to completely remove support for these types from the JPA spec and from Hibernate.
855+
====
856+
857+
Some of the types in `java.time` map naturally to an ANSI SQL column type.
858+
A source of confusion is that some databases still don't follow the ANSI standard naming here.
859+
Also, as you're probably aware, the `DATE` type on Oracle is not an ANSI SQL `DATE`.
860+
In fact, Oracle doesn't have `DATE` or `TIME` types--every date or time must be stored as a timestamp.
861+
862+
.Type mappings from `java.time` to ANSI SQL
863+
|====
864+
| `java.time` class | ANSI SQL type | MySQL | SQL Server | Oracle
865+
866+
| `LocalDate` | `DATE` | `DATE` | `DATE` | `DATE` 💀
867+
| `LocalTime` | `TIME` | `TIME` | `TIME` | `TIMESTAMP` 💀
868+
| `LocalDateTime` | `TIMSTAMP` | `DATETIME` | `DATETIME2` | `TIMESTAMP`
869+
| `OffsetDateTime`, `ZonedDateTime` | `TIMESTAMP WITH TIME ZONE` | `TIMESTAMP` 🙄 | `DATETIMEOFFSET` | `TIMESTAMP WITH TIME ZONE`
870+
|====
871+
872+
On the other hand, there are no perfectly natural mappings for `Instant` and `Duration` on must databases.
873+
By default:
874+
875+
- `Duration` is mapped to a column of type `NUMERIC(21)` holding the length of the duration in nanoseconds, and
876+
- `Instant` is mapped to a column of type `TIMESTAMP` (`DATETIME` on MySQL).
877+
878+
Fortunately, these mappings can be modified by specifying the `JdbcType`.
879+
880+
For example, if we wanted to store an `Instant` using `TIMESTAMP WITH TIME ZONE` (`TIMESTAMP` on MySQL) instead of `TIMESTAMP`, then we could annotate the field:
881+
882+
[source,java]
883+
----
884+
// store the Instant as a TIMESTAMP WITH TIME ZONE, instead of as a TIMESTAMP
885+
@JdbcTypeCode(SqlTypes.TIMESTAMP_WITH_TIMEZONE)
886+
Instant instant;
887+
----
888+
889+
Alternatively, we could set the configuration property `hibernate.type.preferred_instant_jdbc_type`:
890+
891+
892+
[source,java]
893+
----
894+
// store field of type Instant as TIMESTAMP WITH TIME ZONE, instead of as a TIMESTAMP
895+
config.setProperty(MappingSettings.PREFERRED_INSTANT_JDBC_TYPE, SqlTypes.TIMESTAMP_WITH_TIMEZONE);
896+
----
897+
898+
We have worked very hard to make sure that Java date and time types work with consistent and correct semantics across all databases supported by Hibernate.
899+
In particular, Hibernate is very careful in how it handles time zones.
900+
901+
[WARNING]
902+
====
903+
Unfortunately, with the notable exception of Oracle, most SQL databases feature embarrassingly poor support for timezones.
904+
Even some databases which do supposedly support `TIMESTAMP WITH TIME ZONE` simply covert the datetime to UTC.
905+
Here, Hibernate is limited by the capabilities of the databases themselves, and so on many databases, time zone information will not, by default, be preserved for an `OffsetDateTime` or `ZonedDateTime`.
906+
====
907+
908+
[TIP]
909+
====
910+
The still-experimental annotation link:{doc-javadoc-url}org/hibernate/annotations/TimeZoneStorage.html[`@TimeZoneStorage`] provides some additional options in case the default behavior falls short.
911+
//
912+
// [source,java]
913+
// ----
914+
// @TimeZoneStorage(COLUMN)
915+
// @TimeZoneColumn(name = "event_offset")
916+
// @Column(name = "event_timestamp")
917+
// private OffsetDateTime eventDateTime;
918+
// ----
919+
====
920+
830921
[[embeddable-objects]]
831922
=== Embeddable objects
832923

documentation/src/main/asciidoc/introduction/Mapping.adoc

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,7 @@ Of course, the behavior here depends very much on the JDBC driver, and so we rea
734734

735735
There's a couple of alternative ways to represent an embeddable type on the database side.
736736

737+
[[embeddable-udt]]
737738
[discrete]
738739
==== Embeddables as UDTs
739740

@@ -818,32 +819,34 @@ Here we summarize the ones we've just seen in the second half of this chapter, a
818819
|===
819820
| Annotation | Interpretation
820821

821-
| `@Enumerated`, `@EnumeratedValue` | Specify how an `enum` type should be persisted
822-
| `@Nationalized` | Use a nationalized character type: `NCHAR`, `NVARCHAR`, or `NCLOB`
823-
| `@Lob` 💀 | Use JDBC LOB APIs to read and write the annotated attribute
824-
| `@Array` | Map a collection to a SQL `ARRAY` type of the specified length
825-
| `@Struct` | Map an embeddable to a SQL UDT with the given name
826-
| `@TimeZoneStorage` | Specify how the time zone information should be persisted
827-
| `@JdbcType` or `@JdbcTypeCode` | Use an implementation of `JdbcType` to map an arbitrary SQL type
822+
| `@Enumerated`, `@EnumeratedValue` | Specify how an <<enums,`enum` type should be persisted>>
823+
| `@Nationalized` | Use a <<nationalized-chars,nationalized character type>>: `NCHAR`, `NVARCHAR`, or `NCLOB`
824+
| `@Lob` 💀 | Use <<lobs,JDBC LOB APIs>> to read and write the annotated attribute
825+
| `@Array` | Map a collection to a <<arrays,SQL `ARRAY` type>> of the specified length
826+
| `@Struct` | Map an <<embeddable-udt,embeddable to a SQL UDT>> with the given name
827+
| `@TimeZoneStorage` | Specify how the link:{doc-javadoc-url}org/hibernate/annotations/TimeZoneStorageType.html[time zone information should be persisted]
828+
| `@JdbcType` or `@JdbcTypeCode` | Use an implementation of <<jdbc-type,`JdbcType`>> to map an arbitrary SQL type
828829
| `@Collate` | Specify a collation for a column
829830
|===
830831

831-
In addition, there are some configuration properties which have a _global_ affect on how basic types map to SQL column types:
832+
In addition, there are link:{doc-javadoc-url}org/hibernate/cfg/MappingSettings.html[some configuration properties] which have a _global_ effect on how basic types map to SQL column types:
832833

833834
.Type mapping settings
834835
[%autowidth.stretch]
835836
|===
836837
| Configuration property name | Purpose
837838

838-
| `hibernate.use_nationalized_character_data` | Enable use of nationalized character types by default
839-
| `hibernate.type.preferred_boolean_jdbc_type` | Specify the default SQL column type for mapping `boolean`
840-
| `hibernate.type.preferred_uuid_jdbc_type` | Specify the default SQL column type for mapping `UUID`
841-
| `hibernate.type.preferred_duration_jdbc_type` | Specify the default SQL column type for mapping `Duration`
842-
| `hibernate.type.preferred_instant_jdbc_type` | Specify the default SQL column type for mapping `Instant`
843-
| `hibernate.timezone.default_storage` | Specify the default strategy for storing time zone information
844-
| `` |
839+
| `hibernate.use_nationalized_character_data` | Enable use of <<nationalized-chars,nationalized character types>> by default
840+
| `hibernate.type.preferred_boolean_jdbc_type` | Specify the default SQL column type for storing a `boolean`
841+
| `hibernate.type.preferred_uuid_jdbc_type` | Specify the default SQL column type for storing a `UUID`
842+
| `hibernate.type.preferred_duration_jdbc_type` | Specify the default SQL column type for storing a `Duration`
843+
| `hibernate.type.preferred_instant_jdbc_type` | Specify the default SQL column type for storing an `Instant`
844+
| `hibernate.timezone.default_storage` | Specify the default strategy for link:{doc-javadoc-url}org/hibernate/annotations/TimeZoneStorageType.html[storing time zone information]
845+
| `hibernate.type.prefer_native_enum_types` | Use <<named-enums,named enum types>> on PostgreSQL and Oracle
845846
|===
846847

848+
Earlier, we saw how to use these settings to control the default mappings for <<datetime-types,`Instant` and `Duration`>>.
849+
847850
[TIP]
848851
====
849852
These are _global_ settings and thus quite clumsy.
@@ -863,7 +866,7 @@ Thus, the attribute is a sort of "derived" value.
863866
|===
864867
| Annotation | Purpose
865868

866-
| `@Formula` | Map an attribute to a SQL formula
869+
| link:{doc-javadoc-url}org/hibernate/annotations/Formula.html[`@Formula`] | Map an attribute to a SQL formula
867870
| `@JoinFormula` | Map an association to a SQL formula
868871
| `@DiscriminatorFormula` | Use a SQL formula as the discriminator in <<mapping-inheritance,single table inheritance>>.
869872
|===
@@ -887,6 +890,8 @@ class Order {
887890
}
888891
----
889892

893+
The formula is evaluated every time the entity is read from the database.
894+
890895
[[derived-identity]]
891896
=== Derived Identity
892897

documentation/src/main/asciidoc/querylanguage/Expressions.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,7 @@ Its BNF is given by:
941941
"PAD" "(" expression "WITH" expression ("LEADING" | "TRAILING") padCharacter? ")"
942942
----
943943

944+
[[collations]]
944945
[discrete]
945946
===== Collations
946947

hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
210210
private boolean orderUpdatesEnabled;
211211
private boolean orderInsertsEnabled;
212212
private boolean collectionsInDefaultFetchGroupEnabled = true;
213-
private final boolean UnownedAssociationTransientCheck;
213+
private final boolean unownedAssociationTransientCheck;
214214
private final boolean passProcedureParameterNames;
215215
private final boolean preferJdbcDatetimeTypes;
216216

@@ -597,14 +597,14 @@ else if ( defaultNullPrecedence != null ) {
597597
JDBC_TIME_ZONE
598598
);
599599

600-
if ( jdbcTimeZoneValue instanceof TimeZone ) {
601-
this.jdbcTimeZone = (TimeZone) jdbcTimeZoneValue;
600+
if ( jdbcTimeZoneValue instanceof TimeZone timeZone ) {
601+
this.jdbcTimeZone = timeZone;
602602
}
603-
else if ( jdbcTimeZoneValue instanceof ZoneId ) {
604-
this.jdbcTimeZone = TimeZone.getTimeZone( (ZoneId) jdbcTimeZoneValue );
603+
else if ( jdbcTimeZoneValue instanceof ZoneId zoneId ) {
604+
this.jdbcTimeZone = TimeZone.getTimeZone( zoneId );
605605
}
606-
else if ( jdbcTimeZoneValue instanceof String ) {
607-
this.jdbcTimeZone = TimeZone.getTimeZone( ZoneId.of((String) jdbcTimeZoneValue) );
606+
else if ( jdbcTimeZoneValue instanceof String string ) {
607+
this.jdbcTimeZone = TimeZone.getTimeZone( ZoneId.of( string ) );
608608
}
609609
else if ( jdbcTimeZoneValue != null ) {
610610
throw new IllegalArgumentException( "Configuration property " + JDBC_TIME_ZONE
@@ -665,7 +665,7 @@ else if ( jdbcTimeZoneValue != null ) {
665665
Statistics.DEFAULT_QUERY_STATISTICS_MAX_SIZE
666666
);
667667

668-
this.UnownedAssociationTransientCheck = getBoolean(
668+
this.unownedAssociationTransientCheck = getBoolean(
669669
UNOWNED_ASSOCIATION_TRANSIENT_CHECK,
670670
configurationSettings,
671671
isJpaBootstrap()
@@ -1305,7 +1305,7 @@ public boolean isCollectionsInDefaultFetchGroupEnabled() {
13051305

13061306
@Override
13071307
public boolean isUnownedAssociationTransientCheck() {
1308-
return UnownedAssociationTransientCheck;
1308+
return unownedAssociationTransientCheck;
13091309
}
13101310

13111311
@Override

hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
import org.hibernate.mapping.SimpleValue;
9595
import org.hibernate.mapping.SingleTableSubclass;
9696
import org.hibernate.mapping.Subclass;
97+
import org.hibernate.mapping.SyntheticProperty;
9798
import org.hibernate.mapping.Table;
9899
import org.hibernate.mapping.TableOwner;
99100
import org.hibernate.mapping.UnionSubclass;
@@ -566,7 +567,7 @@ private Component createMapperProperty(
566567
propertyAccessor,
567568
isIdClass
568569
);
569-
final Property mapperProperty = new Property();
570+
final Property mapperProperty = new SyntheticProperty();
570571
mapperProperty.setName( NavigablePath.IDENTIFIER_MAPPER_PROPERTY );
571572
mapperProperty.setUpdateable( false );
572573
mapperProperty.setInsertable( false );

0 commit comments

Comments
 (0)