Spring Boot Rabbit MQ Could Not autowire ConnectionFactory - java

Am developing a simple message broker with rabbitmq
Below is the Error am getting of "No Beans of 'Connectionfactory' type found"
My Code is as below with all the correct importations.
Code
package com.producer.demo;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class MQConfig {
public static final String message_queue = "message_queue";
#Bean
public Queue queue(){
return new Queue(message_queue);
}
#Bean
public TopicExchange exchange(){
return new TopicExchange("message_exchange");
}
#Bean
public Binding binding(Queue queue, TopicExchange topicExchange){
return BindingBuilder.bind(queue)
.to(topicExchange)
.with("routing_key");
}
#Bean
public MessageConverter messageConverter(){
return new Jackson2JsonMessageConverter();
}
#Bean
public AmqpTemplate template(ConnectionFactory connectionFactory){
RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setMessageConverter(messageConverter());
return template;
}
}

By default Spring boot manages the Connection, Channel and ConnectionFactory for you. You can specify properties in your application.properties file
spring.rabbitmq.host= 127.0.0.1
spring.rabbitmq.port= 5672
spring.rabbitmq.username= guest
spring.rabbitmq.password= guest
The second option is that you can define the required bean in your MQConfig configuration file
The below code is an example of the same. I don’t advise you to customize unless you need to.
...
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
return connectionFactory;
}

Related

HttpInvokerServiceExporter is deprecated in spring 5. Is there any replacement? Will it be available in spring 6?

HttpInvokerServiceExporter is deprecated in spring 5. I want to call spring remote bean via HTTP protocol. Is there any replacement? HttpInvokerServiceExporter has a vulnerability of RCE also so i need to use some replacement of HttpInvokerServiceExporter.
RMI skeleton
#Configuration
public class RMIServer {
#Bean
DTBookingService bookingService() {
return new DTBookingServiceImpl();
}
#Bean(name = "exporter")
RmiServiceExporter exporter(DTBookingService implementation) {
Class<DTBookingService> serviceInterface = DTBookingService.class;
RmiServiceExporter exporter = new RmiServiceExporter();
exporter.setServiceInterface(serviceInterface); //interface
exporter.setService(implementation); //bean
exporter.setServiceName("bookingService"); //bean name
exporter.setRegistryPort(1099);
try {
exporter.prepare();
} catch (RemoteException e) {
e.printStackTrace();
}
return exporter;
}
}
RMI Stub to call remote bean
import javax.naming.Context;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.rmi.RmiProxyFactoryBean;
#Configuration
public class RMIClientConfig {
#Bean
RmiProxyFactoryBean service() {
RmiProxyFactoryBean proxyFactoryBean = new RmiProxyFactoryBean();
proxyFactoryBean.setServiceUrl("rmi://localhost:1099/bookingService");
proxyFactoryBean.setServiceInterface(DTBookingService.class);
return proxyFactoryBean;
}
}
Now i am trying to call bean via HttpInvokerServiceExporter via http protocol instead of rmi://(RmiServiceExporter). Please suggest if any replacement of HttpInvokerServiceExporter.

spring-jms - listener exchange and bind queue by jms configuration

