jakartaee / jakartaee/persistence
Schema generation on an already-deployed persistence unit: is the 4.0 TCK behaviour intended?
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 268
- Forks
- 78
- Avg merge
- 1d 6h
- Merged PRs (30d)
- 13
Description
A difference between Hibernate and EclipseLink for Persistence 3.2 was that EclipseLink silently ignored the
schema generation properties when creating an entity manager factory associated with an already open
persistence unit, while Hibernate acted on them (since every entity manager factory is fresh, and there's no
caching involved). EclipseLink doesn't reject the request or warn about it; the properties simply have no
effect.
The specification describes schema generation as something performed per createEntityManagerFactory /
Persistence.generateSchema invocation. What it doesn't appear to say anything about is an implementation that shares one
deployed persistence unit across several factories. It neither demands nor forbids EclipseLink's behaviour.
In 4.0 this implicitly changed because of a TCK refactoring. E.g. in
ee.jakarta.tck.persistence.se.schemaGeneration.annotations.discriminatorColumn.Client the private
removeTestData method was removed:
private void removeTestData() {
logger.log(Logger.Level.TRACE, "removeTestData");
if (getEntityTransaction().isActive()) {
getEntityTransaction().rollback();
}
try {
getEntityTransaction().begin();
logger.log(Logger.Level.INFO, "Try to drop table SCHEMAGENSIMPLE");
getEntityManager().createNativeQuery("DROP TABLE SCHEMAGENSIMPLE").executeUpdate();
getEntityTransaction().commit();
} catch (Throwable t) {
logger.log(Logger.Level.INFO,
"AN EXCEPTION WAS THROWN DURING DROP TABLE SCHEMAGENSIMPLE, IT MAY OR MAY NOT BE A PROBLEM, "
+ t.getMessage());
} finally {
try {
if (getEntityTransaction().isActive()) {
getEntityTransaction().rollback();
}
clearEntityTransaction();
// ensure that we close the EM and EMF before proceeding.
clearEMAndEMF();
} catch (Exception re) {
logger.log(Logger.Level.ERROR, "Unexpected Exception in removeTestData:", re);
}
}
}
This was called at the end of setup(), so it ran in @BeforeEach, before every
test method. It eventually called closeEMAndEMF:
public void closeEMAndEMF() throws Exception {
try {
logger.log(Logger.Level.TRACE,
"Rolling back any existing transaction before closing EMF and EM if one exists.");
if (getEntityTransaction(false) != null && getEntityTransaction(false).isActive()) {
logger.log(Logger.Level.TRACE, "An active transaction was found, rolling it back.");
getEntityTransaction(false).rollback();
}
} catch (Exception fe) {
logger.log(Logger.Level.INFO, "Unexpected exception rolling back TX:", fe);
}
clearCache();
if (isStandAloneMode()) {
logger.log(Logger.Level.TRACE, "Closing EM and EMF");
if (getEntityManager(false) != null && getEntityManager(false).isOpen()) {
getEntityManager(false).close();
}
if (getEntityManagerFactory() != null && getEntityManagerFactory().isOpen()) {
getEntityManagerFactory().close();
}
}
}
This closed both the entity manager and the entity manager factory before each test, so for
EclipseLink the persistence unit was undeployed and all caches were cleared. The first thing the test
body does is create a factory with the script generation properties, and because that was a fresh
deployment EclipseLink performed the schema generation. This particular test, which depends on the schema
script being processed, therefore passed.
In 4.0 the private method is gone, so setup() now binds to the inherited PMClientBase.removeTestData(),
which obtains the factory and calls getSchemaManager().truncate() but never closes it.
There is a second change. PMClientBase.cleanup() used to be a plain method called per test from each test's
own @AfterEach.
It is now@AfterEach public final void cleanup() in the base class, and it closes only the entity manager. The factory
is kept open and truncated. emf.close() is deferred to @AfterAll cleanupAll(). So the 4.0 TCK
keeps one entity manager factory alive for a whole test class, where 3.2 tore it down for every test.
The same short-circuit also affects Persistence.generateSchema(), which I think is a good example,
because that method exists for no other purpose. Its own 4.0 javadoc says:
Called when schema generation is to occur as a separate phase from creation of the entity manager factory.
@throws PersistenceExceptionif insufficient or inconsistent configuration information is provided or if
schema generation otherwise fails.
Against an already-deployed unit EclipseLink neither generates nor throws.
EclipseLink 6.0.0-SNAPSHOT now fails 26 tests on this: the 25 failing tests in se.schemaGeneration.*, plus
jpa22.se.generators.sequencegenerators.Client.
Hibernate has no equivalent code path. I Checked against 7.0.5.Final, SessionFactoryObserverForSchemaExport.sessionFactoryCreated() calls SchemaManagementToolCoordinator.process() unconditionally for every SessionFactory, and process() performs
the script action and the database action independently. There's no notion of a "first deployment" anywhere.
It's fine, and I have already adjusted EclipseLink to match the Hibernate behaviour; that fixes all 26 tests
But it is a non-trivial behaviour change for EclipseLink without any specification backing.
Because the database action now runs for every factory, an existing application that has drop-and-create in persistence.xml and opens a second factory will drop its schema. Hibernate users have always had this, so nobody is surprised there, but for EclipseLink it's new behaviour as mentioned above.
So perhaps the specification should say something about this case, marked "new for 4.0"? E.g. something like:
"schema generation is performed for every createEntityManagerFactory / Persistence.generateSchema invocation, regardless of whether the persistence unit is already in use"?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Read the cited TCK class ee.jakarta.tck.persistence.se.schemaGeneration.annotations.discriminatorColumn.Client and the inherited PMClientBase cleanup methods first. Compare their lifecycle behavior with the Persistence.generateSchema() javadoc and the existing specification text. Done means the specification clearly resolves schema generation for already-deployed persistence units, with corresponding TCK behavior agreed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- documentation
- Issue type
- Documentation
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100