Spring default transactionManager not found when using JmsTransactionManager - java

I have a Spring Boot app, where I use JMS with Database. I'm trying to configure JmsTransactionManager to use with default TransactionManager (for JPA). I defined the bean in the #SpringBootApplication file (that means it has #Configuration and #EnableTransactionManagement):
#Bean(name="jmsTransactionManager")
public JmsTransactionManager jmsTransactionManager(ConnectionFactory connectionFactory) {
JmsTransactionManager jmsTransactionManager = new JmsTransactionManager();
jmsTransactionManager.setConnectionFactory(connectionFactory);
return jmsTransactionManager;
}
That's the only bean I configure by myself for JMS because other configuartion spring-boot does automatically, I just have properties in the application.yaml so I assume connectionFactory will be autowired. And I want it to use like this:
#Transactional(transactionManager = "jmsTransactionManager", propagation = Propagation.REQUIRES_NEW)
void do(){
sendJms();
saveDb();
}
#Transactional // uses default JPA TM
void sendDb(){
...
}
So the logic is that I will send to Jms first, then save something to DB, so I need two separate transactions but I want to close the DB transaction before JMS transaction. Maybe it's not correct to make calls like this in such situation, but I don't know how to do it else using declarative transaction management. And the problem is that when I'm defining JmsTransactionManagement the default one, that works with DB stops working, but without a JmsTransactionManagement transactions to db work:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'transactionManager' available: No matching TransactionManager bean found for qualifier 'transactionManager' - neither qualifier
match nor bean name match!
Am I missing something? I have spring-data-jpa in pom so default transactionManager configures by spring boot, but it can't find it, why? Unfortunately didn't find the answer on how to do something like that on the StackOverflow.

I am presuming that you are not using two phase commit (XA) transactions. Essentially in order to chain transactions between multiple transactional resources, both your jms ConnectionFactory and db Datasource have to be an XA resource implementation, and you have to use proper JTA TransactionManager. While it is not particularly hard thing to do, JTA is usually skipped by majority of java programmers as in normal historical situation (spring code deployed in JEE serer) JTA "just works" in background never to be directly accessed by java programmer. In standalone boot application you have to explicitly enable this functionality by providing proper JTA transaction manager and using XA implementation of your resources.
See: https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/boot-features-jta.html
In short JMSTransactionManager and DBTrasnactionManager wont do as you need instance of JTATransactionManager.

Related

How to set Spring Data Cassandra keyspace dynamically?

We're using Spring Boot 1.5.10 with Spring Data for Apache Cassandra and that's all working well.
We've had a new requirement coming along where we need to connect to a different keyspace while the service is up and running.
Through the use of Spring Cloud Config Server, we can easily set the value of spring.data.cassandra.keyspace-name, however, we're not certain if there's a way that we can dynamically switch (force) the service to use this new keyspace without having to restart if first?
Any ideas or suggestions?
Using #RefreshScope with properties/repositories doesn't work as the keyspace is bound to the Cassandra Session bean.
Using Spring Data Cassandra 1.5 with Spring Boot 1.5 you have at least two options:
Declare a #RefreshScope CassandraSessionFactoryBean, see also CassandraDataAutoConfiguration. This will interrupt all Cassandra operations upon refresh and re-create all dependant beans.
Listen to RefreshScopeRefreshedEvent and change the keyspace via USE my-new-keyspace;. This approach is less invasive and doesn't interrupt running queries. You'd basically use an event listener.
#Component
class MySessionRefresh {
private final Session session;
private final Environment environment;
// omitted constructors for brevity
#EventListener
#Order(Ordered.LOWEST_PRECEDENCE)
public void handle(RefreshScopeRefreshedEvent event) {
String keyspace = environment.getProperty("spring.data.cassandra.keyspace-name");
session.execute("USE " + keyspace + ";");
}
}
With Spring Data Cassandra 2, we introduced the SessionFactory abstraction providing AbstractRoutingSessionFactory for code-controlled routing of CQL/session calls.
Yes, you can use the #RefreshScope annotation on a the bean(s) holding the spring.data.cassandra.keyspace-name value.
After changing the config value through Spring Cloud Config Server, you have to issue a POST on the /refresh endpoint of your application.
From the Spring cloud documentation:
A Spring #Bean that is marked as #RefreshScope will get special treatment when there is a configuration change. This addresses the problem of stateful beans that only get their configuration injected when they are initialized. For instance if a DataSource has open connections when the database URL is changed via the Environment, we probably want the holders of those connections to be able to complete what they are doing. Then the next time someone borrows a connection from the pool he gets one with the new URL.
From the RefreshScope class javadoc:
A Scope implementation that allows for beans to be refreshed dynamically at runtime (see refresh(String) and refreshAll()). If a bean is refreshed then the next time the bean is accessed (i.e. a method is executed) a new instance is created. All lifecycle methods are applied to the bean instances, so any destruction callbacks that were registered in the bean factory are called when it is refreshed, and then the initialization callbacks are invoked as normal when the new instance is created. A new bean instance is created from the original bean definition, so any externalized content (property placeholders or expressions in string literals) is re-evaluated when it is created.