I have a project with spring-jms
I'm trying work with exchanges. I created 4 listeners and all of them are been bound into exchange named 'jms.durable.queues' by default. Even thought I create my own exchanges and binding my queues manually on rabbit console, spring is creating a default exchange.
How could I create my own queues and bind into my exchanges or disable spring to create queues and bind into default exchange 'jms.durable.queues'?
My configuration class
import javax.jms.ConnectionFactory;
import com.rabbitmq.jms.admin.RMQConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;
#EnableJms
#Configuration
public class ConnectionQueueConfig {
#Bean
public ConnectionFactory jmsConnectionRabbitFactory(#Autowired RabbitProperties rabbitProperties) {
RMQConnectionFactory connectionFactory = new RMQConnectionFactory();
connectionFactory.setUsername(rabbitProperties.getUser());
connectionFactory.setPassword(rabbitProperties.getPass());
connectionFactory.setVirtualHost(rabbitProperties.getVirtualhost());
connectionFactory.setHost(rabbitProperties.getHost());
connectionFactory.setPort(rabbitProperties.getPort());
return connectionFactory;
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory( #Autowired ConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setAutoStartup(true);
return factory;
}
#Bean
public JmsTemplate defaultJmsTemplate(#Autowired ConnectionFactory connectionFactory) {
return new JmsTemplate(connectionFactory);
}
}
My listeners
#JmsListener(destination = "queue-1-from-exchange-A" )
public void messageConsumer1(#Payload Message message, #Headers MessageHeaders headers){
}
#JmsListener(destination = "queue-2-from-exchange-A" )
public void messageConsumer2(#Payload Message message, #Headers MessageHeaders headers){
}
#JmsListener(destination = "queue-1-from-exchange-B" )
public void messageConsumer3(#Payload Message message, #Headers MessageHeaders headers){
}
#JmsListener(destination = "queue-2-from-exchange-B" )
public void messageConsumer4(#Payload Message message, #Headers MessageHeaders headers){
}
dependencies
implementation 'org.springframework:spring-jms:5.3.13'
implementation 'com.rabbitmq.jms:rabbitmq-jms:2.3.0'
I have seen about RMQDestination.class, can I use it to create my two exchange and queues?
Is there any spring resolver to manager destination programatically on spring-jms configuration?
example as pseudo-code
someSpringResolverDestination.setDestination(new RMQDestination());
someSpringResolverDestination.setDestination(new RMQDestination());
See source code of this class in RabbitMQ JMS Client: https://github.com/rabbitmq/rabbitmq-jms-client/blob/main/src/main/java/com/rabbitmq/jms/admin/RMQDestination.java.
Since you don’t provide an exchange explicitly that queue name is really bound to that default exchange.
See more docs about this JMS client and how to declare destinations manually for specific exchange and binding : https://www.rabbitmq.com/jms-client.html.
UPDATE
See that documentation closer: https://www.rabbitmq.com/jms-client.html#destination-interoperability
#Bean
public Destination jmsDestination() {
RMQDestination jmsDestination = new RMQDestination();
jmsDestination.setDestinationName("myQueue");
jmsDestination.setAmqp(true);
jmsDestination.setAmqpQueueName("rabbitQueueName");
return jmsDestination;
}
Then look into #JmsListener JavaDocs:
/**
* The destination name for this listener, resolved through the container-wide
* {#link org.springframework.jms.support.destination.DestinationResolver} strategy.
*/
String destination();
The JmsDestinationAccessor uses a DynamicDestinationResolver by default, which, probably, is not going to work over here for us since it is going to do whatever you have so far with that jms.durable.queues exchange.
So, another option to consider for your solution is the custom:
/**
* The bean name of the {#link org.springframework.jms.config.JmsListenerContainerFactory}
* to use to create the message listener container responsible for serving this endpoint.
* <p>If not specified, the default container factory is used, if any.
*/
String containerFactory() default "";
So, you need to add a bean for the DefaultJmsListenerContainerFactory and inject into its setDestinationResolver(DestinationResolver) a BeanFactoryDestinationResolver. Then you provide a RMQDestination bean name into that destination option of your #JmsListener method and BeanFactory is going to give you a proper RMQDestination with desired exchange and binding.
Just posting the solution ( as #Artem Bilan has helped me )
I added a DestinationResolver() for connnecting listener to my queues, and worked.
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory( #Autowired ConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setDestinationResolver(new DestinationResolver() {
#Override
public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain) throws JMSException {
RMQDestination jmsDestination = new RMQDestination();
jmsDestination.setDestinationName(destinationName);
jmsDestination.setAmqpQueueName(destinationName);
jmsDestination.setAmqp(true);
return jmsDestination;
}
});
factory.setAutoStartup(true);
return factory;
}

RabbitMq listener listening to two different message types on two different queues

