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 PersistenceException if 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"?
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.generateSchemainvocation. What it doesn't appear to say anything about is an implementation that shares onedeployed 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.Clientthe privateremoveTestDatamethod was removed:This was called at the end of
setup(), so it ran in@BeforeEach, before everytest method. It eventually called
closeEMAndEMF: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 inheritedPMClientBase.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'sown
@AfterEach.It is now
@AfterEach public final void cleanup()in the base class, and it closes only the entity manager. The factoryis kept open and truncated.
emf.close()is deferred to@AfterAll cleanupAll(). So the 4.0 TCKkeeps 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:
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.*, plusjpa22.se.generators.sequencegenerators.Client.Hibernate has no equivalent code path. I Checked against 7.0.5.Final,
SessionFactoryObserverForSchemaExport.sessionFactoryCreated()callsSchemaManagementToolCoordinator.process()unconditionally for every SessionFactory, andprocess()performsthe 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-createinpersistence.xmland 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.generateSchemainvocation, regardless of whether the persistence unit is already in use"?