Spring Boot with session-based data source

I've been tearing my hair out with what should be a pretty common use case for a web application. I have a Spring-Boot application which uses REST Repositories, JPA, etc. The problem is that I have two data sources:
Embedded H2 data source containing user authentication information
MySQL data source for actual data which is specific to the authenticated user
Because the second data source is specific to the authenticated user, I'm attempting to use AbstractRoutingDataSource to route to the correct data source according to Principal user after authentication.
What's absolutely driving me crazy is that Spring-Boot is fighting me tooth and nail to instantiate this data source at startup. I've tried everything I can think of, including Lazy and Scope annotations. If I use Session scope, the application throws an error about no session existing at startup. #Lazy doesn't appear to help at all. No matter what annotations I use, the database is instantiated at startup by Spring Boot and doesn't find any lookup key which essentially crashes the entire application.
The other problem is that the Rest Repository API has IMO a terrible means of specifying the actual data source to be used. If you have multiple data sources with Spring Boot, you have to juggle Qualifier annotations which is a runtime debugging nightmare.
Any advice would be very much appreciated.
Your problem is with the authentication manager configuration. All the samples and guides set this up in a GlobalAuthenticationConfigurerAdapter, e.g. it would look like this as an inner class of your SimpleEmbeddedSecurityConfiguration:
#Configuration
public static class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter
{
#Bean(name = Global.AUTHENTICATION_DATA_QUALIFIER + "DataSource")
public DataSource dataSource()
{
return new EmbeddedDatabaseBuilder().setName("authdb").setType(EmbeddedDatabaseType.H2).addScripts("security/schema.sql", "security/data.sql").build();
}
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception
{
auth.jdbcAuthentication().dataSource(dataSource()).passwordEncoder(passwordEncoder());
}
}
If you don't use GlobalAuthenticationConfigurerAdapter then the DataSource gets picked up by Spring Data REST during the creation of the Security filters (before the #Primary DataSource bean has even been registered) and the whole JPA initialization starts super early (bad idea).
UPDATE: the authentication manager is not the only problem. If you need to have a session-scoped #Primary DataSource (pretty unusual I'd say), you need to switch off everything that wants access to the database on startup (Hibernate and Spring Boot in various places). Example:
spring.datasource.initialize: false
spring.jpa.hibernate.ddlAuto: none
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults: false
spring.jpa.properties.hibernate.dialect: H2
FURTHER UPDATE: if you're using the Actuator it also wants to use the primary data source on startup for a health indicator. You can override that by prividing a bean of the same type, e.g.
#Bean
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#Lazy
public DataSourcePublicMetrics dataSourcePublicMetrics() {
return new DataSourcePublicMetrics();
}
P.S. I believe the GlobalAuthenticationConfigurerAdapter might not be necessary in Spring Boot 1.2.2, but it is in 1.2.1 or 1.1.10.

TransactionManager from Spring or JNDI? (JBOSS + Spring 3 + Hibernate 4 + JTA)

We are using Spring with Hibernate to establish transactions with JTA. The PlatformTransactionManager is the JtaTransactionManager which is wired with the TransactionManager and UserTransaction from narayana.
#Bean
#Scope("prototype")
public TransactionManager jbossTransactionManager() {
return jtaPropertyManager.getJTAEnvironmentBean().getTransactionManager();
}
#Bean
#Scope("prototype")
public UserTransaction jbossUserTransaction() {
return jtaPropertyManager.getJTAEnvironmentBean().getUserTransaction();
}
#Bean
public PlatformTransactionManager transactionManager() {
return new JtaTransactionManager(jbossUserTransaction(), jbossTransactionManager());
}
I have noted that JtaTransactionManager has the UT and TM I would want. On JBoss 6 EAP, I noted that my DataSource has been used as a WrapperDataSource and that this was related to a different TM. Specifically, it is using the TransactionManagerDelegate. This appears to be the transaction manager provided by JBoss via the JNDI names java:TransactionManager and java:jboss/TransactionManager. This is preventing my transactions from having transactional boundaries and I leak data on flush. If I remove my configuration and the UT and TM from the container, my transactions transact properly.
What is deciding to use this other TransactionManager? This appears to be the JCA from the container but I do not understand the mechanism for this decision.
Should I remove my UT and TM and surrender control to the
container to give these components to my app and rely on the JTA
platform as is or should I try to gain more control?
the container provides the datasource with a transaction manager from the JCA. This TransactionManager is a different instance than the one we had wired in from Spring. (Our bean had been instantiated from the arjuna environment bean). Using the JtaManager from Spring to get the transaction manager, via JNDI in the default locations, from the container ensured that we have the same transaction manager in the JTA platform used by Hibernate (JBoss App Server in this case).
Before we made this change, the application TransactionManager was in a transaction with Hibernate but the transactionManager on the datasource was not participating which caused the "leak".
Using the same instance has everything working together. This has also been proven out on WebLogic using the same approach.

