Reputation: 1289
I have a web application in Spring Boot 3.1.4
that uses Hibernate and JDBC-Backed Session persistence, I have a custom JPA Transaction manager that I'm hoping I can use to inject custom connection variables into my JDBC Connection session each time a transaction is initiated ( I do not need this to be done for any of the JDBC connections used for session persistence though ). It's defined as such:
public class CustomJPATransactionManager extends JpaTransactionManager {
private static final Logger logger = LoggerFactory.getLogger(CustomJPATransactionManager.class);
@Autowired
private EntityManager entityManager;
@Override
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
super.prepareSynchronization(status, definition);
if (status.isNewTransaction()) {
logger.trace("start of new JPA Transaction");
// current transaction is suspended and a new one opened right as the security context is accessed
User user = SecurityContextHolder.getContext().getAuthentication().getPrincipal()
if (user != null) {
Session session = entityManager.unwrap(Session.class);
String userId = user.getId() != 0 ? String.valueOf(user.getId()) : "-2";
logger.trace(String.format("userId: %s", userId));
session.doWork(connection -> {
try (Statement statement = connection.createStatement()) {
statement.execute(String.format("SELECT set_config('var.context-id', '%s', %s)", userId,"true"));
}
});
}
}
}
}
The issue I'm encountering is that the access to the Security Context is done through the JDBC store which participates in the transaction management as everything else, and as such, when I attempt to access the security context, spring pauses the current transaction and initiates a new one:
CustomJPATransactionManager : Found thread-bound EntityManager [SessionImpl(354629356PersistenceContext[entityKeys=[], collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction
CustomJPATransactionManager : Suspending current transaction, creating new transaction with name [null]
This, of course, initiates an infinite recursion of the current transaction being suspended and a new one opened (the same Transaction manager is called in recursion), and after a while, they time out.
Are there means of alleviating this behavior? Can I have the spring session access not utilize the same JpaTransactionManager as our "general business logic" EntityManager?
Alternatively - I was thinking of caching the session information in a Session scoped or Request scoped variable that this Jpa Transaction manager could be autowired with, would that conceivably work? Would it need to be a ThreadLocal defined variable?
Thank you for your time.
Upvotes: 0
Views: 192
Reputation: 1
Might be late, but ran into a similar issue recently where my application had 3 transaction managers for 3 different datasources.
Spring Session requires a PlatformTransactionManager
bean to be in the context when it is configuring itself. In your case, it seems like Spring Session is using your custom CustomJPATransactionManager
for handling its own transaction on the JDBC backed Spring Session tables.
What you could do, is create a new TransactionManager bean for the sole purpose of Spring Session using it. Something like so:
@Bean
@Primary
public PlatformTransactionManager springSessionTransactionmanager(DataSource datasource) {
return new DataSourceTransactionManager(datasource);
}
It will have to be annotated with @Primary
annotation for Spring Session to pick it up. Without the @Primary
Spring Session will not know which Transactionmanager to autowire in.
References:
Here is a link to the JdbcHttpSessionConfiguration.java
class that Spring Session uses to set up everything. You can see the section where Spring Session (JDBC) is trying to autowire in a PlatformTransactionManager:
@Autowired
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
Upvotes: 0