Skip to content

Implements Jakarta Persistence 3.2#144

Open
cristof wants to merge 258 commits into
masterfrom
OPENJPA-2940
Open

Implements Jakarta Persistence 3.2#144
cristof wants to merge 258 commits into
masterfrom
OPENJPA-2940

Conversation

@cristof

@cristof cristof commented May 16, 2026

Copy link
Copy Markdown
Contributor

Hi! This work is an effort to implement JPA 3.2. I've started it a long time ago. Richard Zowalla picked it up and, with AI help (Claude), implemented the missing features, including some JPA <3.0 that weren't implemented.
I've tested it against default database, h2, mariadb(lts) e postgresql (18).
Please, check it against your favorite DB so we may fix some edge cases. It would be great if you can run TCK to be sure of the implementations.

cristof added 30 commits July 8, 2025 09:13
* removed TestSecurityContext because it is terminally deprecated since 17 and already removed in current JDK versions
* updated h2-2 test profile jdbc url to remove strict definition
* updated openjpa-slice and openjpa-xmlstore pom system variables definitions
* updated GH actions workflows to use test-h2-2 profiles
* Project passes tests on derby, h2-2, postgres:latest, mysql:lts,
mariadb:lts
* Updated dependency version
* Added API new methods to API implementation classes with methods that
throw UnsupportedOperationException, except for four methods in
EntityManagerImpl that required proper implementations to pass tests
* Project is still passing tests on derby and postgresql, at least
* Added XML JPA 3.2 schema and definitions
* Added configuration support by 3.2 version
* Added SchemaManager impl and corresponding methods in BrokerFactory
interface
* Added concrete working (not for h2-2) implementation of SchemaManager
methods for JDBCBrokerFactory
* Added concrete working impl for EMF#getName()
* Reverting unnecessary changes
* Fixing broken map synchronization
* Changing signature of BrokerFactory API on schema dealing validate method
* Adding test to check if validate operation throws exception when it fails
* Changing GH CI workflow to allow usage of both self-hosted and GH hosted runners
* Tested on derby, h2-2, postgresql:latest, mysql:lts, mariadb:lts
* Implementing emf creation passing PersistenceConfiguration
* Removing unused import in BrokerImpl
* Implemented new PersistenceUnitUtil load methods
* Moved PUU loading tests to test unit already present
* Updated test unit to junit 4.x format
* Original impl was wrongfully dependent of persistence.xml file
* Fixed this issue, but still lacking actual conversion of PersistenceConfiguration
properties to openjpa corresponding properties
* added update OracleDictionary to use unqualified table name from main
* added temporary file information disclosure vul-fix from main
…tence.xml

* Added tests of specifications supported
* Added support for scope and qualifiers in PersistenceUnitInfo and persistence.xml
Added function and corresponding tests, but MySQL still fails in tests.
Passed on h2-2, pg, mariadb.
cristof added 2 commits July 16, 2026 09:17
Oracle Database does not support TIME data type, nor CURRENT_TIME or EXTRACT(QUARTER) functions, so the associated tests must be skipped.
@solomax

solomax commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Hello All,

so far testing matrix seems to be OK:

Passed -- postgresql_11
Failed -- postgresql_16
Failed -- postgresql_18
Passed -- hsqldb
Passed -- h2
Passed -- mariadb
Passed -- derby
Passed -- mssql
Passed -- mysql_8.0
Passed -- mysql_8.4.8
Passed -- mysql_9.4.0

Oracle was skipped due to I, sort of, scared of it :) (waiting for 2 weeks last time ... :))

Postgres: 16, 18 - failed for me with

[ERROR] org.apache.openjpa.persistence.lockmgr.TestPessimisticLocks.testQueryAfterFindWithPessimisticLocks -- Time elapsed: 0.158 s <<< FAILURE!
junit.framework.AssertionFailedError: Unexpected find succeeded. Should throw a PessimisticLockException.
	at junit.framework.Assert.fail(Assert.java:57)
	at junit.framework.TestCase.fail(TestCase.java:223)
	at org.apache.openjpa.persistence.lockmgr.TestPessimisticLocks.testQueryAfterFindWithPessimisticLocks(TestPessimisticLocks.java:303)
	..... more frames dropped