Execute initialization code in EJB3.1

I am currently migrating to EJB3.1 after using Spring for many years. One thing I would like to implement in EJB, for which I couldn't find a matching pattern yet is my MigrationManager.
In Spring I had a bean that dealt with database schema and data migration. For this I implemented a Spring BeanFactoryPostProcessor because this way I had the database connection injected, but the JPA system is not yet initialized. So I could perform all migration steps and then have the application finishing starting.
How can I do something like this in EJB3.1 (Using CDI ... if this is of importance)
Chris
This is the way to run some initialization code from an EJB:
#Singleton
#Startup
public class MigrationManager {
#PostConstruct
public void migrate() {
// do work
}
}
You don't need a separate app for that (as suggested in a comment above).
EntityManagers get instantiated lazily, so as long as you don't inject an EntityManager into some other startup code, this should give you a chance to update your database schema before an EntityManager is actually hitting the database.
By the way, for database schema migration I'd recommend Liquibase, which can be triggered by a ServletContextListener.

Seeking a Spring (3.0.5) Solution for: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here

I have a Transaction problem on Spring 3.0.5. In my case I get the so-called exception "No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here"... I have tried everything so far. I can see in my log that the transactional services are detected and registered as Spring beans and I can also see in the logs that they are proxied and associated with underlying Spring transaction interceptors. (Advise) When I run my Spring MVC app my controller will call the service...... :-( but the transaction interceptors are not triggered. (??) I expect my Spring service proxy to open a session and start a transaction before calling my target service method. Since this does not happen, I get the above exception. I have been almost two days on this problem. Tried everything which I found on the internet...but in vain.
I have layered architecture: presentation (springmvc), service (transaction annotated), dataacess (Spring/Hibernate classic sessionfactory). My model objects are annotated with jpa (javax.persistence.*). My spring context config files are separated in appContext.xml, appContext-service.xml and appContext-dao.xml. I have defined my Spring LocalSessionFactoryBean, Datasource and TransactionManager (HibernateTransactionManager) in appContext-dao.xml. I have put in appContext-service.xml where my service implementations resides. In all of my config files I have included and to detect my beans through Controller, Service and Repository annotations.
I appreciate any kind of help.
It sounds like you are doing everything correctly and you know what you are doing. There's not much we can do here unless you show some configuration.
What I'd suggest is some debugging.
First: do you have Unit tests in the service layer that test the queries you are using? Perhaps you can find the error in the service layer.
Then: debug the MVC app, check the types of the injected services. Verify that they are proxies, not the original types.
If they are the original types, you
have an error in your transaction
configuration .
If they are proxies, step through the
query methods and verify that the
transaction logic is applied.
This sounds like accessing a lazily-loaded list or set of you dao after the closing of the transaction. This typically happens if you access that list in the view in stead of the controller, as your controller probably calls methods in transaction scope, and then leaves the transaction and forwards the loaded bean to the view.
Simple solutions:
Configure your data bean to eagerly load
Force loading of the dependencies in the controller (just loop through them)
have a look at this article ans possibly also quite a few right here on SO on lazy loading / lazy fetching of one-to-many associations and the like
Imagine:
// your dao
public class Foo {
// lots of other properties
List<Something> stuff;
// getters and setter for stuff
}
// repository
#Transactional
public class FooRepo {
public Foo loadFoo(int id) {
...
}
}
// Controller
public class Controller {
public void Control() {
sessionContext.set("foo", repo.loadFoo(1)); // transaction managed by spring
}
public void setFooRepo(FooRepo repo) {this.repo = repo};
}
// View
for (Something something : foo.getStuff()) {
// Ooops transaction is closed! stuff is lazily loaded so NOT AVAILABLE
// practical solutions:
// - do not load lazily (Foo.hbm.xml)
// - or force loading by getting all Somethings in the controller
}

Categories