Skip to content

Context management order in DefaultCacheAwareContextLoaderDelegate interferes with state of newly created context #35396

@novitzkee

Description

@novitzkee

Hi, I encountered weird context caching behavior, which I would consider to be a bug.

When running tests with JUnit 5 using SpringExtension.class and the cache size limit defined by spring.test.context.cache.maxSize is reached, tests begin to fail despite having seemingly valid configurations.

I think that's because DefaultCacheAwareContextLoaderDelegate closes context that is evicted from cache after new context is created and cached, interfering with resources that new context creates.

Repository for issue reproduction (Spring 6.2.10, 5.3.39 not OSS)
In the repository issue is illustrated using Spring's LocalSessionFactoryBean configured with hibernate.hbm2ddl.auto=create-drop

It can be seen in logs that context 7a2a7492 is closed after 4fb42efa is created, effectively dropping all database objects that test needs to run.

[INFO] --- maven-surefire-plugin:3.5.3:test (default-test) @ spring-context-cache-reproduction ---
[WARNING] useSystemClassLoader setting has no effect when not forking
[WARNING] The parameter forkCount should likely not be 0. Forking a JVM for tests improves test accuracy. Ensure to have a <forkCount> >= 1.
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO] Running org.novitzkee.PersonPersistence1Test
14:12:16.970 [main] DEBUG o.s.c.s.GenericApplicationContext -- Refreshing org.springframework.context.support.GenericApplicationContext@7a2a7492
14:12:16.970 [main] DEBUG o.s.c.s.GenericApplicationContext -- Refreshing org.springframework.context.support.GenericApplicationContext@7a2a7492
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.807 s -- in org.novitzkee.PersonPersistence1Test
[INFO] Running org.novitzkee.PersonPersistence2Test
14:12:18.440 [main] DEBUG o.s.c.s.GenericApplicationContext -- Refreshing org.springframework.context.support.GenericApplicationContext@5fb0a09e
14:12:18.440 [main] DEBUG o.s.c.s.GenericApplicationContext -- Refreshing org.springframework.context.support.GenericApplicationContext@5fb0a09e
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.052 s -- in org.novitzkee.PersonPersistence2Test
[INFO] Running org.novitzkee.PersonPersistence3Test
14:12:18.500 [main] DEBUG o.s.c.s.GenericApplicationContext -- Refreshing org.springframework.context.support.GenericApplicationContext@4fb42efa
14:12:18.500 [main] DEBUG o.s.c.s.GenericApplicationContext -- Refreshing org.springframework.context.support.GenericApplicationContext@4fb42efa
14:12:18.547 [main] DEBUG o.s.c.s.GenericApplicationContext -- Closing org.springframework.context.support.GenericApplicationContext@7a2a7492, started on Sun Aug 31 14:12:16 CEST 2025
14:12:18.547 [main] DEBUG o.s.c.s.GenericApplicationContext -- Closing org.springframework.context.support.GenericApplicationContext@7a2a7492, started on Sun Aug 31 14:12:16 CEST 2025
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.065 s <<< FAILURE! -- in org.novitzkee.PersonPersistence3Test
[ERROR] org.novitzkee.PersonPersistence3Test.testSavePerson3 -- Time elapsed: 0.008 s <<< ERROR!
org.hibernate.exception.SQLGrammarException: could not prepare statement
        at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:63)
        at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:37)
        at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
        at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:186)
        at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareStatement(StatementPreparerImpl.java:75)
        at org.hibernate.id.enhanced.SequenceStructure$1.getNextValue(SequenceStructure.java:105)
        at org.hibernate.id.enhanced.NoopOptimizer.generate(NoopOptimizer.java:40)
        at org.hibernate.id.enhanced.SequenceStyleGenerator.generate(SequenceStyleGenerator.java:534)
        at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:114)
        at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:194)
        at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:38)
        at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:179)
        at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:32)
        at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:75)
        at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107)
        at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:672)
        at org.hibernate.internal.SessionImpl.save(SessionImpl.java:665)
        at org.hibernate.internal.SessionImpl.save(SessionImpl.java:660)
        at org.novitzkee.PersonPersistence3Test.testSavePerson3(PersonPersistence3Test.java:27)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Sequence "HIBERNATE_SEQUENCE" not found; SQL statement:
call next value for hibernate_sequence [90036-232]
        at org.h2.message.DbException.getJdbcSQLException(DbException.java:644)
        at org.h2.message.DbException.getJdbcSQLException(DbException.java:489)
        at org.h2.message.DbException.get(DbException.java:223)
        at org.h2.message.DbException.get(DbException.java:199)
        at org.h2.command.Parser.readSequence(Parser.java:8018)
        at org.h2.command.Parser.readTermWithIdentifier(Parser.java:5278)
        at org.h2.command.Parser.readTermWithIdentifier(Parser.java:5148)
        at org.h2.command.Parser.readTerm(Parser.java:4828)
        at org.h2.command.Parser.readFactor(Parser.java:3323)
        at org.h2.command.Parser.readSum(Parser.java:3310)
        at org.h2.command.Parser.readConcat(Parser.java:3275)
        at org.h2.command.Parser.readCondition(Parser.java:3065)
        at org.h2.command.Parser.readExpression(Parser.java:2985)
        at org.h2.command.Parser.parseCall(Parser.java:6611)
        at org.h2.command.Parser.parsePrepared(Parser.java:647)
        at org.h2.command.Parser.parse(Parser.java:581)
        at org.h2.command.Parser.parse(Parser.java:556)
        at org.h2.command.Parser.prepareCommand(Parser.java:484)
        at org.h2.engine.SessionLocal.prepareLocal(SessionLocal.java:645)
        at org.h2.engine.SessionLocal.prepareCommand(SessionLocal.java:561)
        at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1164)
        at org.h2.jdbc.JdbcPreparedStatement.<init>(JdbcPreparedStatement.java:93)
        at org.h2.jdbc.JdbcConnection.prepareStatement(JdbcConnection.java:315)
        at org.apache.commons.dbcp.DelegatingConnection.prepareStatement(DelegatingConnection.java:281)
        at org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.prepareStatement(PoolingDataSource.java:313)
        at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$1.doPrepare(StatementPreparerImpl.java:90)
        at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:176)
        ... 18 more

In this case the resource that both created and destroyed context point to is H2 database, but I think issue is valid for any resource that might live longer than test application context.

Wouldn't it make more sense to close context that is evicted from cache before new one is created ?

Metadata

Metadata

Assignees

Labels

in: testIssues in the test modulestatus: duplicateA duplicate of another issue

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions