From b7dc7292ca68773cb52907e34f89aedc16e06aa0 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 22 Oct 2024 19:40:06 +0200 Subject: [PATCH] Documentation updates for JPA 3.2 Signed-off-by: Gavin King --- .../asciidoc/introduction/Configuration.adoc | 80 +++++++++++-------- .../main/asciidoc/introduction/Entities.adoc | 20 ++++- .../asciidoc/introduction/Introduction.adoc | 8 +- .../main/asciidoc/introduction/Mapping.adoc | 20 ++++- 4 files changed, 84 insertions(+), 44 deletions(-) diff --git a/documentation/src/main/asciidoc/introduction/Configuration.adoc b/documentation/src/main/asciidoc/introduction/Configuration.adoc index 55aa2b4fa4..d1c746366d 100644 --- a/documentation/src/main/asciidoc/introduction/Configuration.adoc +++ b/documentation/src/main/asciidoc/introduction/Configuration.adoc @@ -4,7 +4,7 @@ We would love to make this section short. Unfortunately, there are several distinct ways to configure and bootstrap Hibernate, and we're going to have to describe at least two of them in detail. -The four basic ways to obtain an instance of Hibernate are shown in the following table: +The five basic ways to obtain an instance of Hibernate are shown in the following table: [%breakable,cols="50,50",number=0] |=== @@ -12,8 +12,11 @@ The four basic ways to obtain an instance of Hibernate are shown in the followin | Using the standard JPA-defined XML, and the operation `Persistence.createEntityManagerFactory()` | Usually chosen when portability between JPA implementations is important. -| Using the link:{doc-javadoc-url}org/hibernate/cfg/Configuration.html[`Configuration`] class to construct a `SessionFactory` -| When portability between JPA implementations is not important, this option is quicker, adds some flexibility and saves a typecast. +| Using the standard JPA-defined `PersistenceConfiguration` class +| Usually chosen when portability between JPA implementations is important, but programmatic control is desired. + +| Using link:{doc-javadoc-url}org/hibernate/jpa/HibernatePersistenceConfiguration.html[`HibernatePersistenceConfiguration`] or the older link:{doc-javadoc-url}org/hibernate/cfg/Configuration.html[`Configuration`] class to construct a `SessionFactory` +| When portability between JPA implementations is not important, this option adds some convenience and saves a typecast. | Using the more complex APIs defined in link:{doc-javadoc-url}org/hibernate/boot/package-summary.html[`org.hibernate.boot`] | Used primarily by framework integrators, this option is outside the scope of this document. @@ -102,7 +105,7 @@ or `org.slf4j:slf4j-jdk14` | A JDBC connection pool, for example, {agroal}[Agroal] | `org.hibernate.orm:hibernate-agroal` + and `io.agroal:agroal-pool` -| The {generator}[Hibernate Metamodel Generator], especially if you're using the JPA criteria query API | `org.hibernate.orm:hibernate-processor` +| The {generator}[Hibernate Processor], especially if you're using Jakarta Data or the JPA criteria query API | `org.hibernate.orm:hibernate-processor` | The {query-validator}[Query Validator], for compile-time checking of HQL | `org.hibernate:query-validator` | {validator}[Hibernate Validator], an implementation of {bean-validation}[Bean Validation] | `org.hibernate.validator:hibernate-validator` + @@ -202,41 +205,54 @@ EntityManagerFactory entityManagerFactory = ---- [[configuration-api]] -=== Configuration using Hibernate API +=== Programmatic configuration using JPA API -Alternatively, the venerable class link:{doc-javadoc-url}org/hibernate/cfg/Configuration.html[`Configuration`] allows an instance of Hibernate to be configured in Java code. +The new `PersistenceConfiguration` class allows full programmatic control over creation of the `EntityManagerFactory`. + +[source,java] +---- +EntityManagerFactory entityManagerFactory = + new PersistenceConfiguration("Bookshop") + .managedClass(Book.class) + .managedClass(Author.class) + // PostgreSQL + .property(PersistenceConfiguration.JDBC_URL, "jdbc:postgresql://localhost/example") + // Credentials + .property(PersistenceConfiguration.JDBC_USER, user) + .property(PersistenceConfiguration.JDBC_PASSWORD, password) + // Automatic schema export + .property(PersistenceConfiguration.SCHEMAGEN_DATABASE_ACTION, + Action.SPEC_ACTION_DROP_AND_CREATE) + // SQL statement logging + .property(JdbcSettings.SHOW_SQL, true) + .property(JdbcSettings.FORMAT_SQL, true) + .property(JdbcSettings.HIGHLIGHT_SQL, true) + // Create a new EntityManagerFactory + .createEntityManagerFactory(); +---- + +The specification gives JPA implementors like Hibernate explicit permission to extend this class, and so Hibernate offers the link:{doc-javadoc-url}org/hibernate/jpa/HibernatePersistenceConfiguration.html[`HibernatePersistenceConfiguration`], which lets us obtain a `SessionFactory` without any need for a cast. [source,java] ---- SessionFactory sessionFactory = - new Configuration() - .addAnnotatedClass(Book.class) - .addAnnotatedClass(Author.class) - // PostgreSQL - .setProperty(AvailableSettings.JAKARTA_JDBC_URL, "jdbc:postgresql://localhost/example") - // Credentials - .setProperty(AvailableSettings.JAKARTA_JDBC_USER, user) - .setProperty(AvailableSettings.JAKARTA_JDBC_PASSWORD, password) - // Automatic schema export - .setProperty(AvailableSettings.JAKARTA_HBM2DDL_DATABASE_ACTION, - Action.SPEC_ACTION_DROP_AND_CREATE) - // SQL statement logging - .setProperty(AvailableSettings.SHOW_SQL, true) - .setProperty(AvailableSettings.FORMAT_SQL, true) - .setProperty(AvailableSettings.HIGHLIGHT_SQL, true) + new HibernatePersistenceConfiguration("Bookshop") + .managedClass(Book.class) + .managedClass(Author.class) + // Set properties + ... // Create a new SessionFactory - .buildSessionFactory(); + .createEntityManagerFactory(); ---- -The `Configuration` class has survived almost unchanged since the very earliest (pre-1.0) versions of Hibernate, and so it doesn't look particularly modern. -On the other hand, it's very easy to use, and exposes some options that `persistence.xml` doesn't support. +Alternatively, the venerable class link:{doc-javadoc-url}org/hibernate/cfg/Configuration.html[`Configuration`] offers similar functionality. :native-bootstrap: {doc-user-guide-url}#bootstrap-native :boot: {doc-javadoc-url}/org/hibernate/boot/package-summary.html .Advanced configuration options **** -Actually, the `Configuration` class is just a very simple facade for the more modern, much more powerful—but more complex—API defined in the package `org.hibernate.boot`. +Actually, these APIs are very simple facades resting on the much more powerful--but also more complex--APIs defined in the package `org.hibernate.boot`. This API is useful if you have very advanced requirements, for example, if you're writing a framework or implementing a container. You'll find more information in the {native-bootstrap}[User Guide], and in the {boot}[package-level documentation] of `org.hibernate.boot`. **** @@ -244,7 +260,7 @@ You'll find more information in the {native-bootstrap}[User Guide], and in the { [[configuration-properties]] === Configuration using Hibernate properties file -If we're using the Hibernate `Configuration` API, but we don't want to put certain configuration properties directly in the Java code, we can specify them in a file named `hibernate.properties`, and place the file in the root classpath. +If we're using programmatic configuration, but we don't want to put certain configuration properties directly in the Java code, we can specify them in a file named `hibernate.properties`, and place the file in the root classpath. [source,properties] ---- @@ -263,7 +279,9 @@ hibernate.highlight_sql=true [[basic-configuration-settings]] === Basic configuration settings -The class link:{doc-javadoc-url}org/hibernate/cfg/AvailableSettings.html[`AvailableSettings`] enumerates all the configuration properties understood by Hibernate. +The `PersistenceConfiguration` class declares `static final` constants holding the names of all configuration properties defined by the specification itself, for example, `JDBC_URL` holds the property name `"jakarta.persistence.jdbc.driver"`. + +Similarly, the class link:{doc-javadoc-url}org/hibernate/cfg/AvailableSettings.html[`AvailableSettings`] enumerates all the configuration properties understood by Hibernate. Of course, we're not going to cover every useful configuration setting in this chapter. Instead, we'll mention the ones you need to get started, and come back to some other important settings later, especially when we talk about performance tuning. @@ -408,13 +426,7 @@ As we mentioned <>, it can also be useful to control schema exp The link:{doc-javadoc-url}org/hibernate/relational/SchemaManager.html[`SchemaManager`] API allows programmatic control over schema export: [source,java] -sessionFactory.getSchemaManager().exportMappedObjects(true); - -JPA has a more limited and less ergonomic API: - -[source,java] -Persistence.generateSchema("org.hibernate.example", - Map.of(JAKARTA_HBM2DDL_DATABASE_ACTION, CREATE)) +sessionFactory.getSchemaManager().export(true); ==== [[logging-generated-sql]] diff --git a/documentation/src/main/asciidoc/introduction/Entities.adoc b/documentation/src/main/asciidoc/introduction/Entities.adoc index 3405de59ce..b604ffd810 100644 --- a/documentation/src/main/asciidoc/introduction/Entities.adoc +++ b/documentation/src/main/asciidoc/introduction/Entities.adoc @@ -497,7 +497,7 @@ The JPA specification defines a quite limited set of basic types: | Primitive wrappers | `java.lang` | `Boolean`, `Integer`, `Double`, etc | Strings | `java.lang` | `String` | Arbitrary-precision numeric types | `java.math` | `BigInteger`, `BigDecimal` -| Date/time types | `java.time` | `LocalDate`, `LocalTime`, `LocalDateTime`, `OffsetDateTime`, `Instant` +| Date/time types | `java.time` | `LocalDate`, `LocalTime`, `LocalDateTime`, `OffsetDateTime`, `Instant`, `Year` | Deprecated date/time types 💀 | `java.util` | `Date`, `Calendar` | Deprecated JDBC date/time types 💀 | `java.sql` | `Date`, `Time`, `Timestamp` | Binary and character arrays | | `byte[]`, `char[]` @@ -600,7 +600,23 @@ Status status; ---- -In Hibernate 6, an `enum` annotated `@Enumerated(STRING)` is mapped to: +The `@EnumeratedValue` annotation allows the column value to be customized: + +[source,java] +---- +enum Resolution { + UNRESOLVED(0), FIXED(1), REJECTED(-1); + + @EnumeratedValue // store the code, not the enum ordinal() value + final int code; + + Resolution(int code) { + this.code = code; + } +} +---- + +Since Hibernate 6, an `enum` annotated `@Enumerated(STRING)` is mapped to: - a `VARCHAR` column type with a `CHECK` constraint on most databases, or - an `ENUM` column type on MySQL. diff --git a/documentation/src/main/asciidoc/introduction/Introduction.adoc b/documentation/src/main/asciidoc/introduction/Introduction.adoc index d10f4bc864..16a196fc92 100644 --- a/documentation/src/main/asciidoc/introduction/Introduction.adoc +++ b/documentation/src/main/asciidoc/introduction/Introduction.adoc @@ -625,11 +625,11 @@ configuration.setProperty(AvailableSettings.JAKARTA_HBM2DDL_DATABASE_ACTION, Action.SPEC_ACTION_DROP_AND_CREATE); ---- -Alternatively, in Hibernate 6, we may use the new link:{doc-javadoc-url}org/hibernate/relational/SchemaManager.html[`SchemaManager`] API to export the schema, just as we did <>. +Alternatively, we may use the new link:{doc-javadoc-url}org/hibernate/relational/SchemaManager.html[`SchemaManager`] API to export the schema, just as we did <>. [source,java] ---- -sessionFactory.getSchemaManager().exportMappedObjects(true); +sessionFactory.getSchemaManager().export(true); ---- Since executing DDL statements is very slow on many databases, we don't want to do this before every test. @@ -642,7 +642,7 @@ We may truncate all the tables, leaving an empty database schema, using the `Sch [source,java] ---- -sessionFactory.getSchemaManager().truncateMappedObjects(); +sessionFactory.getSchemaManager().truncate(); ---- After truncating tables, we might need to initialize our test data. @@ -668,7 +668,7 @@ configuration.setProperty(AvailableSettings.JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE, "/org/example/test-data.sql"); ---- -The SQL script will be executed every time `exportMappedObjects()` or `truncateMappedObjects()` is called. +The SQL script will be executed every time `export()` or `truncate()` is called. [TIP] ==== diff --git a/documentation/src/main/asciidoc/introduction/Mapping.adoc b/documentation/src/main/asciidoc/introduction/Mapping.adoc index d40fcb0cb7..8d254a393b 100644 --- a/documentation/src/main/asciidoc/introduction/Mapping.adoc +++ b/documentation/src/main/asciidoc/introduction/Mapping.adoc @@ -4,7 +4,7 @@ Given a domain model—that is, a collection of entity classes decorated with all the fancy annotations we <> in the previous chapter—Hibernate will happily go away and infer a complete relational schema, and even <> if you ask politely. The resulting schema will be entirely sane and reasonable, though if you look closely, you'll find some flaws. -For example, every `VARCHAR` column will have the same length, `VARCHAR(255)`. +For example, by default, every `VARCHAR` column will have the same length, `VARCHAR(255)`. But the process I just described—which we call _top down_ mapping—simply doesn't fit the most common scenario for the use of O/R mapping. It's only rarely that the Java classes precede the relational schema. @@ -220,6 +220,8 @@ The `@Table` annotation can do more than just specify a name: | `catalog` 💀 | The catalog to which the table belongs | `uniqueConstraints` | One or more `@UniqueConstraint` annotations declaring multi-column unique constraints | `indexes` | One or more `@Index` annotations each declaring an index +| `check` | One or more `@CheckConstraint` annotations declaring multi-column check constraints +| `comment` | A DDL comment |=== [%unbreakable] @@ -249,6 +251,8 @@ The `@SecondaryTable` annotation is even more interesting: | `indexes` | One or more `@Index` annotations each declaring an index | `pkJoinColumns` | One or more `@PrimaryKeyJoinColumn` annotations, specifying <> | `foreignKey` | A `@ForeignKey` annotation specifying the name of the `FOREIGN KEY` constraint on the ``@PrimaryKeyJoinColumn``s +| `check` | One or more `@CheckConstraint` annotations declaring multi-column check constraints +| `comment` | A DDL comment |=== [TIP] @@ -324,6 +328,8 @@ Here, there should be a `UNIQUE` constraint on _both_ columns of the association | `inverseJoinColumns` | One or more `@JoinColumn` annotations, specifying <> to the table of the unowned side | `foreignKey` | A `@ForeignKey` annotation specifying the name of the `FOREIGN KEY` constraint on the ``joinColumns``s | `inverseForeignKey` | A `@ForeignKey` annotation specifying the name of the `FOREIGN KEY` constraint on the ``inverseJoinColumns``s +| `check` | One or more `@CheckConstraint` annotations declaring multi-column check constraints +| `comment` | A DDL comment |=== To better understand these annotations, we must first discuss column mappings in general. @@ -360,13 +366,16 @@ The `@Column` annotation is not only useful for specifying the column name. | `name` | The name of the mapped column | `table` | The name of the table to which this column belongs | `length` | The length of a `VARCHAR`, `CHAR`, or `VARBINARY` column type -| `precision` | The decimal digits of precision of a `FLOAT`, `DECIMAL`, `NUMERIC`, or `TIME`, or `TIMESTAMP` column type +| `precision` | The decimal digits of precision of a `FLOAT`, `DECIMAL`, or `NUMERIC` type | `scale` | The scale of a `DECIMAL` or `NUMERIC` column type, the digits of precision that occur to the right of the decimal point +| `secondPrecision` | The digits of precision occurring to the right of the decimal point in the seconds field of a `TIME`, or `TIMESTAMP` column type | `unique` | Whether the column has a `UNIQUE` constraint | `nullable` | Whether the column has a `NOT NULL` constraint | `insertable` | Whether the column should appear in generated SQL `INSERT` statements | `updatable` | Whether the column should appear in generated SQL `UPDATE` statements | `columnDefinition` 💀| A DDL fragment that should be used to declare the column +| `check` | One or more `@CheckConstraint` annotations declaring single-column check constraints +| `comment` | A DDL comment |=== [TIP] @@ -420,6 +429,8 @@ The `@JoinColumn` annotation is used to customize a foreign key column. | `updatable` | Whether the column should appear in generated SQL `UPDATE` statements | `columnDefinition` 💀| A DDL fragment that should be used to declare the column | `foreignKey` | A `@ForeignKey` annotation specifying the name of the `FOREIGN KEY` constraint +| `check` | One or more `@CheckConstraint` annotations declaring single-column check constraints +| `comment` | A DDL comment |=== A foreign key column doesn't necessarily have to refer to the primary key of the referenced table. @@ -593,7 +604,7 @@ class Book { === Column lengths and adaptive column types Hibernate automatically adjusts the column type used in generated DDL based on the column length specified by the `@Column` annotation. -So we don't usually need to explicitly specify that a column should be of type `TEXT` or `CLOB`—or worry about the parade of `TINYTEXT`, `MEDIUMTEXT`, `TEXT`, `LONGTEXT` types on MySQL—because Hibernate will automatically select one of those types if required to accommodate a string of the `length` we specify. +So we don't usually need to explicitly specify that a column should be of type `TEXT` or `CLOB`—or worry about the parade of `TINYTEXT`, `MEDIUMTEXT`, `TEXT`, `LONGTEXT` types on MySQL—because Hibernate automatically selects one of those types if required to accommodate a string of the `length` we specify. The constant values defined in the class link:{doc-javadoc-url}org/hibernate/Length.html[`Length`] are very helpful here: @@ -805,7 +816,7 @@ Here we summarize the ones we've just seen in the second half of this chapter, a |=== | Annotation | Interpretation -| `@Enumerated` | Specify how an `enum` type should be persisted +| `@Enumerated`, `@EnumeratedValue` | Specify how an `enum` type should be persisted | `@Nationalized` | Use a nationalized character type: `NCHAR`, `NVARCHAR`, or `NCLOB` | `@Lob` 💀 | Use JDBC LOB APIs to read and write the annotated attribute | `@Array` | Map a collection to a SQL `ARRAY` type of the specified length @@ -828,6 +839,7 @@ In addition, there are some configuration properties which have a _global_ affec | `hibernate.type.preferred_duration_jdbc_type` | Specify the default SQL column type for mapping `Duration` | `hibernate.type.preferred_instant_jdbc_type` | Specify the default SQL column type for mapping `Instant` | `hibernate.timezone.default_storage` | Specify the default strategy for storing time zone information +| `` | |=== [TIP]