Repository: quarkusio/quarkus-quickstarts
Pull request #1516: Add Jakarta Data quickstart
Author: login: marko-bekhta
Created: 2025-03-27T15:37:47Z | Merged: 2025-04-24T08:51:28Z

**Check list**:

Your pull request:

- [x] targets the `development` branch
- [x] uses the `999-SNAPSHOT` version of Quarkus
- [x] has tests (`mvn clean test`)
- [x] works in native (`mvn clean package -Pnative`)
- [x] has integration/native tests (`mvn clean verify -Pnative`)
- [ ] makes sure the associated guide must not be updated
- [ ] links the guide update pull request (if needed)
- [x] updates or creates the `README.md` file (with build and run instructions)
- [x] for new quickstart, is located in the directory _component-quickstart_
- [x] for new quickstart, is added to the root `pom.xml` and `README.md`





Diff:
diff --git a/README.md b/README.md
index 860f4c8f9e..052b2c23d0 100644
--- a/README.md
+++ b/README.md
@@ -56,6 +56,7 @@ See [CONTRIBUTING](CONTRIBUTING.md) for how to build these examples.
 * [Hibernate ORM REST Data with Panache](./hibernate-orm-rest-data-panache-quickstart): Automatically generate the CRUD endpoints for your entities and repositories using Hibernate ORM with Panache.
 * [Hibernate ORM Multitenancy Database](./hibernate-orm-multi-tenancy-database-quickstart): Multitenant CRUD service over REST using Hibernate ORM to connect to multiple PostgreSQL databases (database approach)
 * [Hibernate ORM Multitenancy Schema](./hibernate-orm-multi-tenancy-schema-quickstart): Multitenant CRUD service over REST using Hibernate ORM to connect to a PostgreSQL database (schema approach)
+* [Hibernate Data Repositories (Jakarta Data) and RESTEasy](./hibernate-orm-quickstart): Exposing a CRUD service over REST using Hibernate Data Repositories (Jakarta Data) to connect to a PostgreSQL database
 * [Hibernate Search + Elasticsearch](./hibernate-search-orm-elasticsearch-quickstart): Index your Hibernate entities in Elasticsearch to get full text search
 * [Infinispan Client](./infinispan-client-quickstart): How to use Infinispan Client. Covers creating caches and simple get/put
 * [Artemis JMS](./jms-quickstart): How to use the Artemis JMS extension