which is weird, but I have no idea how it can be fixed (and why is it differs from 11 ...

I'll try to run TCK

@solomax

solomax commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

@rzo1 are there any changes required to address unresolved comments from @rmannibucau ?

@rzo1

rzo1 commented Jul 16, 2026

Copy link
Copy Markdown

@rzo1 are there any changes required to address unresolved comments from @rmannibucau ?

Most likely as I wrote: Would most likely be beneficial to do the additional check for @ Version instead of swallowing (and if no OptimisticLock possible, just return 0 - but got no feedback on it so far ;-) )

@cristof

cristof commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

I ran profile test-derby with jdk17 with no issues. Checking the github logs, it seems the problem was that elusive ghostly kubernetes error again. Still running oracle tests (on oracle-xe-18 and oracle-xe-21)

@solomax

solomax commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Hello All,

here are results of my TCK run:

Postgres:11 can't be used due to procedure can't have OUT param

At Postgres:16 following errors are reported:

SEVERE: Unexpected exception occurred
<openjpa-4.2.0-SNAPSHOT-r4f1cb50M fatal general error> org.apache.openjpa.persistence.PersistenceException: class java.util.Date cannot be cast to class java.sql.Date (java.util.Date is in module java.base of loader 'bootstrap'; java.sql.Date is in module java.sql of loader 'platform')
     ..........................
     ..........................
     ..........................
Caused by: java.lang.ClassCastException: class java.util.Date cannot be cast to class java.sql.Date (java.util.Date is in module java.base of loader 'bootstrap'; java.sql.Date is in module java.sql of loader 'platform')
	at org.apache.openjpa.jdbc.sql.DBDictionary.setTyped(DBDictionary.java:1693)
	at org.apache.openjpa.jdbc.sql.PostgresDictionary.setTyped(PostgresDictionary.java:461)
	at org.apache.openjpa.jdbc.sql.RowImpl.flush(RowImpl.java:1002)




[ERROR] Tests run: 14, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 3.468 s <<< FAILURE! -- in ee.jakarta.tck.persistence.core.annotations.id.Client
[ERROR] ee.jakarta.tck.persistence.core.annotations.id.Client.FieldUtilDateIdTest -- Time elapsed: 0.222 s <<< ERROR!
java.lang.Exception: FieldUtilDateIdTest failed
	at ee.jakarta.tck.persistence.core.annotations.id.Client.FieldUtilDateIdTest(Client.java:389)
	at java.base/java.lang.reflect.Method.invoke(Method.java:569)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

[ERROR] ee.jakarta.tck.persistence.core.annotations.id.Client.PropertyUtilDateIdTest -- Time elapsed: 0.234 s <<< ERROR!
java.lang.Exception: PropertyUtilDateIdTest failed
	at ee.jakarta.tck.persistence.core.annotations.id.Client.PropertyUtilDateIdTest(Client.java:718)
	at java.base/java.lang.reflect.Method.invoke(Method.java:569)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
[INFO] --- failsafe:3.2.5:integration-test (persistence-tests-2) @ persistence-tck-runner ---
[INFO] Failsafe report directory: /home/solomax/work/_oss/openjpa/openjpa-integration/tck/target/tck32/persistence-tck/bin/target/failsafe-reports
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running ee.jakarta.tck.persistence.se.entityManagerFactory.Client2
11  WARN   [main] openjpa.Runtime - The configuration property named "openjpa.version" was not recognized and will be ignored, although the name closely matches a valid property called "openjpa.Log".
25  ERROR  [main] openjpa.Runtime - A fatal error occurred while trying to create the required validation provider.java.lang.RuntimeException: A default ValidatorFactory could not be created.
	at org.apache.openjpa.persistence.validation.ValidatorImpl.initialize(ValidatorImpl.java:129)
	at org.apache.openjpa.persistence.validation.ValidatorImpl.<init>(ValidatorImpl.java:91)
	at org.apache.openjpa.persistence.validation.ValidationUtils.setupValidation(ValidationUtils.java:99)

     ..........................
     ..........................
     ..........................

