diff --git a/documentation/src/main/asciidoc/introduction/Configuration.adoc b/documentation/src/main/asciidoc/introduction/Configuration.adoc index 55aa2b4fa4ac..d1c746366dd7 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] ---- -SessionFactory sessionFactory = - new Configuration() - .addAnnotatedClass(Book.class) - .addAnnotatedClass(Author.class) +EntityManagerFactory entityManagerFactory = + new PersistenceConfiguration("Bookshop") + .managedClass(Book.class) + .managedClass(Author.class) // PostgreSQL - .setProperty(AvailableSettings.JAKARTA_JDBC_URL, "jdbc:postgresql://localhost/example") + .property(PersistenceConfiguration.JDBC_URL, "jdbc:postgresql://localhost/example") // Credentials - .setProperty(AvailableSettings.JAKARTA_JDBC_USER, user) - .setProperty(AvailableSettings.JAKARTA_JDBC_PASSWORD, password) + .property(PersistenceConfiguration.JDBC_USER, user) + .property(PersistenceConfiguration.JDBC_PASSWORD, password) // Automatic schema export - .setProperty(AvailableSettings.JAKARTA_HBM2DDL_DATABASE_ACTION, - Action.SPEC_ACTION_DROP_AND_CREATE) + .property(PersistenceConfiguration.SCHEMAGEN_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) + .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 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 3405de59ce62..b604ffd810a6 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/Hibernate_Introduction.adoc b/documentation/src/main/asciidoc/introduction/Hibernate_Introduction.adoc index cf93d8d8b9a5..0f6da553b8f5 100644 --- a/documentation/src/main/asciidoc/introduction/Hibernate_Introduction.adoc +++ b/documentation/src/main/asciidoc/introduction/Hibernate_Introduction.adoc @@ -6,7 +6,7 @@ include::{shared-attributes-dir}/filesystem-attributes.adoc[] include::{shared-attributes-dir}/renderer-attributes.adoc[] -= An Introduction to Hibernate 6 += An Introduction to Hibernate 7 :title-logo-image: image:../../style/asciidoctor/images/org/hibernate/logo.png[] :toc: :toclevels: 3 diff --git a/documentation/src/main/asciidoc/introduction/Introduction.adoc b/documentation/src/main/asciidoc/introduction/Introduction.adoc index d10f4bc864ac..e77e7a0b6c9f 100644 --- a/documentation/src/main/asciidoc/introduction/Introduction.adoc +++ b/documentation/src/main/asciidoc/introduction/Introduction.adoc @@ -167,7 +167,7 @@ dependencies { // logging via Log4j implementation 'org.apache.logging.log4j:log4j-core:2.20.0' - // JPA Metamodel Generator + // Hibernate Processor annotationProcessor 'org.hibernate.orm:hibernate-processor:{fullVersion}' // Compile-time checking for HQL @@ -239,30 +239,31 @@ It's the job of this Introduction to make all this crystal clear. ---- package org.hibernate.example; -import org.hibernate.cfg.Configuration; +import org.hibernate.jpa.HibernatePersistenceConfiguration; -import static java.lang.Boolean.TRUE; import static java.lang.System.out; -import static org.hibernate.cfg.AvailableSettings.*; +import static jakarta.persistence.PersistenceConfiguration.*; +import static org.hibernate.cfg.JdbcSettings.*; public class Main { public static void main(String[] args) { - var sessionFactory = new Configuration() - .addAnnotatedClass(Book.class) - // use H2 in-memory database - .setProperty(URL, "jdbc:h2:mem:db1") - .setProperty(USER, "sa") - .setProperty(PASS, "") - // use Agroal connection pool - .setProperty("hibernate.agroal.maxSize", 20) - // display SQL in console - .setProperty(SHOW_SQL, true) - .setProperty(FORMAT_SQL, true) - .setProperty(HIGHLIGHT_SQL, true) - .buildSessionFactory(); + var sessionFactory = + new HibernatePersistenceConfiguration("Bookshelf") + .managedClass(Book.class) + // use H2 in-memory database + .property(JDBC_URL, "jdbc:h2:mem:db1") + .property(JDBC_USER, "sa") + .property(JDBC_PASSWORD, "") + // use Agroal connection pool + .property("hibernate.agroal.maxSize", 20) + // display SQL in console + .property(SHOW_SQL, true) + .property(FORMAT_SQL, true) + .property(HIGHLIGHT_SQL, true) + .createEntityManagerFactory(); // export the inferred database schema - sessionFactory.getSchemaManager().exportMappedObjects(true); + sessionFactory.getSchemaManager().export(true); // persist an entity sessionFactory.inTransaction(session -> { @@ -287,124 +288,122 @@ public class Main { } ---- -Here we've used Hibernate's native APIs. -We could have used JPA-standard APIs to achieve the same thing. - -[[hello-jpa]] -=== Hello, JPA - -If we limit ourselves to the use of JPA-standard APIs, we need to use XML to configure Hibernate. - -[source,xml] -.`META-INF/persistence.xml` ----- - - - - - org.hibernate.example.Book - - - - - - - - - - - - - - - - - - - - - - ----- - -Note that our `build.gradle` and `log4j2.properties` files are unchanged. - -Our entity class is also unchanged from what we had before. - -Unfortunately, JPA doesn't offer an `inSession()` method, so we'll have to implement session and transaction management ourselves. -We can put that logic in our own `inSession()` function, so that we don't have to repeat it for every transaction. -Again, you don't need to understand any of this code right now. - -[[main-jpa]] -[source,java] -.`Main.java` (JPA version) ----- -package org.hibernate.example; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; - -import java.util.Map; -import java.util.function.Consumer; - -import static jakarta.persistence.Persistence.createEntityManagerFactory; -import static java.lang.System.out; -import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_DATABASE_ACTION; -import static org.hibernate.tool.schema.Action.CREATE; - -public class Main { - public static void main(String[] args) { - var factory = createEntityManagerFactory("example", - // export the inferred database schema - Map.of(JAKARTA_HBM2DDL_DATABASE_ACTION, CREATE)); - - // persist an entity - inSession(factory, entityManager -> { - entityManager.persist(new Book("9781932394153", "Hibernate in Action")); - }); - - // query data using HQL - inSession(factory, entityManager -> { - out.println(entityManager.createQuery("select isbn||': '||title from Book").getSingleResult()); - }); - - // query data using criteria API - inSession(factory, entityManager -> { - var builder = factory.getCriteriaBuilder(); - var query = builder.createQuery(String.class); - var book = query.from(Book.class); - query.select(builder.concat(builder.concat(book.get(Book_.isbn), builder.literal(": ")), - book.get(Book_.title))); - out.println(entityManager.createQuery(query).getSingleResult()); - }); - } - - // do some work in a session, performing correct transaction management - static void inSession(EntityManagerFactory factory, Consumer work) { - var entityManager = factory.createEntityManager(); - var transaction = entityManager.getTransaction(); - try { - transaction.begin(); - work.accept(entityManager); - transaction.commit(); - } - catch (Exception e) { - if (transaction.isActive()) transaction.rollback(); - throw e; - } - finally { - entityManager.close(); - } - } -} ----- +// +// [[hello-jpa]] +// === Hello, JPA +// +// If we limit ourselves to the use of JPA-standard APIs, we need to use XML to configure Hibernate. +// +// [source,xml] +// .`META-INF/persistence.xml` +// ---- +// +// +// +// +// org.hibernate.example.Book +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// ---- +// +// Note that our `build.gradle` and `log4j2.properties` files are unchanged. +// +// Our entity class is also unchanged from what we had before. +// +// Unfortunately, JPA doesn't offer an `inSession()` method, so we'll have to implement session and transaction management ourselves. +// We can put that logic in our own `inSession()` function, so that we don't have to repeat it for every transaction. +// Again, you don't need to understand any of this code right now. +// +// [[main-jpa]] +// [source,java] +// .`Main.java` (JPA version) +// ---- +// package org.hibernate.example; +// +// import jakarta.persistence.EntityManager; +// import jakarta.persistence.EntityManagerFactory; +// +// import java.util.Map; +// import java.util.function.Consumer; +// +// import static jakarta.persistence.Persistence.createEntityManagerFactory; +// import static java.lang.System.out; +// import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_DATABASE_ACTION; +// import static org.hibernate.tool.schema.Action.CREATE; +// +// public class Main { +// public static void main(String[] args) { +// var factory = createEntityManagerFactory("example", +// // export the inferred database schema +// Map.of(JAKARTA_HBM2DDL_DATABASE_ACTION, CREATE)); +// +// // persist an entity +// inSession(factory, entityManager -> { +// entityManager.persist(new Book("9781932394153", "Hibernate in Action")); +// }); +// +// // query data using HQL +// inSession(factory, entityManager -> { +// out.println(entityManager.createQuery("select isbn||': '||title from Book").getSingleResult()); +// }); +// +// // query data using criteria API +// inSession(factory, entityManager -> { +// var builder = factory.getCriteriaBuilder(); +// var query = builder.createQuery(String.class); +// var book = query.from(Book.class); +// query.select(builder.concat(builder.concat(book.get(Book_.isbn), builder.literal(": ")), +// book.get(Book_.title))); +// out.println(entityManager.createQuery(query).getSingleResult()); +// }); +// } +// +// // do some work in a session, performing correct transaction management +// static void inSession(EntityManagerFactory factory, Consumer work) { +// var entityManager = factory.createEntityManager(); +// var transaction = entityManager.getTransaction(); +// try { +// transaction.begin(); +// work.accept(entityManager); +// transaction.commit(); +// } +// catch (Exception e) { +// if (transaction.isActive()) transaction.rollback(); +// throw e; +// } +// finally { +// entityManager.close(); +// } +// } +// } +// ---- In practice, we never access the database directly from a `main()` method. So now let's talk about how to organize persistence logic in a real system. @@ -425,7 +424,7 @@ We're going to ask you to suppress this urge for now. [TIP] ==== The _easiest_ way to use Hibernate is to call the `Session` or `EntityManager` directly. -If you're new to Hibernate, frameworks which wrap JPA are only going to make your life more difficult. +If you're new to Hibernate, frameworks which wrap JPA are quite likely to make your life more difficult. ==== We prefer a _bottom-up_ approach to organizing our code. @@ -438,11 +437,13 @@ We might start with something like this, a mix of UI and persistence logic: ---- @Path("/") @Produces("application/json") public class BookResource { + @GET @Path("book/{isbn}") public Book getBook(String isbn) { var book = sessionFactory.fromTransaction(session -> session.find(Book.class, isbn)); return book == null ? Response.status(404).build() : book; } + } ---- Indeed, we might also _finish_ with something like that—it's quite hard to identify anything concretely wrong with the code above, and for such a simple case it seems really difficult to justify making this code more complicated by introducing additional objects. @@ -498,7 +499,7 @@ This is an example of a _query method_, a function which accepts arguments to th And that's all it does; it doesn't orchestrate additional program logic, and it doesn't perform transaction or session management. It's even better to specify the query string using the `@NamedQuery` annotation, so that Hibernate can validate the query at startup time, that is, when the `SessionFactory` is created, instead of when the query is first executed. -Indeed, since we included the <> in our <>, the query can even be validated at _compile time_. +Indeed, since we included <> in our <>, the query can even be validated at _compile time_. We need a place to put the annotation, so let's move our query method to a new class: @@ -511,7 +512,7 @@ class Queries { static List findBooksByTitleWithPagination(Session session, String titlePattern, Page page) { - return session.createNamedQuery("findBooksByTitle", Book.class) + return session.createNamedQuery(Queries_._findBooksByTitle_) //type safe reference to the named query .setParameter("title", titlePattern) .setPage(page) .getResultList(); @@ -521,7 +522,7 @@ class Queries { Notice that our query method doesn't attempt to hide the `EntityManager` from its clients. Indeed, the client code is responsible for providing the `EntityManager` or `Session` to the query method. -This is a quite distinctive feature of our whole approach. +// This is a quite distinctive feature of our whole approach. The client code may: @@ -537,16 +538,17 @@ Whatever the case, the code which orchestrates a unit of work usually just calls public List findBooks(String titlePattern) { var books = sessionFactory.fromTransaction(session -> Queries.findBooksByTitleWithPagination(session, titlePattern, - Page.page(RESULTS_PER_PAGE, page)); + Page.page(RESULTS_PER_PAGE, page))); return books.isEmpty() ? Response.status(404).build() : books; } ---- You might be thinking that our query method looks a bit boilerplatey. -That's true, perhaps, but we're much more concerned that it's not very typesafe. +That's true, perhaps, but we're much more concerned that it's still not perfectly typesafe. Indeed, for many years, the lack of compile-time checking for HQL queries and code which binds arguments to query parameters was our number one source of discomfort with Hibernate. +Here, the `@CheckHQL` annotation takes care of checking the query itself, but the call to `setParameter()` is still not type safe. -Fortunately, there's now a solution to both problems: as an incubating feature of Hibernate 6.3, we now offer the possibility to have the Metamodel Generator fill in the implementation of such query methods for you. +Fortunately, there's now a great solution to both problems. Hibernate Processor is able to fill in the implementation of such query methods for us. This facility is the topic of <>, so for now we'll just leave you with one simple example. Suppose we simplify `Queries` to just the following: @@ -559,7 +561,7 @@ interface Queries { } ---- -Then the Metamodel Generator automatically produces an implementation of the method annotated `@HQL` in a class named `Queries_`. +Then Hibernate Processor automatically produces an implementation of the method annotated `@HQL` in a class named `Queries_`. We can call it just like we called our handwritten version: [source,java] @@ -569,7 +571,7 @@ We can call it just like we called our handwritten version: public List findBooks(String titlePattern) { var books = sessionFactory.fromTransaction(session -> Queries_.findBooksByTitleWithPagination(session, titlePattern, - Page.page(RESULTS_PER_PAGE, page)); + Page.page(RESULTS_PER_PAGE, page))); return books.isEmpty() ? Response.status(404).build() : books; } ---- @@ -578,15 +580,41 @@ In this case, the quantity of code eliminated is pretty trivial. The real value is in improved type safety. We now find out about errors in assignments of arguments to query parameters at compile time. -[NOTE] +This is all quite nice so far, but at this point you're probably wondering whether we could use dependency injection to obtain an _instance_ of the `Queries` interface. +Well, indeed we can. +What we need to do is indicate the kind of session the `Queries` interface depends on, by adding a method to retrieve the session. + +[source,java] +---- +interface Queries { + EntityManager entityManager(); + + @HQL("where title like :title order by title") + List findBooksByTitleWithPagination(String title, Page page); +} +---- + +The `Queries` interface is now considered a _repository_, and we may use CDI to inject the repository implementation generated by Hibernate Processor: + +[source,java] +---- +@Inject Queries queries; + +@GET +@Path("books/{titlePattern}") +@Transactional +public List findBooks(String titlePattern) { + var books = queries.findBooksByTitleWithPagination(session, titlePattern, + Page.page(RESULTS_PER_PAGE, page)); + return books.isEmpty() ? Response.status(404).build() : books; +} +---- + +Alternatively, if CDI isn't available, we may directly instantiate the generated repository implementation class using `new Queries_(entityManager)`. + +[TIP] ==== -At this point, we're certain you're full of doubts about this idea. -And quite rightly so. -We would love to answer your objections right here, but that will take us much too far off track. -So we ask you to file away these thoughts for now. -We promise to make it make sense when we <>. -And, after that, if you still don't like this approach, please understand that it's completely optional. -Nobody's going to come around to your house to force it down your throat. +The Jakarta Data specification now formalizes this approach, and Hibernate Processor provides an implementation, which we've branded _Hibernate Data Repositories_. ==== Now that we have a rough picture of what our persistence logic might look like, it's natural to ask how we should test our code. @@ -617,19 +645,19 @@ Whether we're testing against our real database, or against an in-memory Java da We _usually_ do this when we create the Hibernate `SessionFactory` or JPA `EntityManagerFactory`, and so traditionally we've used a <> for this. The JPA-standard property is `jakarta.persistence.schema-generation.database.action`. -For example, if we're using `Configuration` to configure Hibernate, we could write: +For example, if we're using `PersistenceConfiguration` to configure Hibernate, we could write: [source,java] ---- -configuration.setProperty(AvailableSettings.JAKARTA_HBM2DDL_DATABASE_ACTION, +configuration.property(PersistenceConfiguration.SCHEMAGEN_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 +670,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. @@ -660,15 +688,15 @@ insert into Books (isbn, title) values ('9781617290459', 'Java Persistence with If we name this file `import.sql`, and place it in the root classpath, that's all we need to do. Otherwise, we need to specify the file in the <> `jakarta.persistence.sql-load-script-source`. -If we're using `Configuration` to configure Hibernate, we could write: +If we're using `PersistenceConfiguration` to configure Hibernate, we could write: [source,java] ---- -configuration.setProperty(AvailableSettings.JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE, +configuration.property(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] ==== @@ -698,14 +726,14 @@ Another important test we'll need is one which validates our <>. +If you're only interested in objective facts, +// , or if you prefer not to read things that might undermine the opinion you currently hold, +please feel free to skip straight to the <>. ==== Hibernate is an architecture-agnostic library, not a framework, and therefore integrates comfortably with a wide range of Java frameworks and containers. @@ -726,111 +756,118 @@ Consistent with our place within the ecosystem, we've historically avoided givin This is a practice we're now perhaps inclined to regret, since the resulting vacuum has come to be filled with advice from people advocating architectures, design patterns, and extra frameworks which we suspect make Hibernate a bit less pleasant to use than it should be. In particular, frameworks which wrap JPA seem to add bloat while subtracting some of the fine-grained control over data access that Hibernate works so hard to provide. -These frameworks don't expose the full feature set of Hibernate, and so the program is forced to work with a less powerful abstraction. +These frameworks don't expose the full feature set of Hibernate--nor do they provide much interesting functionality of their own--and so the program is forced to work with a less powerful abstraction. The stodgy, dogmatic, _conventional_ wisdom, which we hesitate to challenge for simple fear of pricking ourselves on the erect hackles that inevitably accompany such dogma-baiting is: > Code which interacts with the database belongs in a separate _persistence layer_. We lack the courage—perhaps even the conviction—to tell you categorically to _not_ follow this recommendation. -But we do ask you to consider the cost in boilerplate of any architectural layer, and whether the benefits this cost buys are really worth it in the context of your system. - -To add a little background texture to this discussion, and at the risk of our Introduction degenerating into a rant at such an early stage, we're going ask you to humor us while talk a little more about ancient history. - -[%unbreakable] -.An epic tale of DAOs and Repositories -**** -Back in the dark days of Java EE 4, before the standardization of Hibernate, and subsequent ascendance of JPA in Java enterprise development, it was common to hand-code the messy JDBC interactions that Hibernate takes care of today. -In those terrible times, a pattern arose that we used to call _Data Access Objects_ (DAOs). -A DAO gave you a place to put all that nasty JDBC code, leaving the important program logic cleaner. - -When Hibernate arrived suddenly on the scene in 2001, developers loved it. -But Hibernate implemented no specification, and many wished to reduce or at least _localize_ the dependence of their project logic on Hibernate. -An obvious solution was to keep the DAOs around, but to replace the JDBC code inside them with calls to the Hibernate `Session`. - -We partly blame ourselves for what happened next. - -Back in 2002 and 2003 this really seemed like a pretty reasonable thing to do. -In fact, we contributed to the popularity of this approach by recommending—or at least not discouraging—the use of DAOs in _Hibernate in Action_. -We hereby apologize for this mistake, and for taking much too long to recognize it. - -Eventually, some folks came to believe that their DAOs shielded their program from depending in a hard way on ORM, allowing them to "swap out" Hibernate, and replace it with JDBC, or with something else. -In fact, this was never really true—there's quite a deep difference between the programming model of JDBC, where every interaction with the database is explicit and synchronous, and the programming model of stateful sessions in Hibernate, where updates are implicit, and SQL statements are executed asynchronously. +But we do ask you to consider the cost in boilerplate of any architectural layer, and whether the benefits this cost buys are really worth it, in the context of your system. -But then the whole landscape for persistence in Java changed in April 2006, when the final draft of JPA 1.0 was approved. -Java now had a standard way to do ORM, with multiple high-quality implementations of the standard API. -This was the end of the line for the DAOs, right? - -Well, no. -It wasn't. -DAOs were rebranded "repositories", and continue to enjoy a sort-of zombie afterlife as a front-end to JPA. -But are they really pulling their weight, or are they just unnecessary extra complexity and bloat? An extra layer of indirection that makes stack traces harder to read and code harder to debug? - -Our considered view is that they're mostly just bloat. -The JPA `EntityManager` is a "repository", and it's a standard repository with a well-defined specification written by people who spend all day thinking about persistence. -If these repository frameworks offered anything actually _useful_—and not obviously foot-shooty—over and above what `EntityManager` provides, we would have already added it to `EntityManager` decades ago. -**** +// To add a little background texture to this discussion, and at the risk of our Introduction degenerating into a rant at such an early stage, we're going ask you to humor us while talk a little more about ancient history. +// +// [%unbreakable] +// .An epic tale of DAOs and Repositories +// **** +// Back in the dark days of Java EE 4, before the standardization of Hibernate, and subsequent ascendance of JPA in Java enterprise development, it was common to hand-code the messy JDBC interactions that Hibernate takes care of today. +// In those terrible times, a pattern arose that we used to call _Data Access Objects_ (DAOs). +// A DAO gave you a place to put all that nasty JDBC code, leaving the important program logic cleaner. +// +// When Hibernate arrived suddenly on the scene in 2001, developers loved it. +// But Hibernate implemented no specification, and many wished to reduce or at least _localize_ the dependence of their project logic on Hibernate. +// An obvious solution was to keep the DAOs around, but to replace the JDBC code inside them with calls to the Hibernate `Session`. +// +// We partly blame ourselves for what happened next. +// +// Back in 2002 and 2003 this really seemed like a pretty reasonable thing to do. +// In fact, we contributed to the popularity of this approach by recommending—or at least not discouraging—the use of DAOs in _Hibernate in Action_. +// We hereby apologize for this mistake, and for taking much too long to recognize it. +// +// Eventually, some folks came to believe that their DAOs shielded their program from depending in a hard way on ORM, allowing them to "swap out" Hibernate, and replace it with JDBC, or with something else. +// In fact, this was never really true—there's quite a deep difference between the programming model of JDBC, where every interaction with the database is explicit and synchronous, and the programming model of stateful sessions in Hibernate, where updates are implicit, and SQL statements are executed asynchronously. +// +// But then the whole landscape for persistence in Java changed in April 2006, when the final draft of JPA 1.0 was approved. +// Java now had a standard way to do ORM, with multiple high-quality implementations of the standard API. +// This was the end of the line for the DAOs, right? +// +// Well, no. +// It wasn't. +// DAOs were rebranded "repositories", and continue to enjoy a sort-of zombie afterlife as a front-end to JPA. +// But are they really pulling their weight, or are they just unnecessary extra complexity and bloat? An extra layer of indirection that makes stack traces harder to read and code harder to debug? +// +// Our considered view is that they're mostly just bloat. +// The JPA `EntityManager` is a "repository", and it's a standard repository with a well-defined specification written by people who spend all day thinking about persistence. +// If these repository frameworks offered anything actually _useful_—and not obviously foot-shooty—over and above what `EntityManager` provides, we would have already added it to `EntityManager` decades ago. +// **** -Ultimately, we're not sure you need a separate persistence layer at all. -At least _consider_ the possibility that it might be OK to call the `EntityManager` directly from your business logic. +// Ultimately, we're not sure you need a separate persistence layer at all. +That is to say, we beg you to please at least _consider_ the possibility that it might be OK to call the `EntityManager` directly from your business logic. image::images/architecture.png[API overview,pdfwidth="100%",width=1100,align="center"] -We can already hear you hissing at our heresy. +We already hear you hissing at our heresy. But before slamming shut the lid of your laptop and heading off to fetch garlic and a pitchfork, take a couple of hours to weigh what we're proposing. -OK, so, look, if it makes you feel better, one way to view `EntityManager` is to think of it as a single _generic_ "repository" that works for every entity in your system. -From this point of view, JPA _is_ your persistence layer. -And there's few good reasons to wrap this abstraction in a second abstraction that's _less_ generic. - -// We might even analogize `EntityManager` to `List`. -// Then DAO-style repositories would be like having separate `StringList`, `IntList`, `PersonList`, and `BookList` classes. -// They're a parallel class hierarchy that makes the data model harder to evolve over time. - -// Of course, such decisions are highly context-dependent: surely _some_ programs out there really do benefit from isolating the persistence logic into some sort of distinct layer; on the other hand, we're equally sure that there are others which simply _don't_. - -Even where a distinct persistence layer _is_ appropriate, DAO-style repositories aren't the unambiguously most-correct way to factorize the equation: - -- most nontrivial queries touch multiple entities, and so it's often quite ambiguous which repository such a query belongs to, -- most queries are extremely specific to a particular fragment of program logic, and aren't reused in different places across the system, and -- the various operations of a repository rarely interact or share common internal implementation details. - -Indeed, repositories, by nature, exhibit very low _cohesion_. -A layer of repository objects might make sense if you have multiple implementations of each repository, but in practice almost nobody ever does. -That's because they're also extremely highly _coupled_ to their clients, with a very large API surface. -And, on the contrary, a layer is only easily replaceable if it has a very _narrow_ API. +Perhaps this is an argument we're bound to lose. +If that's the case, we recommend the Jakarta Data specification as a foundation for your persistence layer. +Our implementation of this specification, Hibernate Data Repositories, is built in to Hibernate Processor, and so you probably already have it available in your program. -[%unbreakable] -[TIP] -==== -Some people do indeed use mock repositories for testing, but we really struggle to see any value in this. -If we don't want to run our tests against our real database, it's usually very easy to "mock" the database itself by running tests against an in-memory Java database like H2. -This works even better in Hibernate 6 than in older versions of Hibernate, since HQL is now _much_ more portable between platforms. -==== - -// So even in cases where separation _is_ of benefit, we go on to question the notion that this must be achieved via a layer of container-managed objects. +Unlike other repository frameworks, Hibernate Data Repositories offers something that plain JPA simply doesn't have: full compile-time type safety for your queries. +To learn more, please refer to _Introducing Hibernate Data Repositories_. -// That said, one thing we _do_ understand is the desire to package: +// OK, so, look, if it makes you feel better, one way to view `EntityManager` is to think of it as a single _generic_ "repository" that works for every entity in your system. +// From this point of view, JPA _is_ your persistence layer. +// And there's few good reasons to wrap this abstraction in a second abstraction that's _less_ generic. // -// - a HQL or SQL query string with -// - the code which binds its parameters +// // We might even analogize `EntityManager` to `List`. +// // Then DAO-style repositories would be like having separate `StringList`, `IntList`, `PersonList`, and `BookList` classes. +// // They're a parallel class hierarchy that makes the data model harder to evolve over time. // -// as a typesafe function. -// DAO-style repositories seem to provide a very natural place to hang such functions, and we suspect that this accounts for at least some of their continued popularity. -// You're probably wondering how _we_ would go about defining such functions. - -// One thing that some repository frameworks offer is the ability to declare an abstract method that queries the database, and have the framework fill in an implementation of the method. -// But the way this works is that you must encode your query into the name of the method itself. +// // Of course, such decisions are highly context-dependent: surely _some_ programs out there really do benefit from isolating the persistence logic into some sort of distinct layer; on the other hand, we're equally sure that there are others which simply _don't_. +// +// Even where a distinct persistence layer _is_ appropriate, DAO-style repositories aren't the unambiguously most-correct way to factorize the equation: +// +// - most nontrivial queries touch multiple entities, and so it's often quite ambiguous which repository such a query belongs to, +// - most queries are extremely specific to a particular fragment of program logic, and aren't reused in different places across the system, and +// - the various operations of a repository rarely interact or share common internal implementation details. +// +// Indeed, repositories, by nature, exhibit very low _cohesion_. +// A layer of repository objects might make sense if you have multiple implementations of each repository, but in practice almost nobody ever does. +// That's because they're also extremely highly _coupled_ to their clients, with a very large API surface. +// And, on the contrary, a layer is only easily replaceable if it has a very _narrow_ API. // -// Which, at least in principle, for a not-very-complicated query, leads to a method name like this: +// [%unbreakable] +// [TIP] +// ==== +// Some people do indeed use mock repositories for testing, but we really struggle to see any value in this. +// If we don't want to run our tests against our real database, it's usually very easy to "mock" the database itself by running tests against an in-memory Java database like H2. +// This works even better in Hibernate 6 than in older versions of Hibernate, since HQL is now _much_ more portable between platforms. +// ==== // -// [.text-center] -// `findFirst10ByOrderDistinctPeopleByLastnameOrFirstnameAsc` +// // So even in cases where separation _is_ of benefit, we go on to question the notion that this must be achieved via a layer of container-managed objects. // -// This is a much worse query language than HQL. -// I think you can see why we didn't implement this idea in Hibernate. +// // That said, one thing we _do_ understand is the desire to package: +// // +// // - a HQL or SQL query string with +// // - the code which binds its parameters +// // +// // as a typesafe function. +// // DAO-style repositories seem to provide a very natural place to hang such functions, and we suspect that this accounts for at least some of their continued popularity. +// // You're probably wondering how _we_ would go about defining such functions. // -_Phew_, let's move on. +// // One thing that some repository frameworks offer is the ability to declare an abstract method that queries the database, and have the framework fill in an implementation of the method. +// // But the way this works is that you must encode your query into the name of the method itself. +// // +// // Which, at least in principle, for a not-very-complicated query, leads to a method name like this: +// // +// // [.text-center] +// // `findFirst10ByOrderDistinctPeopleByLastnameOrFirstnameAsc` +// // +// // This is a much worse query language than HQL. +// // I think you can see why we didn't implement this idea in Hibernate. +// // +// _Phew_, let's move on. [[overview]] === Overview @@ -843,7 +880,7 @@ This introduction will guide you through the basic tasks involved in developing 2. writing a _domain model_, that is, a set of _entity classes_ which represent the persistent types in your program, and which map to tables of your database, 3. customizing these mappings when the model maps to a pre-existing relational schema, 4. using the `Session` or `EntityManager` to perform operations which query the database and return entity instances, or which update the data held in the database, -5. using the Hibernate Metamodel Generator to improve compile-time type-safety, +5. using Hibernate Processor to improve compile-time type-safety, 6. writing complex queries using the Hibernate Query Language (HQL) or native SQL, and, finally 7. tuning performance of the data access logic. diff --git a/documentation/src/main/asciidoc/introduction/Mapping.adoc b/documentation/src/main/asciidoc/introduction/Mapping.adoc index d40fcb0cb75f..8d254a393b08 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] diff --git a/documentation/src/main/asciidoc/introduction/Preface.adoc b/documentation/src/main/asciidoc/introduction/Preface.adoc index 0c1020a7e358..2e39096abd50 100644 --- a/documentation/src/main/asciidoc/introduction/Preface.adoc +++ b/documentation/src/main/asciidoc/introduction/Preface.adoc @@ -1,31 +1,25 @@ [[preface]] == Preface -Hibernate 6 is a major redesign of the world's most popular and feature-rich ORM solution. -The redesign has touched almost every subsystem of Hibernate, including the APIs, mapping annotations, and the query language. -This new Hibernate is more powerful, more robust, and more typesafe. +Hibernate 6 was a major redesign of the world's most popular and feature-rich ORM solution. +The redesign touched almost every subsystem of Hibernate, including the APIs, mapping annotations, and the query language. +This new Hibernate was suddenly more powerful, more robust, more portable, and more type safe. -With so many improvements, it's very difficult to summarize the significance of this work. -But the following general themes stand out. -Hibernate 6: +Hibernate 7 builds on this foundation, adds support for JPA 3.2, and introduces Hibernate Data Repositories, an implementation of the Jakarta Data specification. +Taken together, these enhancements yield a level of compile-time type safety--and resulting developer productivity--which was previously impossible. +Hibernate Data Repositories offers truly seamless integration of the ORM solution with the persistence layer, obsoleting older add-on repository frameworks. -- finally takes advantage of the advances in relational databases over the past decade, updating the query language to support a raft of new constructs in modern dialects of SQL, -- exhibits much more consistent behavior across different databases, greatly improving portability, and generates much higher-quality DDL from dialect-independent code, -- improves error reporting by more scrupulous validation of queries _before_ access to the database, -- improves the type-safety of O/R mapping annotations, clarifies the separation of API, SPI, and internal implementation, and fixes some long-standing architectural flaws, -- removes or deprecates legacy APIs, laying the foundation for future evolution, and -- makes far better use of Javadoc, putting much more information at the fingertips of developers. +Hibernate and Hibernate Reactive are core components of Quarkus 3, the most exciting new environment for cloud-native development in Java, and Hibernate remains the persistence solution of choice for almost every major Java framework or server. -Hibernate 6 and Hibernate Reactive are now core components of Quarkus 3, the most exciting new environment for cloud-native development in Java, and Hibernate remains the persistence solution of choice for almost every major Java framework or server. - -Unfortunately, the changes in Hibernate 6 have obsoleted much of the information about Hibernate that's available in books, in blog posts, and on stackoverflow. +Unfortunately, the changes in Hibernate 6 obsoleted much of the information about Hibernate that's available in books, in blog posts, and on stackoverflow. This guide is an up-to-date, high-level discussion of the current feature set and recommended usage. It does not attempt to cover every feature and should be used in conjunction with other documentation: - Hibernate's extensive link:{doc-javadoc-url}[Javadoc], -- the link:{doc-query-language-url}[Guide to Hibernate Query Language], and +- the link:{doc-query-language-url}[Guide to Hibernate Query Language], +- link:{doc-data-repositories-url}[Introducing Hibernate Data Repositories], and - the Hibernate link:{doc-user-guide-url}[User Guide]. [NOTE] diff --git a/documentation/src/main/asciidoc/shared/url-attributes.adoc b/documentation/src/main/asciidoc/shared/url-attributes.adoc index 12c29a944d4f..3008ae3ac3e9 100644 --- a/documentation/src/main/asciidoc/shared/url-attributes.adoc +++ b/documentation/src/main/asciidoc/shared/url-attributes.adoc @@ -10,6 +10,7 @@ include::./common-attributes.adoc[] :doc-quick-start-url: {doc-version-base-url}/quickstart/html_single/ :doc-query-language-url: {doc-version-base-url}/querylanguage/html_single/Hibernate_Query_Language.html :doc-introduction-url: {doc-version-base-url}/introduction/html_single/Hibernate_Introduction.html +:doc-data-repositories-url: {doc-version-base-url}/repositories/html_single/Hibernate_Data_Repositories.html :doc-user-guide-url: {doc-version-base-url}/userguide/html_single/Hibernate_User_Guide.html :doc-javadoc-url: {doc-version-base-url}/javadocs/ :doc-topical-url: {doc-version-base-url}/topical/html_single/ diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java index ad30789be6af..3a384c934228 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java @@ -112,12 +112,17 @@ * Programs may directly use the APIs defined under {@link org.hibernate.boot}, * as an alternative to using an instance of this class. * + * @apiNote The {@link org.hibernate.jpa.HibernatePersistenceConfiguration} + * is a new alternative to this venerable API, and extends the JPA-standard + * {@link jakarta.persistence.PersistenceConfiguration}. + * * @author Gavin King * @author Steve Ebersole * * @see SessionFactory * @see AvailableSettings * @see org.hibernate.boot + * @see org.hibernate.jpa.HibernatePersistenceConfiguration */ public class Configuration { private static final CoreMessageLogger log = CoreLogging.messageLogger( Configuration.class ); diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/HibernatePersistenceConfiguration.java b/hibernate-core/src/main/java/org/hibernate/jpa/HibernatePersistenceConfiguration.java index 131716218280..13ce41f00d0d 100644 --- a/hibernate-core/src/main/java/org/hibernate/jpa/HibernatePersistenceConfiguration.java +++ b/hibernate-core/src/main/java/org/hibernate/jpa/HibernatePersistenceConfiguration.java @@ -28,9 +28,12 @@ import jakarta.persistence.ValidationMode; /** - * Hibernate extension to the Jakarta Persistence PersistenceConfiguration contract. + * Hibernate extension to the Jakarta Persistence {@link PersistenceConfiguration} + * contract. * * @author Steve Ebersole + * + * @since 7.0 */ public class HibernatePersistenceConfiguration extends PersistenceConfiguration { /**