diff --git a/hibernate-orm-jakarta-data-quickstart/.dockerignore b/hibernate-orm-jakarta-data-quickstart/.dockerignore
new file mode 100644
index 0000000000..ab14769c34
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/.dockerignore
@@ -0,0 +1,5 @@
+*
+!target/*-runner
+!target/*-runner.jar
+!target/lib/*
+!target/quarkus-app/
diff --git a/hibernate-orm-jakarta-data-quickstart/.gitignore b/hibernate-orm-jakarta-data-quickstart/.gitignore
new file mode 100644
index 0000000000..087a18358f
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/.gitignore
@@ -0,0 +1,35 @@
+# Eclipse
+.project
+.classpath
+.settings/
+bin/
+
+# IntelliJ
+.idea
+*.ipr
+*.iml
+*.iws
+
+# NetBeans
+nb-configuration.xml
+
+# Visual Studio Code
+.vscode
+
+# OSX
+.DS_Store
+
+# Vim
+*.swp
+*.swo
+
+# patch
+*.orig
+*.rej
+
+# Maven
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+release.properties
\ No newline at end of file
diff --git a/hibernate-orm-jakarta-data-quickstart/.mvn/wrapper/maven-wrapper.jar b/hibernate-orm-jakarta-data-quickstart/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000000..7967f30dd1
Binary files /dev/null and b/hibernate-orm-jakarta-data-quickstart/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/hibernate-orm-jakarta-data-quickstart/.mvn/wrapper/maven-wrapper.properties b/hibernate-orm-jakarta-data-quickstart/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000000..9548abd8e8
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+wrapperVersion=3.3.2
+distributionType=bin
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar
diff --git a/hibernate-orm-jakarta-data-quickstart/README.md b/hibernate-orm-jakarta-data-quickstart/README.md
new file mode 100644
index 0000000000..32962762e7
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/README.md
@@ -0,0 +1,127 @@
+# Quarkus demo: Hibernate ORM and RESTEasy
+
+This is a minimal CRUD service exposing a couple of endpoints over REST,
+with a front-end based on Angular so you can play with it from your browser.
+
+While the code is surprisingly simple, under the hood this is using:
+- [Hibernate Data Repositories (Jakarta Data)](https://hibernate.org/repositories/) to perform the CRUD operations on the database
+- RESTEasy to expose the REST endpoints
+- A PostgreSQL database; see below to run one via Docker
+- ArC, the CDI inspired dependency injection tool with zero overhead
+- The high performance Agroal connection pool
+- All safely coordinated by the Narayana Transaction Manager
+
+## Requirements
+
+To compile and run this demo you will need:
+
+- JDK 17+
+- GraalVM
+
+In addition, you will need either a PostgreSQL database, or Docker to run one.
+
+### Configuring GraalVM and JDK 17+
+
+Make sure that both the `GRAALVM_HOME` and `JAVA_HOME` environment variables have
+been set, and that a JDK 17+ `java` command is on the path.
+
+See the [Building a Native Executable guide](https://quarkus.io/guides/building-native-image)
+for help setting up your environment.
+
+## Building the demo
+
+Launch the Maven build on the checked out sources of this demo:
+
+> ./mvnw package
+
+## Running the demo
+
+### Live coding with Quarkus
+
+The Maven Quarkus plugin provides a development mode that supports
+live coding. To try this out:
+
+> ./mvnw quarkus:dev
+
+In this mode you can make changes to the code and have the changes immediately applied, by just refreshing your browser.
+
+Dev Mode automatically starts a Docker container with a Postgres database. This feature is called ["Dev Services"](https://quarkus.io/guides/dev-services).
+
+To access the database from the terminal, run:
+
+```sh
+docker exec -it <container-name> psql -U quarkus
+```
+
+    Hot reload works even when modifying your JPA entities or Jakarta Data repositories.
+    Try it! Even the database schema will be updated on the fly.
+
+### Run Quarkus in JVM mode
+
+When you're done iterating in developer mode, you can run the application as a
+conventional jar file.
+
+First compile it:
+
+> ./mvnw package
+
+Next, make sure you have a PostgreSQL database running. In production, Quarkus does not start a container for you like it does in Dev Mode.
+To set up a PostgreSQL database with Docker:
+
+> docker run -it --rm=true --name quarkus_test -e POSTGRES_USER=quarkus_test -e POSTGRES_PASSWORD=quarkus_test -e POSTGRES_DB=quarkus_test -p 5432:5432 postgres:13.3
+
+Connection properties for the Agroal datasource are defined in the standard Quarkus configuration file,
+`src/main/resources/application.properties`.
+
+Then run it:
+
+> java -jar ./target/quarkus-app/quarkus-run.jar
+
+    Have a look at how fast it boots.
+    Or measure total native memory consumption...
+
+### Run Quarkus as a native application
+
+You can also create a native executable from this application without making any
+source code changes. A native executable removes the dependency on the JVM:
+everything needed to run the application on the target platform is included in
+the executable, allowing the application to run with minimal resource overhead.
+
+Compiling a native executable takes a bit longer, as GraalVM performs additional
+steps to remove unnecessary codepaths. Use the  `native` profile to compile a
+native executable:
+
+> ./mvnw package -Dnative
+
+After getting a cup of coffee, you'll be able to run this binary directly:
+
+> ./target/hibernate-orm-jakarta-data-quickstart-1.0.0-SNAPSHOT-runner
+
+    Please brace yourself: don't choke on that fresh cup of coffee you just got.
+    
+    Now observe the time it took to boot, and remember: that time was mostly spent to generate the tables in your database and import the initial data.
+    
+    Next, maybe you're ready to measure how much memory this service is consuming.
+
+N.B. This implies all dependencies have been compiled to native;
+that's a whole lot of stuff: from the bytecode enhancements that Hibernate ORM
+applies to your entities, to the lower level essential components such as the PostgreSQL JDBC driver, the Undertow webserver.
+
+## See the demo in your browser
+
+Navigate to:
+
+<http://localhost:8080/index.html>
+
+Have fun, and join the team of contributors!
+
+## Running the demo in Kubernetes
+
+This section provides extra information for running both the database and the demo on Kubernetes.
+As well as running the DB on Kubernetes, a service needs to be exposed for the demo to connect to the DB.
+
+Then, rebuild demo docker image with a system property that points to the DB.
+
+```bash
+-Dquarkus.datasource.jdbc.url=jdbc:postgresql://<DB_SERVICE_NAME>/quarkus_test
+```
diff --git a/hibernate-orm-jakarta-data-quickstart/mvnw b/hibernate-orm-jakarta-data-quickstart/mvnw
new file mode 100755
index 0000000000..5e9618cac2
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/mvnw
@@ -0,0 +1,332 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.3.2
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ]; then
+
+  if [ -f /usr/local/etc/mavenrc ]; then
+    . /usr/local/etc/mavenrc
+  fi
+
+  if [ -f /etc/mavenrc ]; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ]; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false
+darwin=false
+mingw=false
+case "$(uname)" in
+CYGWIN*) cygwin=true ;;
+MINGW*) mingw=true ;;
+Darwin*)
+  darwin=true
+  # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+  # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+  if [ -z "$JAVA_HOME" ]; then
+    if [ -x "/usr/libexec/java_home" ]; then
+      JAVA_HOME="$(/usr/libexec/java_home)"
+      export JAVA_HOME
+    else
+      JAVA_HOME="/Library/Java/Home"
+      export JAVA_HOME
+    fi
+  fi
+  ;;
+esac
+
+if [ -z "$JAVA_HOME" ]; then
+  if [ -r /etc/gentoo-release ]; then
+    JAVA_HOME=$(java-config --jre-home)
+  fi
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin; then
+  [ -n "$JAVA_HOME" ] \
+    && JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
+  [ -n "$CLASSPATH" ] \
+    && CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw; then
+  [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] \
+    && JAVA_HOME="$(
+      cd "$JAVA_HOME" || (
+        echo "cannot cd into $JAVA_HOME." >&2
+        exit 1
+      )
+      pwd
+    )"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="$(which javac)"
+  if [ -n "$javaExecutable" ] && ! [ "$(expr "$javaExecutable" : '\([^ ]*\)')" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=$(which readlink)
+    if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
+      if $darwin; then
+        javaHome="$(dirname "$javaExecutable")"
+        javaExecutable="$(cd "$javaHome" && pwd -P)/javac"
+      else
+        javaExecutable="$(readlink -f "$javaExecutable")"
+      fi
+      javaHome="$(dirname "$javaExecutable")"
+      javaHome=$(expr "$javaHome" : '\(.*\)/bin')
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ]; then
+  if [ -n "$JAVA_HOME" ]; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ]; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="$(
+      \unset -f command 2>/dev/null
+      \command -v java
+    )"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ]; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  echo "Warning: JAVA_HOME environment variable is not set." >&2
+fi
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+  if [ -z "$1" ]; then
+    echo "Path not specified to find_maven_basedir" >&2
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
+  while [ "$wdir" != '/' ]; do
+    if [ -d "$wdir"/.mvn ]; then
+      basedir=$wdir
+      break
+    fi
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=$(
+        cd "$wdir/.." || exit 1
+        pwd
+      )
+    fi
+    # end of workaround
+  done
+  printf '%s' "$(
+    cd "$basedir" || exit 1
+    pwd
+  )"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    # Remove \r in case we run on Windows within Git Bash
+    # and check out the repository with auto CRLF management
+    # enabled. Otherwise, we may read lines that are delimited with
+    # \r\n and produce $'-Xarg\r' rather than -Xarg due to word
+    # splitting rules.
+    tr -s '\r\n' ' ' <"$1"
+  fi
+}
+
+log() {
+  if [ "$MVNW_VERBOSE" = true ]; then
+    printf '%s\n' "$1"
+  fi
+}
+
+BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
+if [ -z "$BASE_DIR" ]; then
+  exit 1
+fi
+
+MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+export MAVEN_PROJECTBASEDIR
+log "$MAVEN_PROJECTBASEDIR"
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
+if [ -r "$wrapperJarPath" ]; then
+  log "Found $wrapperJarPath"
+else
+  log "Couldn't find $wrapperJarPath, downloading it ..."
+
+  if [ -n "$MVNW_REPOURL" ]; then
+    wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar"
+  else
+    wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar"
+  fi
+  while IFS="=" read -r key value; do
+    # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
+    safeValue=$(echo "$value" | tr -d '\r')
+    case "$key" in wrapperUrl)
+      wrapperUrl="$safeValue"
+      break
+      ;;
+    esac
+  done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+  log "Downloading from: $wrapperUrl"
+
+  if $cygwin; then
+    wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
+  fi
+
+  if command -v wget >/dev/null; then
+    log "Found wget ... using wget"
+    [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
+    if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+      wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+    else
+      wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+    fi
+  elif command -v curl >/dev/null; then
+    log "Found curl ... using curl"
+    [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
+    if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+      curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+    else
+      curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+    fi
+  else
+    log "Falling back to using Java to download"
+    javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
+    javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
+    # For Cygwin, switch paths to Windows format before running javac
+    if $cygwin; then
+      javaSource=$(cygpath --path --windows "$javaSource")
+      javaClass=$(cygpath --path --windows "$javaClass")
+    fi
+    if [ -e "$javaSource" ]; then
+      if [ ! -e "$javaClass" ]; then
+        log " - Compiling MavenWrapperDownloader.java ..."
+        ("$JAVA_HOME/bin/javac" "$javaSource")
+      fi
+      if [ -e "$javaClass" ]; then
+        log " - Running MavenWrapperDownloader.java ..."
+        ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
+      fi
+    fi
+  fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+# If specified, validate the SHA-256 sum of the Maven wrapper jar file
+wrapperSha256Sum=""
+while IFS="=" read -r key value; do
+  case "$key" in wrapperSha256Sum)
+    wrapperSha256Sum=$value
+    break
+    ;;
+  esac
+done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+if [ -n "$wrapperSha256Sum" ]; then
+  wrapperSha256Result=false
+  if command -v sha256sum >/dev/null; then
+    if echo "$wrapperSha256Sum  $wrapperJarPath" | sha256sum -c >/dev/null 2>&1; then
+      wrapperSha256Result=true
+    fi
+  elif command -v shasum >/dev/null; then
+    if echo "$wrapperSha256Sum  $wrapperJarPath" | shasum -a 256 -c >/dev/null 2>&1; then
+      wrapperSha256Result=true
+    fi
+  else
+    echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
+    echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." >&2
+    exit 1
+  fi
+  if [ $wrapperSha256Result = false ]; then
+    echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
+    echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
+    echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
+    exit 1
+  fi
+fi
+
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$JAVA_HOME" ] \
+    && JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
+  [ -n "$CLASSPATH" ] \
+    && CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
+  [ -n "$MAVEN_PROJECTBASEDIR" ] \
+    && MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+# shellcheck disable=SC2086 # safe args
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  $MAVEN_DEBUG_OPTS \
+  -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/hibernate-orm-jakarta-data-quickstart/mvnw.cmd b/hibernate-orm-jakarta-data-quickstart/mvnw.cmd
new file mode 100644
index 0000000000..4136715f08
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/mvnw.cmd
@@ -0,0 +1,206 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.3.2
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo. >&2
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo. >&2
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo. >&2
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo. >&2
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+    IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Found %WRAPPER_JAR%
+    )
+) else (
+    if not "%MVNW_REPOURL%" == "" (
+        SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar"
+    )
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Couldn't find %WRAPPER_JAR%, downloading it ...
+        echo Downloading from: %WRAPPER_URL%
+    )
+
+    powershell -Command "&{"^
+		"$webclient = new-object System.Net.WebClient;"^
+		"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+		"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+		"}"^
+		"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
+		"}"
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Finished downloading %WRAPPER_JAR%
+    )
+)
+@REM End of extension
+
+@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
+SET WRAPPER_SHA_256_SUM=""
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+    IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
+)
+IF NOT %WRAPPER_SHA_256_SUM%=="" (
+    powershell -Command "&{"^
+       "Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash;"^
+       "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
+       "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
+       "  Write-Error 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
+       "  Write-Error 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
+       "  Write-Error 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
+       "  exit 1;"^
+       "}"^
+       "}"
+    if ERRORLEVEL 1 goto error
+)
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+  %JVM_CONFIG_MAVEN_PROPS% ^
+  %MAVEN_OPTS% ^
+  %MAVEN_DEBUG_OPTS% ^
+  -classpath %WRAPPER_JAR% ^
+  "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+  %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff --git a/hibernate-orm-jakarta-data-quickstart/pom.xml b/hibernate-orm-jakarta-data-quickstart/pom.xml
new file mode 100644
index 0000000000..094ccafc49
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/pom.xml
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.acme</groupId>
+    <artifactId>hibernate-orm-jakarta-data-quickstart</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+
+    <properties>
+        <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
+        <quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
+        <quarkus.platform.version>999-SNAPSHOT</quarkus.platform.version>
+        <surefire-plugin.version>3.1.2</surefire-plugin.version>
+        <compiler-plugin.version>3.14.0</compiler-plugin.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <maven.compiler.release>17</maven.compiler.release>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>${quarkus.platform.group-id}</groupId>
+                <artifactId>${quarkus.platform.artifact-id}</artifactId>
+                <version>${quarkus.platform.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-hibernate-orm</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.data</groupId>
+            <artifactId>jakarta.data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-agroal</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-rest</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-rest-jackson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-jdbc-postgresql</artifactId>
+        </dependency>
+
+        <!-- Testing: -->
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-junit5</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.rest-assured</groupId>
+            <artifactId>rest-assured</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>${compiler-plugin.version}</version>
+                <configuration>
+                    <!-- the parameters=true option is critical so that RESTEasy works fine -->
+                    <parameters>true</parameters>
+                    <!-- the jpamodelgen annotation processor provides both static metamodel and Jakarta Data capabilities: -->
+                    <annotationProcessorPaths>
+                        <path>
+                            <groupId>org.hibernate.orm</groupId>
+                            <artifactId>hibernate-jpamodelgen</artifactId>
+                        </path>
+                    </annotationProcessorPaths>
+                </configuration>
+            </plugin>
+            <plugin>
+                <!-- you need this specific version to integrate with the other build helpers -->
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>${surefire-plugin.version}</version>
+                <configuration>
+                    <systemPropertyVariables>
+                        <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
+                        <maven.home>${maven.home}</maven.home>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+            <plugin>
+                <!-- This is what injects the magic Quarkus bytecode -->
+                <groupId>${quarkus.platform.group-id}</groupId>
+                <artifactId>quarkus-maven-plugin</artifactId>
+                <version>${quarkus.platform.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>build</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <!-- Optionally activate this profile to compile the demo into native! -->
+            <id>native</id>
+            <activation>
+                <property>
+                    <name>native</name>
+                </property>
+            </activation>
+            <properties>
+                <quarkus.native.enabled>true</quarkus.native.enabled>
+                <quarkus.package.jar.enabled>false</quarkus.package.jar.enabled>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                        <version>${surefire-plugin.version}</version>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>integration-test</goal>
+                                    <goal>verify</goal>
+                                </goals>
+                                <configuration>
+                                    <systemPropertyVariables>
+                                        <native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
+                                        <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
+                                        <maven.home>${maven.home}</maven.home>
+                                    </systemPropertyVariables>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>eclipse</id>
+            <activation>
+                <property>
+                    <!-- This is a trick to have the profile automatically activated by Eclipse -->
+                    <name>m2e.version</name>
+                </property>
+            </activation>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
+                        <plugin>
+                            <groupId>org.eclipse.m2e</groupId>
+                            <artifactId>lifecycle-mapping</artifactId>
+                            <version>1.0.0</version>
+                            <configuration>
+                                <lifecycleMappingMetadata>
+                                    <pluginExecutions>
+                                        <pluginExecution>
+                                            <pluginExecutionFilter>
+                                                <groupId>io.fabric8</groupId>
+                                                <artifactId>
+                                                    docker-maven-plugin
+                                                </artifactId>
+                                                <versionRange>
+                                                    [0.28.0,)
+                                                </versionRange>
+                                                <goals>
+                                                    <goal>start</goal>
+                                                    <goal>stop</goal>
+                                                </goals>
+                                            </pluginExecutionFilter>
+                                            <action>
+                                                <ignore></ignore>
+                                            </action>
+                                        </pluginExecution>
+                                    </pluginExecutions>
+                                </lifecycleMappingMetadata>
+                            </configuration>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/hibernate-orm-jakarta-data-quickstart/src/main/docker/Dockerfile.jvm b/hibernate-orm-jakarta-data-quickstart/src/main/docker/Dockerfile.jvm
new file mode 100644
index 0000000000..5abc771b70
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/src/main/docker/Dockerfile.jvm
@@ -0,0 +1,98 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
+#
+# Before building the container image run:
+#
+# ./mvnw package
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/hibernate-orm-jakarta-data-quickstart-jvm .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/hibernate-orm-jakarta-data-quickstart-jvm
+#
+# If you want to include the debug port into your docker image
+# you will have to expose the debug port (default 5005 being the default) like this :  EXPOSE 8080 5005.
+# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
+# when running the container
+#
+# Then run the container using :
+#
+# docker run -i --rm -p 8080:8080 quarkus/hibernate-orm-jakarta-data-quickstart-jvm
+#
+# This image uses the `run-java.sh` script to run the application.
+# This scripts computes the command line to execute your Java application, and
+# includes memory/GC tuning.
+# You can configure the behavior using the following environment properties:
+# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") - Be aware that this will override
+# the default JVM options, use `JAVA_OPTS_APPEND` to append options
+# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
+#   in JAVA_OPTS (example: "-Dsome.property=foo")
+# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
+#   used to calculate a default maximal heap memory based on a containers restriction.
+#   If used in a container without any memory constraints for the container then this
+#   option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
+#   of the container available memory as set here. The default is `50` which means 50%
+#   of the available memory is used as an upper boundary. You can skip this mechanism by
+#   setting this value to `0` in which case no `-Xmx` option is added.
+# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
+#   is used to calculate a default initial heap memory based on the maximum heap memory.
+#   If used in a container without any memory constraints for the container then this
+#   option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
+#   of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
+#   is used as the initial heap size. You can skip this mechanism by setting this value
+#   to `0` in which case no `-Xms` option is added (example: "25")
+# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
+#   This is used to calculate the maximum value of the initial heap memory. If used in
+#   a container without any memory constraints for the container then this option has
+#   no effect. If there is a memory constraint then `-Xms` is limited to the value set
+#   here. The default is 4096MB which means the calculated value of `-Xms` never will
+#   be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
+# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
+#   when things are happening. This option, if set to true, will set
+#  `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
+# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
+#    true").
+# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
+# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
+#   https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
+# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
+# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
+#   (example: "20")
+# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
+#   (example: "40")
+# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
+#   (example: "4")
+# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
+#   previous GC times. (example: "90")
+# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
+# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
+# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
+#   contain the necessary JRE command-line options to specify the required GC, which
+#   will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
+# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
+# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
+# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
+#   accessed directly. (example: "foo.example.com,bar.example.com")
+#
+###
+FROM registry.access.redhat.com/ubi9/openjdk-21:1.21
+
+ENV LANGUAGE='en_US:en'
+
+
+# We make four distinct layers so if there are application changes the library layers can be re-used
+COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/
+COPY --chown=185 target/quarkus-app/*.jar /deployments/
+COPY --chown=185 target/quarkus-app/app/ /deployments/app/
+COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/
+
+EXPOSE 8080
+USER 185
+ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
+ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
+
+ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
+
diff --git a/hibernate-orm-jakarta-data-quickstart/src/main/docker/Dockerfile.legacy-jar b/hibernate-orm-jakarta-data-quickstart/src/main/docker/Dockerfile.legacy-jar
new file mode 100644
index 0000000000..6c2118c44c
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/src/main/docker/Dockerfile.legacy-jar
@@ -0,0 +1,94 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
+#
+# Before building the container image run:
+#
+# ./mvnw package -Dquarkus.package.jar.type=legacy-jar
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/hibernate-orm-jakarta-data-quickstart-legacy-jar .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/hibernate-orm-jakarta-data-quickstart-legacy-jar
+#
+# If you want to include the debug port into your docker image
+# you will have to expose the debug port (default 5005 being the default) like this :  EXPOSE 8080 5005.
+# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
+# when running the container
+#
+# Then run the container using :
+#
+# docker run -i --rm -p 8080:8080 quarkus/hibernate-orm-jakarta-data-quickstart-legacy-jar
+#
+# This image uses the `run-java.sh` script to run the application.
+# This scripts computes the command line to execute your Java application, and
+# includes memory/GC tuning.
+# You can configure the behavior using the following environment properties:
+# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") - Be aware that this will override
+# the default JVM options, use `JAVA_OPTS_APPEND` to append options
+# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
+#   in JAVA_OPTS (example: "-Dsome.property=foo")
+# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
+#   used to calculate a default maximal heap memory based on a containers restriction.
+#   If used in a container without any memory constraints for the container then this
+#   option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
+#   of the container available memory as set here. The default is `50` which means 50%
+#   of the available memory is used as an upper boundary. You can skip this mechanism by
+#   setting this value to `0` in which case no `-Xmx` option is added.
+# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
+#   is used to calculate a default initial heap memory based on the maximum heap memory.
+#   If used in a container without any memory constraints for the container then this
+#   option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
+#   of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
+#   is used as the initial heap size. You can skip this mechanism by setting this value
+#   to `0` in which case no `-Xms` option is added (example: "25")
+# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
+#   This is used to calculate the maximum value of the initial heap memory. If used in
+#   a container without any memory constraints for the container then this option has
+#   no effect. If there is a memory constraint then `-Xms` is limited to the value set
+#   here. The default is 4096MB which means the calculated value of `-Xms` never will
+#   be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
+# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
+#   when things are happening. This option, if set to true, will set
+#  `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
+# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
+#    true").
+# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
+# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
+#   https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
+# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
+# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
+#   (example: "20")
+# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
+#   (example: "40")
+# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
+#   (example: "4")
+# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
+#   previous GC times. (example: "90")
+# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
+# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
+# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
+#   contain the necessary JRE command-line options to specify the required GC, which
+#   will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
+# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
+# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
+# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
+#   accessed directly. (example: "foo.example.com,bar.example.com")
+#
+###
+FROM registry.access.redhat.com/ubi9/openjdk-21:1.21
+
+ENV LANGUAGE='en_US:en'
+
+
+COPY target/lib/* /deployments/lib/
+COPY target/*-runner.jar /deployments/quarkus-run.jar
+
+EXPOSE 8080
+USER 185
+ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
+ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
+
+ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
diff --git a/hibernate-orm-jakarta-data-quickstart/src/main/docker/Dockerfile.native b/hibernate-orm-jakarta-data-quickstart/src/main/docker/Dockerfile.native
new file mode 100644
index 0000000000..3016460f2e
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/src/main/docker/Dockerfile.native
@@ -0,0 +1,29 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
+#
+# Before building the container image run:
+#
+# ./mvnw package -Dnative
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.native -t quarkus/hibernate-orm-jakarta-data-quickstart .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/hibernate-orm-jakarta-data-quickstart
+#
+# The ` registry.access.redhat.com/ubi8/ubi-minimal:8.10` base image is based on UBI 9.
+# To use UBI 8, switch to `quay.io/ubi8/ubi-minimal:8.10`.
+###
+FROM registry.access.redhat.com/ubi8/ubi-minimal:8.10
+WORKDIR /work/
+RUN chown 1001 /work \
+    && chmod "g+rwX" /work \
+    && chown 1001:root /work
+COPY --chown=1001:root --chmod=0755 target/*-runner /work/application
+
+EXPOSE 8080
+USER 1001
+
+ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
diff --git a/hibernate-orm-jakarta-data-quickstart/src/main/docker/Dockerfile.native-micro b/hibernate-orm-jakarta-data-quickstart/src/main/docker/Dockerfile.native-micro
new file mode 100644
index 0000000000..7d1224204f
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/src/main/docker/Dockerfile.native-micro
@@ -0,0 +1,32 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
+# It uses a micro base image, tuned for Quarkus native executables.
+# It reduces the size of the resulting container image.
+# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image.
+#
+# Before building the container image run:
+#
+# ./mvnw package -Dnative
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/hibernate-orm-jakarta-data-quickstart .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/hibernate-orm-jakarta-data-quickstart
+#
+# The `quay.io/quarkus/quarkus-micro-image:2.0` base image is based on UBI 9.
+# To use UBI 8, switch to `quay.io/quarkus/quarkus-micro-image:2.0`.
+###
+FROM quay.io/quarkus/quarkus-micro-image:2.0
+WORKDIR /work/
+RUN chown 1001 /work \
+    && chmod "g+rwX" /work \
+    && chown 1001:root /work
+COPY --chown=1001:root --chmod=0755 target/*-runner /work/application
+
+EXPOSE 8080
+USER 1001
+
+ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
diff --git a/hibernate-orm-jakarta-data-quickstart/src/main/java/org/acme/hibernate/orm/Fruit.java b/hibernate-orm-jakarta-data-quickstart/src/main/java/org/acme/hibernate/orm/Fruit.java
new file mode 100644
index 0000000000..60fe168ff1
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/src/main/java/org/acme/hibernate/orm/Fruit.java
@@ -0,0 +1,41 @@
+package org.acme.hibernate.orm;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+
+@Entity
+public class Fruit {
+
+	@Id
+	@GeneratedValue
+	private Integer id;
+
+	@Column(length = 40, unique = true)
+	private String name;
+
+	public Fruit() {
+	}
+
+	public Fruit(String name) {
+		this.name = name;
+	}
+
+	public Integer getId() {
+		return id;
+	}
+
+	public void setId(Integer id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+}
diff --git a/hibernate-orm-jakarta-data-quickstart/src/main/java/org/acme/hibernate/orm/FruitRepository.java b/hibernate-orm-jakarta-data-quickstart/src/main/java/org/acme/hibernate/orm/FruitRepository.java
new file mode 100644
index 0000000000..619e00db9c
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/src/main/java/org/acme/hibernate/orm/FruitRepository.java
@@ -0,0 +1,24 @@
+package org.acme.hibernate.orm;
+
+import java.util.stream.Stream;
+
+import jakarta.data.Order;
+import jakarta.data.repository.CrudRepository;
+import jakarta.data.repository.Delete;
+import jakarta.data.repository.Find;
+import jakarta.data.repository.Query;
+import jakarta.data.repository.Repository;
+
+@Repository
+public interface FruitRepository extends CrudRepository<Fruit, Integer> {
+
+	@Find
+	Stream<Fruit> findAll(Order<Fruit> order);
+
+	@Query("update Fruit f set f.name = :name where f.id = :id")
+	void update(Integer id, String name);
+
+	@Delete
+	void delete(Integer id);
+
+}
diff --git a/hibernate-orm-jakarta-data-quickstart/src/main/java/org/acme/hibernate/orm/FruitResource.java b/hibernate-orm-jakarta-data-quickstart/src/main/java/org/acme/hibernate/orm/FruitResource.java
new file mode 100644
index 0000000000..27b4710be3
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/src/main/java/org/acme/hibernate/orm/FruitResource.java
@@ -0,0 +1,76 @@
+package org.acme.hibernate.orm;
+
+import java.util.List;
+
+import org.jboss.logging.Logger;
+
+import jakarta.data.Order;
+import jakarta.data.Sort;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.transaction.Transactional;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+
+@Path("fruits")
+@ApplicationScoped
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+@Transactional
+public class FruitResource {
+
+	@Inject
+	FruitRepository repository;
+
+	@GET
+	public List<Fruit> get() {
+		return repository.findAll( Order.by( Sort.asc( Fruit_.NAME ) ) ).toList();
+	}
+
+	@GET
+	@Path("{id}")
+	public Fruit getSingle(Integer id) {
+		return repository.findById( id )
+				.orElseThrow( () -> new WebApplicationException( "Fruit with id of %d does not exist.".formatted( id ), 404 ) );
+	}
+
+	@POST
+	@Transactional
+	public Response create(Fruit fruit) {
+		if ( fruit.getId() != null ) {
+			throw new WebApplicationException( "Id was invalidly set on request.", 422 );
+		}
+
+		repository.insert( fruit );
+		return Response.ok( fruit ).status( 201 ).build();
+	}
+
+	@PUT
+	@Path("{id}")
+	@Transactional
+	public Fruit update(Integer id, Fruit fruit) {
+		if ( fruit.getName() == null ) {
+			throw new WebApplicationException( "Fruit Name was not set on request.", 422 );
+		}
+
+		repository.update( id, fruit.getName() );
+
+		return fruit;
+	}
+
+	@DELETE
+	@Path("{id}")
+	@Transactional
+	public Response delete(Integer id) {
+		repository.delete( id );
+		return Response.status( 204 ).build();
+	}
+}
diff --git a/hibernate-orm-jakarta-data-quickstart/src/main/resources/META-INF/resources/favicon.ico b/hibernate-orm-jakarta-data-quickstart/src/main/resources/META-INF/resources/favicon.ico
new file mode 100644
index 0000000000..b4ef4208a6
Binary files /dev/null and b/hibernate-orm-jakarta-data-quickstart/src/main/resources/META-INF/resources/favicon.ico differ
diff --git a/hibernate-orm-jakarta-data-quickstart/src/main/resources/META-INF/resources/index.html b/hibernate-orm-jakarta-data-quickstart/src/main/resources/META-INF/resources/index.html
new file mode 100644
index 0000000000..d66b6b23eb
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/src/main/resources/META-INF/resources/index.html
@@ -0,0 +1,152 @@
+<!doctype html>
+<html>
+<head>
+    <meta charset="utf-8"/>
+    <title>CRUD Mission - Quarkus</title>
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/wingcss/0.1.8/wing.min.css"/>
+    <style>
+        input[type=number] {
+            width: 100%;
+            padding: 12px 20px;
+            margin: 8px 0;
+            display: inline-block;
+            border: 1px solid #ccc;
+            border-radius: 4px;
+            box-sizing: border-box;
+            -webkit-transition: .5s;
+            transition: .5s;
+            outline: 0;
+            font-family: 'Open Sans', serif;
+        }
+    </style>
+    <!-- Load AngularJS -->
+    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
+    <script type="text/javascript">
+      var app = angular.module("FruitManagement", []);
+
+      //Controller Part
+      app.controller("FruitManagementController", function ($scope, $http) {
+
+        //Initialize page with default data which is blank in this example
+        $scope.fruits = [];
+
+        $scope.form = {
+          id: -1,
+          name: ""
+        };
+
+        //Now load the data from server
+        _refreshPageData();
+
+        //HTTP POST/PUT methods for add/edit fruits
+        $scope.update = function () {
+          var method = "";
+          var url = "";
+          var data = {};
+          if ($scope.form.id == -1) {
+            //Id is absent so add fruits - POST operation
+            method = "POST";
+            url = '/fruits';
+            data.name = $scope.form.name;
+          } else {
+            //If Id is present, it's edit operation - PUT operation
+            method = "PUT";
+            url = '/fruits/' + $scope.form.id;
+            data.name = $scope.form.name;
+          }
+
+          $http({
+            method: method,
+            url: url,
+            data: angular.toJson(data),
+            headers: {
+              'Content-Type': 'application/json'
+            }
+          }).then(_success, _error);
+        };
+
+        //HTTP DELETE- delete fruit by id
+        $scope.remove = function (fruit) {
+          $http({
+            method: 'DELETE',
+            url: '/fruits/' + fruit.id
+          }).then(_success, _error);
+        };
+
+        //In case of edit fruits, populate form with fruit data
+        $scope.edit = function (fruit) {
+          $scope.form.name = fruit.name;
+          $scope.form.id = fruit.id;
+        };
+
+          /* Private Methods */
+        //HTTP GET- get all fruits collection
+        function _refreshPageData() {
+          $http({
+            method: 'GET',
+            url: '/fruits'
+          }).then(function successCallback(response) {
+            $scope.fruits = response.data;
+          }, function errorCallback(response) {
+            console.log(response.statusText);
+          });
+        }
+
+        function _success(response) {
+          _refreshPageData();
+          _clearForm()
+        }
+
+        function _error(response) {
+          alert(response.data.message || response.statusText);
+        }
+
+        //Clear the form
+        function _clearForm() {
+          $scope.form.name = "";
+          $scope.form.id = -1;
+        }
+      });
+    </script>
+</head>
+<body ng-app="FruitManagement" ng-controller="FruitManagementController">
+
+<div class="container">
+    <h1>CRUD Mission - Quarkus</h1>
+    <p>This application demonstrates how a Quarkus application implements a CRUD endpoint to manage fruits.
+        This management interface invokes the CRUD service endpoint, which interacts with a database using Jakarta Data and several other well known libraries.
+    </p>
+    <p>Behind the scenes, we have:
+        <ul>
+            <li><a href="https://hibernate.org/repositories/" target="_blank">Hibernate Data Repositories (Jakarta Data)</a> taking care of all CRUD operations</li>
+            <li>RESTEasy powering the REST API</li>
+            <li>ArC, a CDI based dependency injection framework</li>
+            <li>the Narayana Transaction Manager coordinating all transactions</li>
+            <li>Agroal, the high performance Datasource implementation</li>
+            <li>Infinispan used as Hibernate 2nd level caching: enabled on both entities and queries</li>
+            <li>The Undertow webserver</li>
+            <li>Some magic bytecode generation plugged in the compiler...</li>
+        </ul>
+    </p>
+
+    <h3>Add/Edit a fruit</h3>
+    <form ng-submit="update()">
+        <div class="row">
+            <div class="col-6"><input type="text" placeholder="Name" ng-model="form.name" size="60"/></div>
+        </div>
+        <input type="submit" value="Save"/>
+    </form>
+
+    <h3>Fruit List</h3>
+    <div class="row">
+        <div class="col-2">Name</div>
+    </div>
+    <div class="row" ng-repeat="fruit in fruits">
+        <div class="col-2">{{ fruit.name }}</div>
+        <div class="col-8"><a ng-click="edit( fruit )" class="btn">Edit</a> <a ng-click="remove( fruit )" class="btn">Remove</a>
+        </div>
+    </div>
+</div>
+
+</body>
+</html>
diff --git a/hibernate-orm-jakarta-data-quickstart/src/main/resources/application.properties b/hibernate-orm-jakarta-data-quickstart/src/main/resources/application.properties
new file mode 100644
index 0000000000..1a7447d24b
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/src/main/resources/application.properties
@@ -0,0 +1,9 @@
+quarkus.hibernate-orm.sql-load-script=import.sql
+
+%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://localhost/quarkus_test
+%prod.quarkus.datasource.username=quarkus_test
+%prod.quarkus.datasource.password=quarkus_test
+%prod.quarkus.hibernate-orm.database.generation=create
+
+%dev.quarkus.hibernate-orm.log.sql=true
+
diff --git a/hibernate-orm-jakarta-data-quickstart/src/main/resources/import.sql b/hibernate-orm-jakarta-data-quickstart/src/main/resources/import.sql
new file mode 100644
index 0000000000..b799774449
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/src/main/resources/import.sql
@@ -0,0 +1,4 @@
+INSERT INTO fruit(id, name) VALUES (1, 'Cherry');
+INSERT INTO fruit(id, name) VALUES (2, 'Apple');
+INSERT INTO fruit(id, name) VALUES (3, 'Banana');
+ALTER SEQUENCE fruit_seq RESTART WITH 4;
diff --git a/hibernate-orm-jakarta-data-quickstart/src/test/java/org/acme/hibernate/orm/FruitsEndpointIT.java b/hibernate-orm-jakarta-data-quickstart/src/test/java/org/acme/hibernate/orm/FruitsEndpointIT.java
new file mode 100644
index 0000000000..539aa0f061
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/src/test/java/org/acme/hibernate/orm/FruitsEndpointIT.java
@@ -0,0 +1,10 @@
+package org.acme.hibernate.orm;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+public class FruitsEndpointIT extends FruitsEndpointTest {
+
+    // Runs the same tests as the parent class
+
+}
diff --git a/hibernate-orm-jakarta-data-quickstart/src/test/java/org/acme/hibernate/orm/FruitsEndpointTest.java b/hibernate-orm-jakarta-data-quickstart/src/test/java/org/acme/hibernate/orm/FruitsEndpointTest.java
new file mode 100644
index 0000000000..38b70c0ab5
--- /dev/null
+++ b/hibernate-orm-jakarta-data-quickstart/src/test/java/org/acme/hibernate/orm/FruitsEndpointTest.java
@@ -0,0 +1,63 @@
+package org.acme.hibernate.orm;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.core.IsNot.not;
+
+import org.junit.jupiter.api.Test;
+
+import io.quarkus.test.junit.QuarkusTest;
+
+@QuarkusTest
+public class FruitsEndpointTest {
+
+    @Test
+    public void testListAllFruits() {
+        //List all, should have all 3 fruits the database has initially:
+        given()
+                .when().get("/fruits")
+                .then()
+                .statusCode(200)
+                .body(
+                        containsString("Cherry"),
+                        containsString("Apple"),
+                        containsString("Banana"));
+
+        //Delete the Cherry:
+        given()
+                .when().delete("/fruits/1")
+                .then()
+                .statusCode(204);
+
+        //List all, cherry should be missing now:
+        given()
+                .when().get("/fruits")
+                .then()
+                .statusCode(200)
+                .body(
+                        not(containsString("Cherry")),
+                        containsString("Apple"),
+                        containsString("Banana"));
+
+        //Create the Pear:
+        given()
+                .when()
+                .body("{\"name\" : \"Pear\"}")
+                .contentType("application/json")
+                .post("/fruits")
+                .then()
+                .statusCode(201);
+
+        //List all, cherry should be missing now:
+        given()
+                .when().get("/fruits")
+                .then()
+                .statusCode(200)
+                .body(
+                        not(containsString("Cherry")),
+                        containsString("Apple"),
+                        containsString("Banana"),
+                        containsString("Pear"));
+    }
+
+}
diff --git a/pom.xml b/pom.xml
index 4d43cf5e1b..3859a58535 100644
--- a/pom.xml
+++ b/pom.xml
@@ -35,6 +35,7 @@
         <module>google-cloud-functions-quickstart</module>
         <module>google-cloud-functions-http-quickstart</module>
         <module>hibernate-orm-quickstart</module>