My requirement is to be able to listen to rabbitMq messages on two different queues. One queue receives message with content_type = text/plain and my Spring boot listener method accepts String parameter. Second queue receives message with content_type = application/json and my listener method accepts parameter of my POJO 'User' type. I am sending messages using RabbitMQ web portal. I am not able to successfully listen to both types of messages in the same spring boot listener application. Please help me to succeed in listening to both types of messages on two queues.Below is my listener class and configuraiton class code snippet.
Listener class:
#Component
public class EventListener {
public void processFirstQueue(String message) {
try {
if (message != null) {
log.info("Received the message from Queue!");
service.process(message);
}
} catch (Exception e) {
log.error("Error occurred " + e);
}
}
public void processSecondQueue(User user) {
try {
if (user!= null) {
log.info("Received the message from Queue!");
service.processUser(user);
}
} catch (Exception e) {
log.error("Error occurred " + e);
}
}
}
RabbitMqConfig.java
public class RabbitMqConfig {
#Bean(name = "rabbitmq-server")
public CachingConnectionFactory getConnectionFactory() {
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
cachingConnectionFactory.setHost("localhost");
return cachingConnectionFactory;
}
#Bean
public SimpleMessageListenerContainer container(ConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames("queue1", "queue2");
container.setMessageListener(listenerAdapter);
container.setMessageConverter(new Jackson2JsonMessageConverter());
return container;
}
#Bean
public MessageListenerAdapter listenerAdapter(OutgoingEventListener receiver) {
MessageListenerAdapter listener = new MessageListenerAdapter(receiver);
Map<String, String> queueToMethodName = new HashMap<>();
queueToMethodName.put("queue1", "processFirstQueue");
queueToMethodName.put("queue2", "processSecondQueue");
listener.setQueueOrTagToMethodName(queueToMethodName);
return listener;
}
#Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
#Bean
public RabbitTemplate getRabbitTemplate(ConnectionFactory connectionFactory) {
return new RabbitTemplate(connectionFactory);
}
}
I am able to post json message successfully to queue2 as seen in below screenshot.
But when I am posting content_type = text/plain to queue1 as seen in below screenshot, I am getting error in my Spring boot service saying content-type text is found but json is expected.
Try to add message converter for your consumer like that -
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class RabbitMqConfig {
#Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
#Bean
public SimpleRabbitListenerContainerFactory jsaFactory(ConnectionFactory connectionFactory,
SimpleRabbitListenerContainerFactoryConfigurer configurer) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
configurer.configure(factory, connectionFactory);
factory.setMessageConverter(jsonMessageConverter());
return factory;
}
}

Spring IntegrationFlow with RestController