Caused by: java.lang.RuntimeException: A default ValidatorFactory could not be created.
	at org.apache.openjpa.persistence.validation.ValidatorImpl.initialize(ValidatorImpl.java:129)
	at org.apache.openjpa.persistence.validation.ValidatorImpl.<init>(ValidatorImpl.java:91)
	at org.apache.openjpa.persistence.validation.ValidationUtils.setupValidation(ValidationUtils.java:99)
	... 77 more

I'll try to investigate and improve script to work with some other DBs

@solomax

solomax commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

I've added test to easily reproduce A default ValidatorFactory could not be created. issue

I believe we need to create OpenJPA impl for ValidationProvider and specify it in META-INF/services/jakarta.validation.spi.ValidationProvider

@solomax

solomax commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

I've added validator and now got following Exception:

SEVERE: Received unexpected Exception
<openjpa-4.2.0-SNAPSHOT-ra80a08eM nonfatal user error> org.apache.openjpa.persistence.ArgumentException: This configuration disallows runtime optimization, but the following listed types were not enhanced at build time or at class load time with a javaagent: "
ee.jakarta.tck.persistence.se.entityManagerFactory.Order
ee.jakarta.tck.persistence.se.entityManagerFactory.Member".
	at org.apache.openjpa.enhance.ManagedClassSubclasser.prepareUnenhancedClasses(ManagedClassSubclasser.java:120)
	at org.apache.openjpa.kernel.AbstractBrokerFactory.loadPersistentTypes(AbstractBrokerFactory.java:313)
	at org.apache.openjpa.kernel.AbstractBrokerFactory.initializeBroker(AbstractBrokerFactory.java:239)
	at org.apache.openjpa.kernel.AbstractBrokerFactory.newBroker(AbstractBrokerFactory.java:215)
	at org.apache.openjpa.kernel.DelegatingBrokerFactory.newBroker(DelegatingBrokerFactory.java:168)
	at org.apache.openjpa.persistence.EntityManagerFactoryImpl.doCreateEM(EntityManagerFactoryImpl.java:312)

@rzo1

rzo1 commented Jul 17, 2026

Copy link
Copy Markdown

How do you run the tests exactly? They require quite a lot of properties 🙃

@solomax

solomax commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

I'm using: reset && ./run-tck32.sh :)
It starts Postgres:16 and runs everything

As I understand for some reason jpa.provider.implementation.specific.properties is not working
I'll try to debug :)

@rzo1

rzo1 commented Jul 17, 2026

Copy link
Copy Markdown

@solomax I looked into the Client2#createEntityManagerFactoryNoBeanValidatorTest issue and could reproduce both states on PostgreSQL 16. The test actually passes without the bval change: it verifies JPA spec 3.6.1 — ValidationMode.CALLBACK with no Bean Validation provider on the classpath must end in a PersistenceException, and OpenJPA already throws it (ValidationUnavailableException → wrapped in PersistenceProviderImpl). The ERROR ... A default ValidatorFactory could not be created stack trace in the log is expected output on the passing path.

That's also why the TCK runs this single test in the separate persistence-tests-2 execution: persistence-tests-1 gets hibernate-validator via ${validator.classes}, while persistence-tests-2 deliberately has no validator. Adding org.apache.bval.bundle to our profile puts a validator on both classpaths, so the EMF now gets created, em.persist() runs, and it fails on the unenhanced Order/Member classes instead — could you revert 4588093?

Re jpa.provider.implementation.specific.properties "not working": for this one test that's by TCK design — Client2 passes the raw myProps to createEntityManagerFactory and never calls PMClientBase.getPersistenceUnitProperties(), which is the only place that string gets parsed into openjpa.* properties. For all other tests it works fine.