+        <module>hibernate-orm-jakarta-data-quickstart</module>
         <module>hibernate-reactive-quickstart</module>
         <module>hibernate-reactive-stateless-quickstart</module>
         <module>hibernate-reactive-routes-quickstart</module>


Reviews:
user: login: yrodiere
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T13:14:41Z
user: login: marko-bekhta
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-18T09:30:32Z
user: login: marko-bekhta
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-18T10:29:09Z
user: login: gsmet
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-18T12:26:10Z
user: login: yrodiere
body: Thanks. LGTM, but it's still a draft? Is there still something missing?
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-24T07:36:39Z

Review comments:
diff_hunk: @@ -0,0 +1,119 @@
+# Quarkus demo: Hibernate Data Repositories (Jakarta Data) and RESTEasy
+
+This is a minimal CRUD service exposing a couple of endpoints over REST,
+with a front-end based on Angular so you can play with it from your browser.
+
+While the code is surprisingly simple, under the hood this is using:
+ - [Hibernate Data Repositories (Jakarta Data)](https://hibernate.org/repositories/) to perform the CRUD operations on the database
+ - RESTEasy to expose the REST endpoints
+ - A PostgreSQL database; see below to run one via Docker
+ - ArC, the CDI inspired dependency injection tool with zero overhead
+ - The high performance Agroal connection pool
+ - Infinispan based caching
path: hibernate-orm-jakarta-data-quickstart/README.md
user: login: yrodiere
body: ????
created_at: 2025-04-11T12:37:00Z
author_association: MEMBER
path: hibernate-orm-jakarta-data-quickstart/README.md
user: login: yrodiere
body: Can you try to use what Luca did in his PR as a template for your README? https://github.com/quarkusio/quarkus-quickstarts/pull/1520
If you copied from the existing projects, you probably copied some outdated/incorrect information.
created_at: 2025-04-11T12:38:45Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.acme</groupId>
+    <artifactId>hibernate-orm-jakarta-data-quickstart</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+
+    <properties>
+        <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
+        <quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
+        <quarkus.platform.version>999-SNAPSHOT</quarkus.platform.version>
+        <surefire-plugin.version>3.1.2</surefire-plugin.version>
+        <compiler-plugin.version>3.14.0</compiler-plugin.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <maven.compiler.release>17</maven.compiler.release>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>${quarkus.platform.group-id}</groupId>
+                <artifactId>${quarkus.platform.artifact-id}</artifactId>
+                <version>${quarkus.platform.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-hibernate-orm</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.data</groupId>
+            <artifactId>jakarta.data-api</artifactId>
+            <!-- TODO: remove the version as it is suppose to be managed by Quarkus BOM -->
path: hibernate-orm-jakarta-data-quickstart/pom.xml
user: login: yrodiere
body: TODO :)
created_at: 2025-04-11T12:39:00Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,94 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
+#
+# Before building the container image run:
+#
+# ./mvnw package -Dquarkus.package.jar.type=legacy-jar
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/hibernate-orm-quickstart-legacy-jar .
path: hibernate-orm-jakarta-data-quickstart/src/main/docker/Dockerfile.legacy-jar
user: login: yrodiere
body: I fear copy-pasting won't cut it: these files include the name of your project.

Maybe try creating a new quarkus project (`quarkus create`) with the same name, and copy the files here?
created_at: 2025-04-11T12:41:07Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,49 @@
+package org.acme.hibernate.orm;
+
+import jakarta.persistence.Cacheable;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+import jakarta.persistence.NamedQuery;
+import jakarta.persistence.QueryHint;
+import jakarta.persistence.SequenceGenerator;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(name = "known_fruits")
path: hibernate-orm-jakarta-data-quickstart/src/main/java/org/acme/hibernate/orm/Fruit.java
user: login: yrodiere
body: Not necessary; see also Luca's PR.

```suggestion
```
created_at: 2025-04-11T12:41:31Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,49 @@
+package org.acme.hibernate.orm;
+
+import jakarta.persistence.Cacheable;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+import jakarta.persistence.NamedQuery;
+import jakarta.persistence.QueryHint;
+import jakarta.persistence.SequenceGenerator;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(name = "known_fruits")
+@Cacheable
+public class Fruit {
+
+    @Id
+    @SequenceGenerator(name = "fruitsSequence", sequenceName = "known_fruits_id_seq", allocationSize = 1, initialValue = 10)
+    @GeneratedValue(generator = "fruitsSequence")
path: hibernate-orm-jakarta-data-quickstart/src/main/java/org/acme/hibernate/orm/Fruit.java
user: login: yrodiere
body: ```suggestion
    @GeneratedValue
```
created_at: 2025-04-11T12:41:42Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,25 @@
+package org.acme.hibernate.orm;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+import jakarta.data.Order;
+import jakarta.data.repository.CrudRepository;
+import jakarta.data.repository.Delete;
+import jakarta.data.repository.Find;
+import jakarta.data.repository.Query;
+import jakarta.data.repository.Repository;
+
+@Repository
+public interface FruitRepository extends CrudRepository<Fruit, Integer> {
+
+	@Find
+	Stream<Fruit> findAll(Order<Fruit> order);
+
+	@Query("UPDATE Fruit f SET f.name = :name WHERE f.id = :id")
path: hibernate-orm-jakarta-data-quickstart/src/main/java/org/acme/hibernate/orm/FruitRepository.java
user: login: yrodiere
body: Just saying, but Gavin will have a stroke if he sees all caps keywords in queries.
created_at: 2025-04-11T13:09:45Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,110 @@
+package org.acme.hibernate.orm;
+
+import java.util.List;
+
+import org.jboss.logging.Logger;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import jakarta.data.Order;
+import jakarta.data.Sort;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.transaction.Transactional;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.ExceptionMapper;
+import jakarta.ws.rs.ext.Provider;
+
+@Path("fruits")
+@ApplicationScoped
+@Produces("application/json")
+@Consumes("application/json")
+public class FruitResource {
+
+    private static final Logger LOGGER = Logger.getLogger(FruitResource.class.getName());
+
+    @Inject
+    FruitRepository repository;
+
+    @GET
+    public List<Fruit> get() {
+        return repository.findAll( Order.by( Sort.asc( Fruit_.NAME ) ) ).toList();
+    }
+
+    @GET
+    @Path("{id}")
+    public Fruit getSingle(Integer id) {
+        return repository.findById( id )
+                .orElseThrow( () -> new WebApplicationException( "Fruit with id of %d does not exist.".formatted( id ), 404 ) );
+    }
+
+    @POST
+    @Transactional
+    public Response create(Fruit fruit) {
+        if (fruit.getId() != null) {
+            throw new WebApplicationException("Id was invalidly set on request.", 422);
+        }
+
+        repository.insert(fruit);
+        return Response.ok(fruit).status(201).build();
+    }
+
+    @PUT
+    @Path("{id}")
+    @Transactional
+    public Fruit update(Integer id, Fruit fruit) {
+        if (fruit.getName() == null) {
+            throw new WebApplicationException("Fruit Name was not set on request.", 422);
+        }
+
+        repository.update(id, fruit.getName());
+
+        return fruit;
+    }
+
+    @DELETE
+    @Path("{id}")
+    @Transactional
+    public Response delete(Integer id) {
+        repository.delete(id);
+        return Response.status(204).build();
+    }
+
+    @Provider
+    public static class ErrorMapper implements ExceptionMapper<Exception> {
+
+        @Inject
+        ObjectMapper objectMapper;
+
+        @Override
+        public Response toResponse(Exception exception) {
+            LOGGER.error("Failed to handle request", exception);
+
+            int code = 500;
+            if (exception instanceof WebApplicationException) {
+                code = ((WebApplicationException) exception).getResponse().getStatus();
+            }
+
+            ObjectNode exceptionJson = objectMapper.createObjectNode();
+            exceptionJson.put("exceptionType", exception.getClass().getName());
+            exceptionJson.put("code", code);
+
+            if (exception.getMessage() != null) {
+                exceptionJson.put("error", exception.getMessage());
+            }
+
+            return Response.status(code)
+                    .entity(exceptionJson)
+                    .build();
+        }
path: hibernate-orm-jakarta-data-quickstart/src/main/java/org/acme/hibernate/orm/FruitResource.java
user: login: yrodiere
body: Is this necessary?

The point of quickstarts is partly to show how succinct the code can be. Adding unnecessary features like this kind of defeats that purpose.
created_at: 2025-04-11T13:11:17Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,10 @@
+%prod.quarkus.datasource.db-kind=postgresql
+%prod.quarkus.datasource.username=quarkus_test
+%prod.quarkus.datasource.password=quarkus_test
+%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://localhost/quarkus_test
+%prod.quarkus.datasource.jdbc.max-size=8
+%prod.quarkus.datasource.jdbc.min-size=2
+%prod.quarkus.hibernate-orm.database.generation=create
+
+quarkus.hibernate-orm.log.sql=true
+quarkus.hibernate-orm.sql-load-script=import.sql
path: hibernate-orm-jakarta-data-quickstart/src/main/resources/application.properties
user: login: yrodiere
body: ```suggestion
quarkus.hibernate-orm.sql-load-script=import.sql

%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://localhost/quarkus_test
%prod.quarkus.datasource.username=quarkus_test
%prod.quarkus.datasource.password=quarkus_test
%prod.quarkus.hibernate-orm.database.generation=create
```
created_at: 2025-04-11T13:14:13Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,110 @@
+package org.acme.hibernate.orm;
+
+import java.util.List;
+
+import org.jboss.logging.Logger;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import jakarta.data.Order;
+import jakarta.data.Sort;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.transaction.Transactional;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.ext.ExceptionMapper;
+import jakarta.ws.rs.ext.Provider;
+
+@Path("fruits")
+@ApplicationScoped
+@Produces("application/json")
+@Consumes("application/json")
+public class FruitResource {
+
+    private static final Logger LOGGER = Logger.getLogger(FruitResource.class.getName());
+
+    @Inject
+    FruitRepository repository;
+
+    @GET
+    public List<Fruit> get() {
+        return repository.findAll( Order.by( Sort.asc( Fruit_.NAME ) ) ).toList();
+    }
+
+    @GET
+    @Path("{id}")
+    public Fruit getSingle(Integer id) {
+        return repository.findById( id )
+                .orElseThrow( () -> new WebApplicationException( "Fruit with id of %d does not exist.".formatted( id ), 404 ) );
+    }
+
+    @POST
+    @Transactional
+    public Response create(Fruit fruit) {
+        if (fruit.getId() != null) {
+            throw new WebApplicationException("Id was invalidly set on request.", 422);
+        }
+
+        repository.insert(fruit);
+        return Response.ok(fruit).status(201).build();
+    }
+
+    @PUT
+    @Path("{id}")
+    @Transactional
+    public Fruit update(Integer id, Fruit fruit) {
+        if (fruit.getName() == null) {
+            throw new WebApplicationException("Fruit Name was not set on request.", 422);
+        }
+
+        repository.update(id, fruit.getName());
+
+        return fruit;
+    }
+
+    @DELETE
+    @Path("{id}")
+    @Transactional
+    public Response delete(Integer id) {
+        repository.delete(id);
+        return Response.status(204).build();
+    }
+
+    @Provider
+    public static class ErrorMapper implements ExceptionMapper<Exception> {
+
+        @Inject
+        ObjectMapper objectMapper;
+
+        @Override
+        public Response toResponse(Exception exception) {
+            LOGGER.error("Failed to handle request", exception);
+
+            int code = 500;
+            if (exception instanceof WebApplicationException) {
+                code = ((WebApplicationException) exception).getResponse().getStatus();
+            }
+
+            ObjectNode exceptionJson = objectMapper.createObjectNode();
+            exceptionJson.put("exceptionType", exception.getClass().getName());
+            exceptionJson.put("code", code);
+
+            if (exception.getMessage() != null) {
+                exceptionJson.put("error", exception.getMessage());
+            }
+
+            return Response.status(code)
+                    .entity(exceptionJson)
+                    .build();
+        }
path: hibernate-orm-jakarta-data-quickstart/src/main/java/org/acme/hibernate/orm/FruitResource.java
user: login: marko-bekhta
body: yeah, not really needed, just what was in the orm quickstart
created_at: 2025-04-18T09:30:32Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,119 @@
+# Quarkus demo: Hibernate Data Repositories (Jakarta Data) and RESTEasy
+
+This is a minimal CRUD service exposing a couple of endpoints over REST,
+with a front-end based on Angular so you can play with it from your browser.
+
+While the code is surprisingly simple, under the hood this is using:
+ - [Hibernate Data Repositories (Jakarta Data)](https://hibernate.org/repositories/) to perform the CRUD operations on the database
+ - RESTEasy to expose the REST endpoints
+ - A PostgreSQL database; see below to run one via Docker
+ - ArC, the CDI inspired dependency injection tool with zero overhead
+ - The high performance Agroal connection pool
+ - Infinispan based caching
path: hibernate-orm-jakarta-data-quickstart/README.md
user: login: marko-bekhta
body: Surprised as much as you or even more 😄😆, yeah, we talked about this one elsewhere, will clean it up!
created_at: 2025-04-18T10:29:09Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,119 @@
+# Quarkus demo: Hibernate Data Repositories (Jakarta Data) and RESTEasy
+
+This is a minimal CRUD service exposing a couple of endpoints over REST,
+with a front-end based on Angular so you can play with it from your browser.
+
+While the code is surprisingly simple, under the hood this is using:
+ - [Hibernate Data Repositories (Jakarta Data)](https://hibernate.org/repositories/) to perform the CRUD operations on the database
+ - RESTEasy to expose the REST endpoints
+ - A PostgreSQL database; see below to run one via Docker
+ - ArC, the CDI inspired dependency injection tool with zero overhead
+ - The high performance Agroal connection pool
+ - Infinispan based caching
path: hibernate-orm-jakarta-data-quickstart/README.md
user: login: gsmet
body: You can drop the README, really.

Or you can just have a title and a link to the guide.
created_at: 2025-04-18T12:26:10Z
author_association: MEMBER

PR comments:
user: login: marko-bekhta
created_at: 2025-04-24T07:46:24Z
body: hey 👋🏻 

>  Is there still something missing?

- I wanted to wait for the PR Luca opened to get merged first to see if there'd be any other updates there, but I think it's already in.
- I noticed some "issues" with transactions/cdi contexts and repositories so wanted to check with Gavin to see what he thinks about it https://github.com/hibernate/hibernate-orm/pull/9855#discussion_r2050448996. but now as it got changed (https://github.com/hibernate/hibernate-orm/commit/e0c81b97ecfa8ad54fda498f0196fa545be76c51) I'm not sure whether to wait for that update to be released or leave things as they currently are in this PR.
- Guillaume had that suggestion for the readme, so I was just waiting to see what Guillaume and you decide that we want to do here 😃 

Other than that ... 🟢 😃 
author_association: MEMBER
user: login: yrodiere
created_at: 2025-04-24T08:51:06Z
body: > hey 👋🏻
> 
> > Is there still something missing?
> 
>     * I wanted to wait for the PR Luca opened to get merged first to see if there'd be any other updates there, but I think it's already in.

It's in.

>     * I noticed some "issues" with transactions/cdi contexts and repositories so wanted to check with Gavin to see what he thinks about it [HHH-19109 fix use of @TransactionScoped by repository hibernate/hibernate-orm#9855 (comment)](https://github.com/hibernate/hibernate-orm/pull/9855#discussion_r2050448996). but now as it got changed ([hibernate/hibernate-orm@e0c81b9](https://github.com/hibernate/hibernate-orm/commit/e0c81b97ecfa8ad54fda498f0196fa545be76c51)) I'm not sure whether to wait for that update to be released or leave things as they currently are in this PR.

The best practice is to use `@Transactional` everywhere.  You can put it on the REST Resource class if that's easier. I wouldn't worry about other (`@Transactional`-less) use cases for this PR.

>     * Guillaume had that suggestion for the readme, so I was just waiting to see what Guillaume and you decide that we want to do here 😃

He suggested to remove it, IIRC? Or at least replace it with just a title + link to docs. Fine by me, but should be a separate PR that would need to address _all_ Hibernate quickstarts. So it's fine to merge your PR as is. 

So... merging :)
author_association: MEMBER
user: login: yrodiere
created_at: 2025-04-24T08:51:50Z
body: @marko-bekhta Did you link to this quickstart from the Quarkus docs? You probably should, otherwise nobody will notice it :)
author_association: MEMBER
user: login: marko-bekhta
created_at: 2025-04-24T08:54:18Z
body: > You probably should

thanks for the suggestion 😃  will send a PR later 👍🏻
author_association: MEMBER