diff --git a/.github/workflows/ci-actions.yml b/.github/workflows/ci-actions.yml
index 8f51c4b65e3c7..f59379f0dc061 100644
--- a/.github/workflows/ci-actions.yml
+++ b/.github/workflows/ci-actions.yml
@@ -416,13 +416,12 @@ jobs:
- category: Data2
db2: "true"
mysql: "true"
- postgres: "true"
mariadb: "true"
timeout: 65
test-modules: >
jpa
- jpa-postgresql
jpa-mysql
+ jpa-db2
reactive-mysql-client
reactive-db2-client
hibernate-reactive-db2
@@ -445,8 +444,9 @@ jobs:
hibernate-orm-rest-data-panache
- category: Data5
postgres: "true"
- timeout: 55
+ timeout: 60
test-modules: >
+ jpa-postgresql
hibernate-search-elasticsearch
narayana-stm
narayana-jta
diff --git a/bom/runtime/pom.xml b/bom/runtime/pom.xml
index c400702dcbe8d..574b8ca71ec70 100644
--- a/bom/runtime/pom.xml
+++ b/bom/runtime/pom.xml
@@ -115,6 +115,7 @@
8.0.20
7.2.2.jre8
10.14.2.0
+ 11.5.4.0
1.2.6
4.3.0
5.6.2
@@ -708,6 +709,11 @@
quarkus-jaeger-deployment
${project.version}
+
+ io.quarkus
+ quarkus-jdbc-db2
+ ${project.version}
+
io.quarkus
quarkus-jdbc-postgresql
@@ -3305,6 +3311,11 @@
derbytools
${derby-jdbc.version}
+
+ com.ibm.db2
+ jcc
+ ${db2-jdbc.version}
+
com.microsoft.sqlserver
mssql-jdbc
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/Feature.java b/core/deployment/src/main/java/io/quarkus/deployment/Feature.java
index db95d5c78e9f3..d78efb58b7c0a 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/Feature.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/Feature.java
@@ -37,6 +37,7 @@ public enum Feature {
INFINISPAN_CLIENT,
INFINISPAN_EMBEDDED,
JAEGER,
+ JDBC_DB2,
JDBC_DERBY,
JDBC_H2,
JDBC_POSTGRESQL,
diff --git a/docs/src/main/asciidoc/datasource.adoc b/docs/src/main/asciidoc/datasource.adoc
index 578c480759777..51c536ec7934b 100644
--- a/docs/src/main/asciidoc/datasource.adoc
+++ b/docs/src/main/asciidoc/datasource.adoc
@@ -42,7 +42,7 @@ If you want a better understanding of how all this works, this guide has a lot m
=== JDBC datasource
-Add the `agroal` extension plus one of `jdbc-derby`, `jdbc-h2`, `jdbc-mariadb`, `jdbc-mssql`, `jdbc-mysql` or `jdbc-postgresql`.
+Add the `agroal` extension plus one of `jdbc-db2`, `jdbc-derby`, `jdbc-h2`, `jdbc-mariadb`, `jdbc-mssql`, `jdbc-mysql`, or `jdbc-postgresql`.
Then configure your datasource:
@@ -89,7 +89,7 @@ The database kind defines which type of database you will connect to.
We currently include these built-in database kinds:
-* DB2: `db2` (reactive only)
+* DB2: `db2`
* Derby: `derby`
* H2: `h2`
* MariaDB: `mariadb`
@@ -151,6 +151,7 @@ You will also need to choose, and add, the Quarkus extension for your relational
Quarkus provides driver extensions for:
+* DB2 - `jdbc-db2`
* Derby - `jdbc-derby`
* H2 - `jdbc-h2`
* MariaDB - `jdbc-mariadb`
@@ -203,6 +204,10 @@ When using one of the built-in datasource kinds, the JDBC driver is resolved aut
|===
|Database kind |JDBC driver |XA driver
+|`db2`
+|`com.ibm.db2.jcc.DBDriver`
+|`com.ibm.db2.jcc.DB2XADataSource`
+
|`derby`
|`org.apache.derby.jdbc.ClientDriver`
|`org.apache.derby.jdbc.ClientXADataSource`
@@ -524,6 +529,14 @@ Defaults for the different parts are as follows:
The https://jdbc.postgresql.org/documentation/head/connect.html[official documentation] go into more detail and list optional parameters as well.
+=== DB2
+
+`jdbc:db2://[:]/[:=;[=;]]`
+
+Example:: `jdbc:db2://localhost:50000/MYDB:user=dbadm;password=dbadm;`
+
+See the https://www.ibm.com/support/knowledgecenter/SSEPGG_11.5.0/com.ibm.db2.luw.apdv.java.doc/src/tpc/imjcc_r0052342.html[official documentation] for more detail on URL syntax and additional supported options.
+
=== MariaDB
`jdbc:mariadb:[replication:|failover:|sequential:|aurora:]//[,...]/[database][?=[&=]]`
diff --git a/docs/src/main/asciidoc/hibernate-orm.adoc b/docs/src/main/asciidoc/hibernate-orm.adoc
index 8f006f109a8f5..a21f9961170cc 100644
--- a/docs/src/main/asciidoc/hibernate-orm.adoc
+++ b/docs/src/main/asciidoc/hibernate-orm.adoc
@@ -29,6 +29,7 @@ Add the following dependencies to your project:
* the Hibernate ORM extension: `io.quarkus:quarkus-hibernate-orm`
* your JDBC driver extension; the following options are available:
+ - `quarkus-jdbc-db2` for link:https://www.ibm.com/products/db2-database[IBM DB2]
- `quarkus-jdbc-derby` for link:https://db.apache.org/derby/[Apache Derby]
- `quarkus-jdbc-h2` for link:https://www.h2database.com/html/main.html[H2]
- `quarkus-jdbc-mariadb` for link:https://mariadb.com/[MariaDB]
diff --git a/extensions/jdbc/jdbc-db2/deployment/pom.xml b/extensions/jdbc/jdbc-db2/deployment/pom.xml
new file mode 100644
index 0000000000000..9bfe433e2ca74
--- /dev/null
+++ b/extensions/jdbc/jdbc-db2/deployment/pom.xml
@@ -0,0 +1,50 @@
+
+
+
+ quarkus-jdbc-db2-parent
+ io.quarkus
+ 999-SNAPSHOT
+
+ 4.0.0
+
+ quarkus-jdbc-db2-deployment
+ Quarkus - JDBC - DB2 - Deployment
+
+
+
+ io.quarkus
+ quarkus-core-deployment
+
+
+ io.quarkus
+ quarkus-arc-deployment
+
+
+ io.quarkus
+ quarkus-agroal-spi
+
+
+ io.quarkus
+ quarkus-jdbc-db2
+
+
+
+
+
+
+ maven-compiler-plugin
+
+
+
+ io.quarkus
+ quarkus-extension-processor
+ ${project.version}
+
+
+
+
+
+
+
diff --git a/extensions/jdbc/jdbc-db2/deployment/src/main/java/io/quarkus/jdbc/db2/deployment/JDBCDB2Processor.java b/extensions/jdbc/jdbc-db2/deployment/src/main/java/io/quarkus/jdbc/db2/deployment/JDBCDB2Processor.java
new file mode 100644
index 0000000000000..6d75ad8c51242
--- /dev/null
+++ b/extensions/jdbc/jdbc-db2/deployment/src/main/java/io/quarkus/jdbc/db2/deployment/JDBCDB2Processor.java
@@ -0,0 +1,59 @@
+package io.quarkus.jdbc.db2.deployment;
+
+import io.quarkus.agroal.deployment.JdbcDriverBuildItem;
+import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
+import io.quarkus.arc.processor.BuiltinScope;
+import io.quarkus.datasource.common.runtime.DatabaseKind;
+import io.quarkus.deployment.Capabilities;
+import io.quarkus.deployment.Capability;
+import io.quarkus.deployment.Feature;
+import io.quarkus.deployment.annotations.BuildProducer;
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.builditem.FeatureBuildItem;
+import io.quarkus.deployment.builditem.NativeImageEnableAllCharsetsBuildItem;
+import io.quarkus.deployment.builditem.SslNativeConfigBuildItem;
+import io.quarkus.deployment.builditem.nativeimage.NativeImageConfigBuildItem;
+import io.quarkus.jdbc.db2.runtime.DB2AgroalConnectionConfigurer;
+
+public class JDBCDB2Processor {
+
+ @BuildStep
+ FeatureBuildItem feature() {
+ return new FeatureBuildItem(Feature.JDBC_DB2);
+ }
+
+ @BuildStep
+ void registerDriver(BuildProducer jdbcDriver,
+ SslNativeConfigBuildItem sslNativeConfigBuildItem) {
+ jdbcDriver.produce(new JdbcDriverBuildItem(DatabaseKind.DB2, "com.ibm.db2.jcc.DB2Driver",
+ "com.ibm.db2.jcc.DB2XADataSource"));
+ }
+
+ @BuildStep
+ void configureAgroalConnection(BuildProducer additionalBeans,
+ Capabilities capabilities) {
+ if (capabilities.isPresent(Capability.AGROAL)) {
+ additionalBeans
+ .produce(new AdditionalBeanBuildItem.Builder().addBeanClass(DB2AgroalConnectionConfigurer.class)
+ .setDefaultScope(BuiltinScope.APPLICATION.getName())
+ .setUnremovable()
+ .build());
+ }
+ }
+
+ @BuildStep
+ NativeImageConfigBuildItem build() {
+ // The DB2 JDBC driver has been updated with conditional checks for the
+ // "QuarkusWithJcc" system property which will no-op some code paths that
+ // are not needed for T4 JDBC usage and are incompatible with native mode
+ return NativeImageConfigBuildItem.builder()
+ .addNativeImageSystemProperty("QuarkusWithJcc", "true")
+ .build();
+ }
+
+ @BuildStep
+ NativeImageEnableAllCharsetsBuildItem enableAllCharsets() {
+ // When connecting to DB2 on z/OS the Cp037 charset is required
+ return new NativeImageEnableAllCharsetsBuildItem();
+ }
+}
diff --git a/extensions/jdbc/jdbc-db2/pom.xml b/extensions/jdbc/jdbc-db2/pom.xml
new file mode 100644
index 0000000000000..6006e86aa6aae
--- /dev/null
+++ b/extensions/jdbc/jdbc-db2/pom.xml
@@ -0,0 +1,21 @@
+
+
+
+ quarkus-jdbc-parent
+ io.quarkus
+ 999-SNAPSHOT
+
+ 4.0.0
+
+ quarkus-jdbc-db2-parent
+ Quarkus - JDBC - DB2
+ pom
+
+ deployment
+ runtime
+
+
+
+
diff --git a/extensions/jdbc/jdbc-db2/runtime/pom.xml b/extensions/jdbc/jdbc-db2/runtime/pom.xml
new file mode 100644
index 0000000000000..c3f92096be0df
--- /dev/null
+++ b/extensions/jdbc/jdbc-db2/runtime/pom.xml
@@ -0,0 +1,56 @@
+
+
+
+ quarkus-jdbc-db2-parent
+ io.quarkus
+ 999-SNAPSHOT
+
+ 4.0.0
+
+ quarkus-jdbc-db2
+ Quarkus - JDBC - DB2 - Runtime
+ Connect to the DB2 database via JDBC
+
+
+
+ com.ibm.db2
+ jcc
+
+
+ io.quarkus
+ quarkus-core
+
+
+ io.quarkus
+ quarkus-agroal
+ true
+
+
+ org.graalvm.nativeimage
+ svm
+
+
+
+
+
+
+ io.quarkus
+ quarkus-bootstrap-maven-plugin
+
+
+ maven-compiler-plugin
+
+
+
+ io.quarkus
+ quarkus-extension-processor
+ ${project.version}
+
+
+
+
+
+
+
diff --git a/extensions/jdbc/jdbc-db2/runtime/src/main/java/io/quarkus/jdbc/db2/runtime/DB2AgroalConnectionConfigurer.java b/extensions/jdbc/jdbc-db2/runtime/src/main/java/io/quarkus/jdbc/db2/runtime/DB2AgroalConnectionConfigurer.java
new file mode 100644
index 0000000000000..3939d6d46e8b7
--- /dev/null
+++ b/extensions/jdbc/jdbc-db2/runtime/src/main/java/io/quarkus/jdbc/db2/runtime/DB2AgroalConnectionConfigurer.java
@@ -0,0 +1,9 @@
+package io.quarkus.jdbc.db2.runtime;
+
+import io.quarkus.agroal.runtime.AgroalConnectionConfigurer;
+import io.quarkus.agroal.runtime.JdbcDriver;
+import io.quarkus.datasource.common.runtime.DatabaseKind;
+
+@JdbcDriver(DatabaseKind.DB2)
+public class DB2AgroalConnectionConfigurer implements AgroalConnectionConfigurer {
+}
diff --git a/extensions/jdbc/jdbc-db2/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/jdbc/jdbc-db2/runtime/src/main/resources/META-INF/quarkus-extension.yaml
new file mode 100644
index 0000000000000..a3fa12e7954cd
--- /dev/null
+++ b/extensions/jdbc/jdbc-db2/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -0,0 +1,9 @@
+---
+name: "JDBC Driver - DB2"
+metadata:
+ keywords:
+ - "jdbc-db2"
+ - "jdbc"
+ - "db2"
+ categories:
+ - "data"
diff --git a/extensions/jdbc/pom.xml b/extensions/jdbc/pom.xml
index daa20c372a228..a9e4512de15b5 100644
--- a/extensions/jdbc/pom.xml
+++ b/extensions/jdbc/pom.xml
@@ -15,6 +15,7 @@
pom
jdbc-postgresql
+ jdbc-db2
jdbc-derby
jdbc-h2
jdbc-mariadb
diff --git a/integration-tests/jpa-db2/README.md b/integration-tests/jpa-db2/README.md
new file mode 100644
index 0000000000000..0f9ae1f5799db
--- /dev/null
+++ b/integration-tests/jpa-db2/README.md
@@ -0,0 +1,40 @@
+# JPA example with DB2
+
+## Running the tests
+
+By default, the tests of this module are disabled.
+
+To run the tests in a standard JVM with DB2 started as a Docker container, you can run the following command:
+
+```
+mvn verify -Dtest-db2 -Ddocker
+```
+
+Additionally, you can generate a native image and run the tests for this native image by adding `-Dnative`:
+
+```
+mvn verify -Dtest-db2 -Ddocker -Dnative
+```
+
+## To manually run an equivalent DB2 container instead of through Testcontainers, do the following:
+
+1. Start DB2 in a container
+
+```
+docker run \
+ -e DBNAME=hreact \
+ -e DB2INSTANCE=hreact \
+ -e DB2INST1_PASSWORD=hreact \
+ -e AUTOCONFIG=false \
+ -e ARCHIVE_LOGS=false \
+ -e LICENSE=accept \
+ -p 50000:50000 \
+ --privileged \
+ ibmcom/db2:11.5.0.0a
+```
+
+2. Run the test, specifying the JDBC URL for the container you started in the previous step
+
+```
+mvn verify -Dtest-db2 -Djdbc-db2.url=jdbc:db2://localhost:50000/hreact
+```
diff --git a/integration-tests/jpa-db2/pom.xml b/integration-tests/jpa-db2/pom.xml
new file mode 100644
index 0000000000000..11547c09c547c
--- /dev/null
+++ b/integration-tests/jpa-db2/pom.xml
@@ -0,0 +1,247 @@
+
+
+
+ quarkus-integration-tests-parent
+ io.quarkus
+ 999-SNAPSHOT
+ ../
+
+ 4.0.0
+
+ quarkus-integration-test-jpa-db2
+ Quarkus - Integration Tests - JPA - DB2
+ Module that contains JPA related tests running with the DB2 database
+
+
+ jdbc:db2://localhost:50000/hreact
+ ibmcom/db2:11.5.0.0a
+
+
+
+
+ io.quarkus
+ quarkus-undertow
+
+
+ io.quarkus
+ quarkus-hibernate-orm
+
+
+ io.quarkus
+ quarkus-jdbc-db2
+
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+
+
+ maven-surefire-plugin
+
+ true
+
+
+
+ maven-failsafe-plugin
+
+ true
+
+
+
+ io.quarkus
+ quarkus-maven-plugin
+
+
+
+ build
+
+
+
+
+
+
+
+
+
+ test-db2
+
+
+ test-db2
+
+
+
+
+
+ maven-surefire-plugin
+
+ false
+
+
+
+ maven-failsafe-plugin
+
+ false
+
+
+
+
+
+
+
+ native-image
+
+
+ native
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+
+ integration-test
+ verify
+
+
+
+
+ ${project.build.directory}/${project.build.finalName}-runner
+
+
+
+
+
+
+
+ io.quarkus
+ quarkus-maven-plugin
+
+
+ native-image
+
+ native-image
+
+
+ true
+ true
+ ${graalvmHome}
+
+
+
+
+
+
+
+
+
+ docker-db2
+
+
+ docker
+
+
+
+ jdbc:db2://localhost:50005/hreact
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+
+
+
+ ${db2.image}
+ quarkus-test-db2
+
+
+ bridge
+
+ true
+
+ 50005:50000
+
+
+ hreact
+ hreact
+ hreact
+ accept
+
+ false
+ false
+
+
+ DB2:
+ default
+ cyan
+
+
+
+ .*Setup has completed\..*
+
+
+
+
+
+
+
+
+
+ docker-start
+ compile
+
+ stop
+ start
+
+
+
+ docker-stop
+ post-integration-test
+
+ stop
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+
+ docker-prune
+ generate-resources
+
+ exec
+
+
+ ${basedir}/../../.github/docker-prune.sh
+
+
+
+
+
+
+
+
+
diff --git a/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Address.java b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Address.java
new file mode 100644
index 0000000000000..a473d1cb87767
--- /dev/null
+++ b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Address.java
@@ -0,0 +1,42 @@
+package io.quarkus.it.jpa.db2;
+
+import javax.persistence.Embeddable;
+
+/**
+ * This is an enmarked @Embeddable class.
+ * Let's see if just being referenced by the main entity is enough to be detected.
+ *
+ * @author Emmanuel Bernard emmanuel@hibernate.org
+ */
+//FIXME : this used to be non-annotated explicitly for testing purposes
+// added the annotation as it's illegal according to the ORM metadata validation
+@Embeddable
+public class Address {
+ private String street1;
+ private String street2;
+ private String zipCode;
+
+ public String getStreet1() {
+ return street1;
+ }
+
+ public void setStreet1(String street1) {
+ this.street1 = street1;
+ }
+
+ public String getStreet2() {
+ return street2;
+ }
+
+ public void setStreet2(String street2) {
+ this.street2 = street2;
+ }
+
+ public String getZipCode() {
+ return zipCode;
+ }
+
+ public void setZipCode(String zipCode) {
+ this.zipCode = zipCode;
+ }
+}
diff --git a/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Animal.java b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Animal.java
new file mode 100644
index 0000000000000..c02474d45c22e
--- /dev/null
+++ b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Animal.java
@@ -0,0 +1,16 @@
+package io.quarkus.it.jpa.db2;
+
+/**
+ * @author Emmanuel Bernard emmanuel@hibernate.org
+ */
+public class Animal {
+ private double weight;
+
+ public double getWeight() {
+ return weight;
+ }
+
+ public void setWeight(double weight) {
+ this.weight = weight;
+ }
+}
diff --git a/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Customer.java b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Customer.java
new file mode 100644
index 0000000000000..b0ebf79488a49
--- /dev/null
+++ b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Customer.java
@@ -0,0 +1,48 @@
+package io.quarkus.it.jpa.db2;
+
+import javax.persistence.Embedded;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+/**
+ * Used to test reflection references for JPA
+ *
+ * @author Emmanuel Bernard emmanuel@hibernate.org
+ */
+@Entity
+public class Customer extends Human {
+ @Id
+ // no getter explicitly to test field only reflective access
+ private Long id;
+
+ private Address address;
+ private WorkAddress workAddress;
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ // Address is referenced but not marked as @Embeddable
+ @Embedded
+ public Address getAddress() {
+ return address;
+ }
+
+ public WorkAddress getWorkAddress() {
+ return workAddress;
+ }
+
+ public void setWorkAddress(WorkAddress workAddress) {
+ this.workAddress = workAddress;
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+}
diff --git a/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Human.java b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Human.java
new file mode 100644
index 0000000000000..dddb023b353cc
--- /dev/null
+++ b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Human.java
@@ -0,0 +1,21 @@
+package io.quarkus.it.jpa.db2;
+
+import javax.persistence.MappedSuperclass;
+
+/**
+ * Mapped superclass test
+ *
+ * @author Emmanuel Bernard emmanuel@hibernate.org
+ */
+@MappedSuperclass
+public class Human extends Animal {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/JPAFunctionalityTestEndpoint.java b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/JPAFunctionalityTestEndpoint.java
new file mode 100644
index 0000000000000..0169577b8fbb8
--- /dev/null
+++ b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/JPAFunctionalityTestEndpoint.java
@@ -0,0 +1,158 @@
+package io.quarkus.it.jpa.db2;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.UUID;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.EntityTransaction;
+import javax.persistence.PersistenceUnit;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Root;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Various tests covering JPA functionality. All tests should work in both standard JVM and native mode.
+ */
+@SuppressWarnings("serial")
+@WebServlet(name = "JPATestBootstrapEndpoint", urlPatterns = "/jpa/testfunctionality")
+public class JPAFunctionalityTestEndpoint extends HttpServlet {
+
+ @PersistenceUnit(unitName = "templatePU")
+ EntityManagerFactory entityManagerFactory;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ try {
+ doStuffWithHibernate(entityManagerFactory);
+ } catch (Exception e) {
+ reportException("An error occurred while performing Hibernate operations", e, resp);
+ }
+ resp.getWriter().write("OK");
+ }
+
+ /**
+ * Lists the various operations we want to test for:
+ */
+ private static void doStuffWithHibernate(EntityManagerFactory entityManagerFactory) {
+
+ //Cleanup any existing data:
+ deleteAllPerson(entityManagerFactory);
+
+ //Store some well known Person instances we can then test on:
+ storeTestPersons(entityManagerFactory);
+
+ //Load all persons and run some checks on the query results:
+ verifyListOfExistingPersons(entityManagerFactory);
+
+ //Try a JPA named query:
+ verifyJPANamedQuery(entityManagerFactory);
+
+ deleteAllPerson(entityManagerFactory);
+
+ }
+
+ private static void verifyJPANamedQuery(final EntityManagerFactory emf) {
+ EntityManager em = emf.createEntityManager();
+ EntityTransaction transaction = em.getTransaction();
+ transaction.begin();
+ TypedQuery typedQuery = em.createNamedQuery(
+ "get_person_by_name", Person.class);
+ typedQuery.setParameter("name", "Quarkus");
+ final Person singleResult = typedQuery.getSingleResult();
+
+ if (!singleResult.getName().equals("Quarkus")) {
+ throw new RuntimeException("Wrong result from named JPA query");
+ }
+
+ transaction.commit();
+ em.close();
+ }
+
+ private static void verifyListOfExistingPersons(final EntityManagerFactory emf) {
+ EntityManager em = emf.createEntityManager();
+ EntityTransaction transaction = em.getTransaction();
+ transaction.begin();
+ listExistingPersons(em);
+ transaction.commit();
+ em.close();
+ }
+
+ private static void storeTestPersons(final EntityManagerFactory emf) {
+ EntityManager em = emf.createEntityManager();
+ EntityTransaction transaction = em.getTransaction();
+ transaction.begin();
+ persistNewPerson(em, "Gizmo");
+ persistNewPerson(em, "Quarkus");
+ persistNewPerson(em, "Hibernate ORM");
+ transaction.commit();
+ em.close();
+ }
+
+ private static void deleteAllPerson(final EntityManagerFactory emf) {
+ EntityManager em = emf.createEntityManager();
+ EntityTransaction transaction = em.getTransaction();
+ transaction.begin();
+ em.createNativeQuery("Delete from Person").executeUpdate();
+ transaction.commit();
+ em.close();
+ }
+
+ private static void listExistingPersons(EntityManager em) {
+ CriteriaBuilder cb = em.getCriteriaBuilder();
+
+ CriteriaQuery cq = cb.createQuery(Person.class);
+ Root from = cq.from(Person.class);
+ cq.select(from).orderBy(cb.asc(from.get("name")));
+ TypedQuery q = em.createQuery(cq);
+ List allpersons = q.getResultList();
+ if (allpersons.size() != 3) {
+ throw new RuntimeException("Incorrect number of results");
+ }
+ if (!allpersons.get(0).getName().equals("Gizmo")) {
+ throw new RuntimeException("Incorrect order of results");
+ }
+ StringBuilder sb = new StringBuilder("list of stored Person names:\n\t");
+ for (Person p : allpersons) {
+ p.describeFully(sb);
+ sb.append("\n\t");
+ if (p.getStatus() != Status.LIVING) {
+ throw new RuntimeException("Incorrect status " + p);
+ }
+ }
+ sb.append("\nList complete.\n");
+ System.out.print(sb);
+ }
+
+ private static void persistNewPerson(EntityManager entityManager, String name) {
+ Person person = new Person();
+ person.setName(name);
+ person.setStatus(Status.LIVING);
+ person.setAddress(new SequencedAddress("Street " + randomName()));
+ entityManager.persist(person);
+ }
+
+ private static String randomName() {
+ return UUID.randomUUID().toString();
+ }
+
+ private void reportException(String errorMessage, final Exception e, final HttpServletResponse resp) throws IOException {
+ final PrintWriter writer = resp.getWriter();
+ if (errorMessage != null) {
+ writer.write(errorMessage);
+ writer.write(" ");
+ }
+ writer.write(e.toString());
+ writer.append("\n\t");
+ e.printStackTrace(writer);
+ writer.append("\n\t");
+ }
+
+}
diff --git a/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/NotAnEntityNotReferenced.java b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/NotAnEntityNotReferenced.java
new file mode 100644
index 0000000000000..2eb63793a824b
--- /dev/null
+++ b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/NotAnEntityNotReferenced.java
@@ -0,0 +1,11 @@
+package io.quarkus.it.jpa.db2;
+
+/**
+ * Should not be referenced by the code
+ * So be dead code eliminated.
+ * Used to test that entities get referenced but not non entities
+ *
+ * @author Emmanuel Bernard emmanuel@hibernate.org
+ */
+public class NotAnEntityNotReferenced {
+}
diff --git a/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Person.java b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Person.java
new file mode 100644
index 0000000000000..340ee10a5edbc
--- /dev/null
+++ b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Person.java
@@ -0,0 +1,71 @@
+package io.quarkus.it.jpa.db2;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQuery;
+
+@Entity
+@NamedQuery(name = "get_person_by_name", query = "select p from Person p where name = :name")
+public class Person {
+
+ private long id;
+ private String name;
+ private SequencedAddress address;
+ private Status status;
+
+ public Person() {
+ }
+
+ public Person(long id, String name, SequencedAddress address) {
+ this.id = id;
+ this.name = name;
+ this.address = address;
+ }
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "personSeq")
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
+ public SequencedAddress getAddress() {
+ return address;
+ }
+
+ public void setAddress(SequencedAddress address) {
+ this.address = address;
+ }
+
+ public Status getStatus() {
+ return status;
+ }
+
+ public void setStatus(Status status) {
+ this.status = status;
+ }
+
+ public void describeFully(StringBuilder sb) {
+ sb.append("Person with id=").append(id).append(", name='").append(name).append("', status='").append(status)
+ .append("', address { ");
+ getAddress().describeFully(sb);
+ sb.append(" }");
+ }
+}
diff --git a/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/SequencedAddress.java b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/SequencedAddress.java
new file mode 100644
index 0000000000000..73037d41250ae
--- /dev/null
+++ b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/SequencedAddress.java
@@ -0,0 +1,42 @@
+package io.quarkus.it.jpa.db2;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+@Entity
+public class SequencedAddress {
+
+ private long id;
+ private String street;
+
+ public SequencedAddress() {
+ }
+
+ public SequencedAddress(String street) {
+ this.street = street;
+ }
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "addressSeq")
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public String getStreet() {
+ return street;
+ }
+
+ public void setStreet(String name) {
+ this.street = name;
+ }
+
+ public void describeFully(StringBuilder sb) {
+ sb.append("Address with id=").append(id).append(", street='").append(street).append("'");
+ }
+}
diff --git a/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Status.java b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Status.java
new file mode 100644
index 0000000000000..973807a1ea2df
--- /dev/null
+++ b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/Status.java
@@ -0,0 +1,6 @@
+package io.quarkus.it.jpa.db2;
+
+public enum Status {
+ LIVING,
+ DECEASED
+}
diff --git a/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/WorkAddress.java b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/WorkAddress.java
new file mode 100644
index 0000000000000..dadea80c1a306
--- /dev/null
+++ b/integration-tests/jpa-db2/src/main/java/io/quarkus/it/jpa/db2/WorkAddress.java
@@ -0,0 +1,21 @@
+package io.quarkus.it.jpa.db2;
+
+import javax.persistence.Embeddable;
+
+/**
+ * Class marked @Embeddable explicitly so it is picked up.
+ *
+ * @author Emmanuel Bernard emmanuel@hibernate.org
+ */
+@Embeddable
+public class WorkAddress {
+ private String company;
+
+ public String getCompany() {
+ return company;
+ }
+
+ public void setCompany(String company) {
+ this.company = company;
+ }
+}
diff --git a/integration-tests/jpa-db2/src/main/resources/META-INF/persistence.xml b/integration-tests/jpa-db2/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 0000000000000..1d067ecc6ba8d
--- /dev/null
+++ b/integration-tests/jpa-db2/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ Hibernate test case template Persistence Unit
+
+
+
+
+
+
+
+
+
+
+
diff --git a/integration-tests/jpa-db2/src/main/resources/application.properties b/integration-tests/jpa-db2/src/main/resources/application.properties
new file mode 100644
index 0000000000000..fe498c6bed1ee
--- /dev/null
+++ b/integration-tests/jpa-db2/src/main/resources/application.properties
@@ -0,0 +1,4 @@
+quarkus.datasource.db-kind=db2
+quarkus.datasource.username=hreact
+quarkus.datasource.password=hreact
+quarkus.datasource.jdbc.url=${jdbc-db2.url}
\ No newline at end of file
diff --git a/integration-tests/jpa-db2/src/test/java/io/quarkus/it/jpa/db2/JPAFunctionalityInGraalITCase.java b/integration-tests/jpa-db2/src/test/java/io/quarkus/it/jpa/db2/JPAFunctionalityInGraalITCase.java
new file mode 100644
index 0000000000000..a4913879e667a
--- /dev/null
+++ b/integration-tests/jpa-db2/src/test/java/io/quarkus/it/jpa/db2/JPAFunctionalityInGraalITCase.java
@@ -0,0 +1,7 @@
+package io.quarkus.it.jpa.db2;
+
+import io.quarkus.test.junit.NativeImageTest;
+
+@NativeImageTest
+public class JPAFunctionalityInGraalITCase extends JPAFunctionalityTest {
+}
diff --git a/integration-tests/jpa-db2/src/test/java/io/quarkus/it/jpa/db2/JPAFunctionalityTest.java b/integration-tests/jpa-db2/src/test/java/io/quarkus/it/jpa/db2/JPAFunctionalityTest.java
new file mode 100644
index 0000000000000..9f9cdb68458fc
--- /dev/null
+++ b/integration-tests/jpa-db2/src/test/java/io/quarkus/it/jpa/db2/JPAFunctionalityTest.java
@@ -0,0 +1,18 @@
+package io.quarkus.it.jpa.db2;
+
+import static org.hamcrest.Matchers.is;
+
+import org.junit.jupiter.api.Test;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.RestAssured;
+
+@QuarkusTest
+public class JPAFunctionalityTest {
+
+ @Test
+ public void testJPAFunctionalityFromServlet() throws Exception {
+ RestAssured.when().get("/jpa/testfunctionality").then().body(is("OK"));
+ }
+
+}
diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml
index f0258f4b69c73..b75beeadde35b 100644
--- a/integration-tests/pom.xml
+++ b/integration-tests/pom.xml
@@ -34,6 +34,7 @@
kafka
kafka-streams
jpa
+ jpa-db2
jpa-derby
jpa-postgresql
vault