I would like to create an application that would perform the following steps:
Receive request thru RestController
Send the received message to a queue (AMQP - MessageChannel) (correlationId?)
Wait for the reply in another queue (AMQP - MessageChannel) (correlationId?)
Return the response on the same thread as the request in step 1.
I thought about using IntegrationFlow for this, but I'm not able to adapt the steps.
Besides, would you know what is the best way to implement this flow?
I tried to implement with the following code:
package poc.integration.http;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.integration.amqp.channel.PollableAmqpChannel;
import org.springframework.integration.amqp.dsl.Amqp;
import org.springframework.integration.amqp.outbound.AmqpOutboundEndpoint;
import org.springframework.integration.amqp.support.DefaultAmqpHeaderMapper;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.http.dsl.Http;
import org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway;
import org.springframework.integration.http.inbound.RequestMapping;
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;
import org.springframework.integration.scheduling.PollerMetadata;
import org.springframework.messaging.MessageChannel;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
#Configuration
#EnableIntegration
public class IntegrationFlowConfig {
final Logger logger = LoggerFactory.getLogger(IntegrationFlowConfig.class);
#Bean
public HttpRequestHandlingMessagingGateway inbound() {
HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(true);
gateway.setRequestMapping(mapping());
gateway.setRequestPayloadTypeClass(String.class);
gateway.setRequestChannelName("httpRequest");
return gateway;
}
#Bean
public RequestMapping mapping() {
RequestMapping requestMapping = new RequestMapping();
requestMapping.setPathPatterns("/foo");
requestMapping.setMethods(HttpMethod.GET);
return requestMapping;
}
#ServiceActivator(inputChannel = "httpResponse")
#Bean
public HttpRequestExecutingMessageHandler outbound() {
HttpRequestExecutingMessageHandler handler =
new HttpRequestExecutingMessageHandler("http://10.141.201.206:80/foo");
handler.setHttpMethod(HttpMethod.GET);
handler.setExpectedResponseType(String.class);
return handler;
}
#ServiceActivator(inputChannel="httpRequest", outputChannel="httpResponse")
public Object processarMensagem(Object mensagem) {
return mensagem + " - done";
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata pollerAmqp(ThreadPoolTaskScheduler taskScheduler) {
final PollerMetadata poller = new PollerMetadata();
poller.setTaskExecutor(taskScheduler);
poller.setReceiveTimeout(-1);
return poller;
}
#Bean
public MessageChannel httpRequest(AmqpTemplate amqpTemplate) {
PollableAmqpChannel channel = new PollableAmqpChannel("httpRequest", amqpTemplate,
DefaultAmqpHeaderMapper.outboundMapper(), DefaultAmqpHeaderMapper.inboundMapper());
channel.setExtractPayload(true);
return channel;
}
#Bean
public MessageChannel httpResponse(AmqpTemplate amqpTemplate) {
PollableAmqpChannel channel = new PollableAmqpChannel("httpResponse", amqpTemplate,
DefaultAmqpHeaderMapper.outboundMapper(), DefaultAmqpHeaderMapper.inboundMapper());
channel.setExtractPayload(true);
return channel;
}
}
but i'm receiving the message:
No reply received within timeout
You just need an IntegrationFlow with an Http.inboundControllerAdapter() as a starting point. It fully replaces the mentioned RestController, but let you to avoid extra work bridging from the #RestController to the IntegrationFlow and back.
The next step in the flow should be an Amqp.outboundGateway() to send and receive over AMQP. This one takes care about a correlation for you.
See more in docs:
https://docs.spring.io/spring-integration/docs/5.3.0.RELEASE/reference/html/http.html#http-java-config
https://docs.spring.io/spring-integration/docs/5.3.0.RELEASE/reference/html/amqp.html#configuring-with-the-java-dsl-4

hibernate Configuration class with multiple DataSource

Our application need to deal with multiple databases. We tried to configure multiple data sources through Hibernate configuration and added two configurations one for database 1 and second for database 2. This configuration fails with following Exception
WARNING: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mainController': Unsatisfied dependency expressed through field 'dataDAO'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataDAO': Unsatisfied dependency expressed through field 'sessionFactory'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.hibernate.SessionFactory' available: expected single matching bean but found 2: sessionFactory1,sessionFactory2
Nov 29, 2019 5:08:11 PM org.springframework.web.context.ContextLoader initWebApplicationContext
SEVERE: Context initialization failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mainController': Unsatisfied dependency expressed through field 'dataDAO'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataDAO': Unsatisfied dependency expressed through field 'sessionFactory'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.hibernate.SessionFactory' available: expected single matching bean but found 2: sessionFactory1,sessionFactory2
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
This is my Configuration class :
package com.pack1.config;
import java.beans.PropertyVetoException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Logger;
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.core.env.Environment;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.pack1.routing.MyRoutingDataSource;
#Configuration
#ComponentScan("com.pack1")
#EnableWebMvc
#EnableTransactionManagement
// Load to Environment
#PropertySources({#PropertySource("classpath:ds/datasource-cfg.properties")})
public class ApplicationContextConfig implements WebMvcConfigurer
{
// The Environment class serves as the property holder
// and stores all the properties loaded by the #PropertySource
#Autowired
private Environment env;
private Logger logger = Logger.getLogger(getClass().getName());
#Bean(name = "viewResolver")
public InternalResourceViewResolver getViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/pages/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
// Returns Routing DataSource (MyRoutingDataSource)
#Autowired
#Bean(name = "dataSource")
public DataSource getDataSource(DataSource dataSource1, DataSource dataSource2) {
System.out.println("## Create DataSource from dataSource1 & dataSource2");
MyRoutingDataSource dataSource = new MyRoutingDataSource();
Map<Object, Object> dsMap = new HashMap<Object, Object>();
dsMap.put("PUBLISHER_DS", dataSource1);
dsMap.put("ADVERTISER_DS", dataSource2);
dataSource.setTargetDataSources(dsMap);
return dataSource;
}
#Bean(name = "dataSource1")
public DataSource getDataSource1() throws SQLException, PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
// See: datasouce-cfg.properties
dataSource.setDriverClass(env.getProperty("ds.database-driver1"));
dataSource.setJdbcUrl(env.getProperty("ds.url1"));
dataSource.setUser(env.getProperty("ds.username1"));
dataSource.setPassword(env.getProperty("ds.password1"));
System.out.println("## getDataSource1: " + dataSource);
return dataSource;
}
private Properties getHibernateProperties1()
{
// set hibernate properties
Properties props = new Properties();
props.setProperty("ds.hibernate.dialect1", env.getProperty("ds.hibernate.dialect1"));
props.setProperty("ds.hibernate.show_sql1", env.getProperty("ds.hibernate.show_sql1"));
return props;
}
// need a helper method
// read environment property and convert to int
private int getIntProperty1(String propName) {
String propVal = env.getProperty(propName);
// now convert to int
int intPropVal = Integer.parseInt(propVal);
return intPropVal;
}
#Bean
public LocalSessionFactoryBean sessionFactory1() throws SQLException, PropertyVetoException{
// create session factorys
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
// set the properties
sessionFactory.setDataSource(getDataSource1());
sessionFactory.setPackagesToScan(env.getProperty("ds.hibernate.packagesToScan1"));
sessionFactory.setHibernateProperties(getHibernateProperties1());
return sessionFactory;
}
#Autowired
#Bean(name = "transactionManager")
public HibernateTransactionManager getTransactionManager1(SessionFactory sessionFactory1) {
// setup transaction manager based on session factory
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory1);
return txManager;
}
#Bean(name = "dataSource2")
public DataSource getDataSource2() throws SQLException, PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
// See: datasouce-cfg.properties
dataSource.setDriverClass(env.getProperty("ds.database-driver2"));
dataSource.setJdbcUrl(env.getProperty("ds.url2"));
dataSource.setUser(env.getProperty("ds.username2"));
dataSource.setPassword(env.getProperty("ds.password2"));
System.out.println("## getDataSource2: " + dataSource);
return dataSource;
}
private Properties getHibernateProperties2()
{
// set hibernate properties
Properties props = new Properties();
props.setProperty("ds.hibernate.dialect2", env.getProperty("ds.hibernate.dialect2"));
props.setProperty("ds.hibernate.show_sql2", env.getProperty("ds.hibernate.show_sql2"));
return props;
}
// need a helper method
// read environment property and convert to int
private int getIntProperty2(String propName) {
String propVal = env.getProperty(propName);
// now convert to int
int intPropVal = Integer.parseInt(propVal);
return intPropVal;
}
#Bean
public LocalSessionFactoryBean sessionFactory2() throws SQLException, PropertyVetoException{
// create session factorys
LocalSessionFactoryBean sessionFactory1 = new LocalSessionFactoryBean();
// set the properties
sessionFactory1.setDataSource(getDataSource2());
sessionFactory1.setPackagesToScan(env.getProperty("ds.hibernate.packagesToScan2"));
sessionFactory1.setHibernateProperties(getHibernateProperties2());
return sessionFactory1;
}
#Autowired
#Bean(name = "transactionManager")
public HibernateTransactionManager getTransactionManage2(SessionFactory sessionFactory1) {
// setup transaction manager based on session factory
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory1);
return txManager;
}
}
Dao class :
package com.pack1.dao;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;
#Repository
public class DataDAO extends JdbcDaoSupport {
#Autowired
#Qualifier(value="t1")
private SessionFactory firstDBSessionFactory;
public List<Publisher> queryPublishers() {
// get the current hibernate session
Session currentSession = firstDBSessionFactory.getCurrentSession();
// create a query
Query<Publisher> theQuery =
currentSession.createQuery("from Publisher", Publisher.class);
// execute query and get result list
List<Publisher> customers = theQuery.getResultList();
// return the results
return customers;
return customers;
}
#Autowired
#Qualifier(value="t2")
private SessionFactory secondDBSessionFactory;
#Transactional(propagation= Propagation.REQUIRED, readOnly=true, value="t2")
public List<Advertiser> queryAdvertisers() {
// get the current hibernate session
Session currentSession = secondDBSessionFactory.getCurrentSession();
// create a query
Query<Advertiser> theQuery =
currentSession.createQuery("from Advertiser", Advertiser.class);
// execute query and get result list
List<Advertiser> customers = theQuery.getResultList();
// return the results
return customers;
}
}
public List<String> queryDashboard() {
return null;
}
}
Properties file :
# DataSource (PUBLISHER System).
ds.database-driver1=com.mysql.jdbc.Driver
ds.url1=jdbc:mysql://127.0.0.1:3306/pan_db
ds.username1=hbstudent
ds.password1=hbstudent
# DataSource (ADVERTISER System).
ds.database-driver2=com.mysql.jdbc.Driver
ds.url2=jdbc:mysql://127.0.0.1:3306/voter_db
ds.username2=hbstudent
ds.password2=hbstudent
#
# Hibernate properties1
#
ds.hibernate.dialect1=org.hibernate.dialect.MySQLDialect
ds.hibernate.show_sql1=true
ds.hibernate.hbm2ddl.auto1=update
#
# Hibernate properties2
#
ds.hibernate.dialect2=org.hibernate.dialect.MySQLDialect
ds.hibernate.show_sql2=true
ds.hibernate.hbm2ddl.auto2=update
SpringWebAppInitializer class :
package com.pack1.config;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.DispatcherServlet;
public class SpringWebAppInitializer implements WebApplicationInitializer
{
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(ApplicationContextConfig.class);
ctx.setServletContext(servletContext); // ②
Dynamic servlet = servletContext.addServlet("dispatcher",new DispatcherServlet(ctx)); // ③
servlet.addMapping("/*");
servlet.setLoadOnStartup(1);
servletContext.addFilter("name", CharacterEncodingFilter.class)
.addMappingForUrlPatterns(null, false, "/*");
}
/*#Override
public void onStartup(ServletContext servletContext) throws ServletException
{
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(ApplicationContextConfig.class);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("SpringDispatcher",
new DispatcherServlet(appContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
//
dispatcher.setInitParameter("contextClass", appContext.getClass().getName());
servletContext.addListener(new ContextLoaderListener(appContext));
// UTF8 Charactor Filter.
FilterRegistration.Dynamic fr = servletContext.addFilter("encodingFilter", CharacterEncodingFilter.class);
fr.setInitParameter("encoding", "UTF-8");
fr.setInitParameter("forceEncoding", "true");
fr.addMappingForUrlPatterns(null, true, "/*");
}*/
}
WebMvcConfigurerAdapter Class :
package com.pack1.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import com.pack1.intercepter.DataSourceIntercepter;
#Configuration
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter{
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
// Default..
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
//
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new DataSourceIntercepter())//
.addPathPatterns("/publisher/*", "/advertiser/*");
}
}
following exception is :
INFO: Server startup in [8,376] milliseconds
Dec 05, 2019 12:05:18 PM org.springframework.web.servlet.DispatcherServlet noHandlerFound
WARNING: No mapping for GET /multiple_DB1/
Dec 05, 2019 12:05:22 PM org.springframework.web.servlet.DispatcherServlet noHandlerFound
WARNING: No mapping for GET /multiple_DB1/
Dec 05, 2019 12:05:23 PM org.springframework.web.servlet.DispatcherServlet noHandlerFound
WARNING: No mapping for GET /multiple_DB1/
I am new to using multiple Datasource in Spring MVC. Please help me Coding in Hibernate Configuration class .
You will need multiple configuration files for each database and one of them needs to be declared as Primary. I am providing you some examples to have an idea what you need.
PrimaryDB is declared the primary database for the project. Primary means that the JPA by default will be executed on the primary database.
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "primaryEntityManagerFactory",
basePackages = {"com.example.repositories.primary"})
public class PrimaryDBConfig {
#Primary
#Bean(name = "primaryDatasource")
#ConfigurationProperties(prefix = "primary.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "primaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder, #Qualifier("primaryDatasource") DataSource dataSource) {
return builder.dataSource(dataSource).packages("com.example.entities.primary")
.persistenceUnit("primary").build();
}
#Primary
#Bean(name = "primaryTransactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("primaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
A second configuration file about our second database FooDB
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "fooEntityManagerFactory",
basePackages = {"com.example.repositories.foo"})
public class FooDBConfig {
#Bean(name = "fooDatasource")
#ConfigurationProperties(prefix = "foo.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "fooEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder, #Qualifier("fooDatasource") DataSource dataSource) {
return builder.dataSource(dataSource).packages("com.example.entities.crf")
.persistenceUnit("foo").build();
}
#Bean(name = "fooTransactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("fooEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
In addition, you will need to add the following on application.properties, because Spring will listen to the default data source configuration.
primary.datasource.url= URL
primary.datasource.username=username
primary.datasource.password=password
foo.datasource.url=URL
foo.datasource.username=username
foo.datasource.password=password
Find below Configuration file
package com.yogendra.configuration;
import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:application.properties")
public class DatabaseConfiguration {
#Autowired
private Environment env;
#Bean("datasource1")
public DataSource dataSource1() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("app1.db.driverClassName"));
dataSource.setUrl(env.getProperty("app1.db.URL"));
dataSource.setUsername(env.getProperty("app1.db.username"));
dataSource.setPassword(env.getProperty("app1.db.password"));
return dataSource;
}
#Bean("datasource2")
public DataSource dataSource2() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("app2.db.driverClassName"));
dataSource.setUrl(env.getProperty("app2.db.URL"));
dataSource.setUsername(env.getProperty("app2.db.username"));
dataSource.setPassword(env.getProperty("app2.db.password"));
return dataSource;
}
#Bean("jdbc1")
public JdbcTemplate jdbcTemplate1() {
return new JdbcTemplate(dataSource1());
}
#Bean("jdbc2")
public JdbcTemplate jdbcTemplate2() {
return new JdbcTemplate(dataSource2());
}
#Bean("session1")
public LocalSessionFactoryBean sessionFactory1() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource1());
sessionFactory.setPackagesToScan(new String[] { "com.yogendra" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean("session2")
public LocalSessionFactoryBean sessionFactory2() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource2());
sessionFactory.setPackagesToScan(new String[] { "com.yogendra" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean("tx1")
public HibernateTransactionManager transactionManager1() {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory1().getObject());
return transactionManager;
}
#Bean("tx2")
public HibernateTransactionManager transactionManager2() {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(sessionFactory2().getObject());
return transactionManager;
}
#SuppressWarnings("serial")
private Properties hibernateProperties() {
return new Properties() {
{
setProperty("hibernate.hbm2ddl.auto", "none");
setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
setProperty("hibernate.show_sql", "true");
}
};
}
}
And here is properties files
app1.db.driverClassName=org.postgresql.Driver
app1.db.URL=jdbc:postgresql://localhost/test_db
app1.db.username=postgres
app1.db.password=postgres
app2.db.driverClassName=org.postgresql.Driver
app2.db.URL=jdbc:postgresql://localhost/test_db2
app2.db.username=postgres
app2.db.password=postgres
You can find working code here
See how you can use the sessionFactory
#Autowired
#Qualifier("session2")
SessionFactory sessionFactory;
#GetMapping(value = "/test2")
#Transactional("tx2")
public void test2() {
List<String> cities = sessionFactory.getCurrentSession().createSQLQuery("select city from address")
.list();
System.out.println("------------ citties ------------ " + cities);
}

Categories

Resources