Reputation: 44755
I'm using Spring boot and I defined the spring.datasource.*
properties to enable my datasource. If I only use this it works fine. However, I'm now trying to add JMS to my application as well, using the following config:
@Configuration
@EnableJms
public class TriggerQueueConfig {
private Logger logger = LoggerFactory.getLogger(getClass());
@Value("${jms.host:localhost}")
private String host;
@Value("${jms.port:1414}")
private int port;
@Value("${jms.concurrency.min:3}-${jms.concurrency.max:10}")
private String concurrency;
@Value("${jms.manager}")
private String queueManager;
@Value("${jms.cache:100}")
private int cacheSize;
@Bean
public JmsListenerContainerFactory<?> jmsListenerContainerFactory() throws JMSException {
logger.debug("Setting queue concurrency to {} (min-max)", concurrency);
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(cachedConnectionFactory());
factory.setMessageConverter(messageConverter());
factory.setTransactionManager(transactionManager());
factory.setSessionTransacted(true);
factory.setConcurrency(concurrency);
return factory;
}
@Bean(name = "jmsTransactionManager")
public JmsTransactionManager transactionManager() throws JMSException {
JmsTransactionManager transactionManager = new JmsTransactionManager();
transactionManager.setConnectionFactory(cachedConnectionFactory());
return transactionManager;
}
@Bean
@Primary
public ConnectionFactory cachedConnectionFactory() throws JMSException {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(ibmConnectionFactory());
connectionFactory.setSessionCacheSize(cacheSize);
connectionFactory.setCacheConsumers(true);
return connectionFactory;
}
@Bean
public ConnectionFactory ibmConnectionFactory() throws JMSException {
logger.debug("Connecting to queue on {}:{}", host, port);
MQQueueConnectionFactory connectionFactory = new MQQueueConnectionFactory();
connectionFactory.setHostName(host);
connectionFactory.setPort(port);
connectionFactory.setQueueManager(queueManager);
connectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
return connectionFactory;
}
@Bean
public MessageConverter messageConverter() {
MarshallingMessageConverter converter = new MarshallingMessageConverter();
converter.setMarshaller(marshaller());
converter.setUnmarshaller(marshaller());
return converter;
}
@Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan("com.example");
return marshaller;
}
}
The JMS listener I created is working fine. However, when I'm trying to persist data using my repository (Spring Data JPA) in a @Transactional
method, I'm getting the following exception:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected single matching bean but found 2: transactionManager,jmsTransactionManager
This makes sense, because both transactionmanagers are PlatformTransactionManager
's. Usually you would put @Primary
on top of the bean that should be the default one. However, in this case I'm using Spring boot's autoconfiguration so I can't add the @Primary
on it.
An alternative solution would be to provide the name of the transaction manager with each @Transactional
annotation (for example @Transactional("transactionManager")
, but this would be a lot of work, and it would make more sense to have a default transactionmanager because the JMS transactionmanager is an exceptional case.
Is there an easy way to define the automatically configured transactionmanager to be used by default?
Upvotes: 5
Views: 4906
Reputation: 15086
The Spring boot 'magic' is really only this:
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
in org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration
class.
Notice the @ConditionalOnMissingBean
annotation - this will get configured only if a bean of type PlatformTransactionManager
doesn't exist. So you can override this by creating your own bean with @Primary
annotation:
@Bean
@Primary
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
Upvotes: 6