Verified A/B/A on postgres:16 with a fresh branch-tip build, running only the persistence-tests-2 execution: without bval → Tests run: 1, Failures: 0, Errors: 0; with bval → the ArgumentException: This configuration disallows runtime optimization failure you saw; reverted → green again.

@solomax

solomax commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

You are right @rzo1, for whatever reason the test was red for me

Reverted and GREEN :)

I'll try to add parametrization and perform more tests

@rzo1

rzo1 commented Jul 17, 2026

Copy link
Copy Markdown

I am currently looking into my suggestion regarding Romains comment.

rzo1 and others added 2 commits July 17, 2026 08:41
…thout a version, so optimistic lock failures still surface

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@solomax

solomax commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Just commited updated run-tck32.sh
It is now able to run TCK against MySql with DB_TYPE=mysql ./run-tck32.sh (version can also be specified

My results are:
[ERROR] Tests run: 2134, Failures: 0, Errors: 145, Skipped: 4

I'll try to take a look at all this later :)

@cristof

cristof commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Kubernetes flukes aside, Oracle build is now green, skipping only tests where there is no database support (TIME data type, CURRENT_TIME and EXTRACT(QUARTER from)). Ran the tests on oracle-xe-18 and oracle-xe-21.
Provided the TCK tests are ok, I think we could update the docs and (pre?) release?

@solomax

solomax commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

It seems some errors in MySQL TCK tests are caused by:

Caused by: org.apache.openjpa.lib.jdbc.ReportingSQLException: Data truncation: Data too long for column 'CHARDATA' at row 1 {prepstmnt 2112012700 INSERT INTO DATATYPES (id, BOOLEANDATA, BYTEARRAYDATA, BYTEDATA, CHARARRAYDATA, CHARDATA, DBLDATA, ENUMSDATA, FLOATDATA, INTDATA, INTDATA2, LONGDATA, SHORTDATA) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)} [code=1406, state=22001]
	at org.apache.openjpa.lib.jdbc.LoggingConnectionDecorator.wrap(LoggingConnectionDecorator.java:219)
	at org.apache.openjpa.lib.jdbc.LoggingConnectionDecorator.wrap(LoggingConnectionDecorator.java:195)
	at org.apache.openjpa.lib.jdbc.LoggingConnectionDecorator$LoggingConnection$LoggingPreparedStatement.executeUpdate(LoggingConnectionDecorator.java:1188)
	at org.apache.openjpa.lib.jdbc.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:308)
	at org.apache.openjpa.jdbc.kernel.JDBCStoreManager$CancelPreparedStatement.executeUpdate(JDBCStoreManager.java:1867)

While trying to persist entity with:

	@Column(name = "CHARDATA")
	protected char characterData;

Table is create with:

CREATE TABLE DATATYPES (ID INTEGER NOT NULL, BOOLEANDATA SMALLINT NULL, CHARDATA CHAR NULL, SHORTDATA SMALLINT NULL, INTDATA INTEGER NULL, INTDATA2 INTEGER NULL, LONGDATA NUMERIC NULL, DBLDATA DOUBLE PRECISION NULL, FLOATDATA REAL NULL, ENUMODATA INTEGER NULL, ENUMSDATA VARCHAR(25), BYTEDATA SMALLINT NULL, TRANS SMALLINT NULL, BYTEARRAYDATA BLOB, CHARARRAYDATA VARCHAR(448), CONSTRAINT PK_DATATYPES PRIMARY KEY (ID) ) ENGINE=innoDB DEFAULT CHARSET=latin1 !

java oblect is created with:

			char[] cArray = { 'a' };
			byte[] bArray = { (byte) 100 };
			d1 = new DataTypes(1, false, (byte) 100, 'a', (short) 100, 300, 600L, 50D, 1.0F, cArray, bArray);

Something weird :(((

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants