I'm trying to get jOOQ transactions to work, except using code instead of XML configuration. I think it should be as simple as
// inside #Configuration annotated class
#Bean
public DataSource makeTransactionAware(DataSource fromConnectionPool) {
return new TransactionAwareDataSourceProxy(fromConnectionPool);
}
except, I need to tell Spring that the injected DataSource is not the same one as returned from the method. How do I do that?
Bean custom naming & qualifier should do the work for you.
https://www.baeldung.com/spring-qualifier-annotation
#Bean(name = "txAwareDS")
public DataSource makeTransactionAware(#Qualifier("dataSource") DataSource fromConnectionPool) {
return new TransactionAwareDataSourceProxy(fromConnectionPool);
}
Example code is just a starting point.
From the Spring configuration file shared
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close" >
<!-- These properties are replaced by Maven "resources" -->
<property name="url" value="${db.url}" />
<property name="driverClassName" value="${db.driver}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>
<bean id="transactionAwareDataSource"
class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<constructor-arg ref="dataSource" />
</bean>
will be configured as
#Configuration
#PropertySource("classpath:config.properties")
public class DataSourceConfig{
#Value("${db.username}")
private String username;
#Value("${db.password}")
private String password;
#Value("${db.url}")
private String url;
#Bean
public DataSource dataSource() {
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("<driver class name>"); // Spring will load the driver discovered if not specified explicitly.
basicDataSource.setUsername(username);
basicDataSource.setPassword(password);
basicDataSource.setUrl(url);
return basicDataSource;
}
#Bean
public TransactionAwareDataSourceProxy transactionAwareDataSource(DataSource dataSource) {
return new TransactionAwareDataSourceProxy(dataSource)
}
}
Hope this helps
Related
Good day!
My problem is when i transferred configuration for another DB from xml to #Bean my transactions is lost.... dont rollback and not work.
I see this when in DB after first insert created row (!), but in this method(transaction) start second insert i take Exception and row after first inset stay on DB!
This xml
<bean name="sqlSessionFactoryYarus" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="/WEB-INF/MapperConfigYarus.xml" />
<property name="dataSource" ref="dataSourceYarus" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="ru.project.crm.mapper_yarus"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryYarus" />
</bean>
<bean id="transactionManagerYarus" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceYarus" />
<qualifier value="yarus"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManagerYarus" />
this code (dataSource there is no this not to waste space)
#Component
#Scope("singleton")
#DependsOn("springApplicationContextHolder")
public class YarusConnectionConfig {
#Bean
public SqlSessionFactory sqlSessionFactoryYarus() throws Exception {
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSourceYarus());
sqlSessionFactory.setConfigLocation(new ClassPathResource("../MapperConfigYarus.xml"));
return sqlSessionFactory.getObject();
}
#Bean
public MapperScannerConfigurer yarusMapper() throws Exception {
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setSqlSessionFactoryBeanName("sqlSessionFactoryYarus");
msc.setBasePackage("ru.project.crm.mapper_yarus");
return msc;
}
#Bean
public DataSourceTransactionManager transactionManagerYarus() throws Exception {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSourceYarus());
return dataSourceTransactionManager;
}
}
And
All paces when i want to Transactional annotate #Transactional(value = "transactionManagerYarus")
And if i build project with xml Transactional works fine
BUT if build with #Bean its dont work...
Plesae Help me!
I use
1) Spring 4.3
2) MyBatis
3) Postgesql
4) Java 8
Also we find solution.
Problem was in dataSource
#Bean(destroyMethod = "close", name = "dataSourceYarus")
public ComboPooledDataSource dataSourceYarus() {
ComboPooledDataSource cpds = new ComboPooledDataSource();
//config connection
}
It is my bean, and i call this bean like method, for example
new DataSourceTransactionManager(dataSourceYarus());
I did not attach any importance to this, because in all example this is true.
BUT in xml configuration caused this like "Bean" on his name, i replace call in java-config to
new DataSourceTransactionManager(context.getBean("dataSourceYarus"))
and.... this work for me!
Because if call this method, every time creating new pool then transaction was over
I'm new to Spring and trying to get a example to work. But my application loads twice each time it starts. I think it could be a context problem because of my internet research and I have just one context.xml.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:environment.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="false"/>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:environment.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="false"/>
</bean>
<bean name="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper" />
<bean name="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="requestFactory" ref="requestFactory" />
</bean>
<bean name="requestFactory" class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<property name="connectTimeout" value="10000" />
<property name="readTimeout" value="10000" />
</bean>
<bean name="httpClient" class="org.apache.http.client.HttpClient" factory-bean="requestFactory" factory-method="getHttpClient"/>
<bean name="TraderApplication" class="net.mrmoor.TraderApplication"/>
<bean name="API" class="com.iggroup.api.API"/>
<bean name="LightStreamerComponent" class="com.iggroup.api.streaming.LightStreamerComponent"/>
</beans>
My code of the TraderApplication Class is:
... skipped imports ....
#SpringBootApplication
public class TraderApplication implements CommandLineRunner{
private static final Logger log = LoggerFactory.getLogger(TraderApplication.class);
#Autowired
protected ObjectMapper objectMapper;
#Autowired
private API api;
#Autowired
private LightStreamerComponent lightStreamerComponent = new LightStreamerComponent();
private AuthenticationResponseAndConversationContext authenticationContext = null;
private ArrayList<HandyTableListenerAdapter> listeners = new ArrayList<HandyTableListenerAdapter>();
public static void main(String args[]) {
SpringApplication.run(TraderApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
try {
if (args.length < 2) {
log.error("Usage:- Application identifier password apikey");
System.exit(-1);
}
String identifier = args[0];
String password = args[1];
String apiKey = args[2];
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/public-api-client-spring-context.xml");
TraderApplication app = (TraderApplication) applicationContext.getBean("TraderApplication");
app.run(identifier, password, apiKey);
} catch (Exception e) {
log.error("Unexpected error:", e);
}
}
You mention in your comments above you got this working by removing SpringApplication.run(TraderApplication.class, args); but this would be removing spring-boot from your application so I'm going to assume since your question has a tag of [spring-boot] that this is not what you wanted. So here is an alternative way that you can configure beans using your xml.
#ImportResource({"classpath*:public-api-client-spring-context.xml"}) //Proper way to import xml in Spring Boot
#SpringBootApplication
public class TraderApplication implements CommandLineRunner {
...code you had before goes here
#Autowired
TraderApplication app;
#Override
public void run(String... args) throws Exception {
.. your parsing logic here
app.run(identifier, password, apiKey); //Now uses the autowired instance
}
}
You didn't list your pom.xml or build.gradle but it's important to remember that components you have registered in your context xml may be automatically configured in Spring Boot and you may not need to register them yourself in your xml.(Depending on which items you have starters for in your build file)
I have following xml configuration in my spring context xml, I have used annotation based approach very less and unable to figure out how to represent following using annotation, need help.
<bean id="myPolicyAdmin" class="org.springframework.security.oauth2.client.OAuth2RestTemplate">
<constructor-arg>
<bean class="org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails">
<property name="accessTokenUri" value="${accessTokenEndpointUrl}" />
<property name="clientId" value="${clientId}" />
<property name="clientSecret" value="${clientSecret}" />
<property name="username" value="${policyAdminUserName}" />
<property name="password" value="${policyAdminUserPassword}" />
</bean>
</constructor-arg>
</bean>
In my java class(Policy manager) it is referred as following, I am actually referring a sample and trying to convert it all annotation baesed.
#Autowired
#Qualifier("myPolicyAdmin")
private OAuth2RestTemplate myPolicyAdminTemplate;
EDIT:
I tried creating a bean for org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails but not sure how to set its properties and how to access it as constructor args to myPolicyAdminTemplate
You can configure the same beans using JavaConfig as follows:
#Component
#Configuration
public class AppConfig
{
#Value("${accessTokenEndpointUrl}") String accessTokenUri;
#Value("${clientId}") String clientId;
#Value("${clientSecret}") String clientSecret;
#Value("${policyAdminUserName}") String username;
#Value("${policyAdminUserPassword}") String password;
#Bean
public OAuth2RestTemplate myPolicyAdmin(ResourceOwnerPasswordResourceDetails details)
{
return new OAuth2RestTemplate(details);
}
#Bean
public ResourceOwnerPasswordResourceDetails resourceOwnerPasswordResourceDetails()
{
ResourceOwnerPasswordResourceDetails bean = new ResourceOwnerPasswordResourceDetails();
bean.setAccessTokenUri(accessTokenUri);
bean.setClientId(clientId);
bean.setClientSecret(clientSecret);
bean.setUsername(username);
bean.setPassword(password);
return bean;
}
}
To set the props of a bean you can either use #Value during construction:
How to inject a value to bean constructor using annotations
and this one:
Spring: constructor injection of primitive values (properties) with annotation based configuration
Or #Value in the variables. You could also actually use #Resource, but I wouldn't recommend it.
In your case, the constructor for the
org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails
Would be kinda like
#Autowired
public ResourceOwnerPasswordResourceDetails(
#Value("${accessTokenEndpointUrl}") String accessTokenUri,
#Value("${clientId}") String clientId,
#Value("${clientSecret}") String clientSecret,
#Value("${policyAdminUserName}") String username,
#Value("${policyAdminUserPassword}") String password
)
I shall give you a XML configuration with it anotation based config equivalent respectively, so that you will easily see how to change your bean from xml to java and vice-versa
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<context:component-scan base-package="com.mycompany.backendhibernatejpa.controller" />
<mvc:annotation-driven />
<bean id="iAbonneDao" class="com.mycompany.backendhibernatejpa.daoImpl.AbonneDaoImpl"/>
<bean id="iAbonneService" class="com.mycompany.backendhibernatejpa.serviceImpl.AbonneServiceImpl"/>
<!-- couche de persistance JPA -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<property name="generateDdl" value="true" />
<property name="showSql" value="true" />
</bean>
</property>
<property name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:bd.properties"/>
</bean>
<!-- la source de donnéees DBCP -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" >
<property name="driverClassName" value="${bd.driver}" />
<property name="url" value="${bd.url}" />
<property name="username" value="${bd.username}" />
<property name="password" value="${bd.password}" />
</bean>
<!-- le gestionnaire de transactions -->
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- traduction des exceptions -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<!-- annotations de persistance -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
</beans>
this file is named springrest-sevlet. actually you can give the name you want followed by "-servlet" and mention that name in the file web.xml
<web-app>
<display-name>Gescable</display-name>
<servlet>
<servlet-name>springrest</servlet-name>
<servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springrest</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
the two files should be place in the folder "WEB-INF".
Now the equivalent with anotation based config
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.mycompany.backendhibernatejpaannotation.configuration;
import com.mycompany.backendhibernatejpaannotation.daoImpl.AbonneDaoImpl;
import com.mycompany.backendhibernatejpaannotation.daoInterface.IAbonneDao;
import com.mycompany.backendhibernatejpaannotation.serviceImpl.AbonneServiceImpl;
import com.mycompany.backendhibernatejpaannotation.serviceInterface.IAbonneService;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
/**
*
* #author vivien saa
*/
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.mycompany.backendhibernatejpaannotation")
public class RestConfiguration {
#Bean
public IAbonneDao iAbonneDao() {
return new AbonneDaoImpl();
}
#Bean
public IAbonneService iAbonneService() {
return new AbonneServiceImpl();
}
// #Bean
// public PropertyPlaceholderConfigurer placeholderConfigurer() {
// PropertyPlaceholderConfigurer placeholderConfigurer = new PropertyPlaceholderConfigurer();
// placeholderConfigurer.setLocations("classpath:bd.properties");
// return placeholderConfigurer;
// }
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/gescable");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
#Bean
public HibernateJpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setDatabasePlatform("org.hibernate.dialect.MySQL5InnoDBDialect");
jpaVendorAdapter.setGenerateDdl(true);
jpaVendorAdapter.setShowSql(true);
return jpaVendorAdapter;
}
#Bean
public InstrumentationLoadTimeWeaver loadTimeWeaver() {
InstrumentationLoadTimeWeaver loadTimeWeaver = new InstrumentationLoadTimeWeaver();
return loadTimeWeaver;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSource());
entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter());
entityManagerFactory.setLoadTimeWeaver(loadTimeWeaver());
return entityManagerFactory;
}
#Bean
public JpaTransactionManager jpaTransactionManager() {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return jpaTransactionManager;
}
#Bean
public PersistenceExceptionTranslationPostPro`enter code here`cessor persistenceExceptionTranslationPostProcessor() {
return new PersistenceExceptionTranslationPostProcessor();
}
#Bean
public PersistenceAnnotationBeanPostProcessor persistenceAnnotationBeanPostProcessor() {
return new PersistenceAnnotationBeanPostProcessor();
}
}
this file must be accompanied by this one
package com.mycompany.backendhibernatejpaannotation.configuration;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
/**
*
* #author vivien saa
*/
public class Initializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{RestConfiguration.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
I'm working with Spring and Mybatis and I have two databases, the configuration for the first database was relative easy, but I can't get to work the second database with Spring and transactions, here is my code
#Configuration
#ComponentScan(basePackages = {"hernandez.service", "hernandez.dao"})
#EnableTransactionManagement
#MapperScan(basePackages="hernandez.mapper" )
#Import(DbConfig2.class)
public class AppConfig {
#Bean(name = "dataSource")
public DataSource dataSource() {
DriverManagerDataSource ds = new DriverManagerDataSource("com.mysql.jdbc.Driver",
"jdbc:mysql://localhost:3306/northwind", "root", "");
return ds;
}
#Bean
public SqlSessionFactoryBean sqlSessionFactory() {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource());
return factoryBean;
}
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
#Configuration
#MapperScan("loli.mapper" )
public class DbConfig2 {
#Bean(name = "dataSource_2")
public DataSource dataSource2() {
DriverManagerDataSource ds = new DriverManagerDataSource("com.mysql.jdbc.Driver",
"jdbc:mysql://localhost:3306/dmsolut_dmsms", "root", "");
return ds;
}
#Bean
public SqlSessionFactory sqlSessionFactory2() throws Exception{
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource2());
return factoryBean.getObject();
}
#Bean(name = "transactionManager_2")
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource2());
}
}
Is there a way to get this working with pure Spring Java configuration or at least with some XML? There's no official documentation to get two databases working in the Mybatis-Spring project
Multi datasources with mybatis are used in my project right now. This is an Example, add to your application.xml
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
<property name="url" value="${center.connectionURL}"/>
<property name="username" value="${userName}"/>
<property name="password" value="${password}"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.xxx.dao.center"/>
<property name="sqlSessionFactoryBeanName" value="cneterSqlSessionFactory"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" name="cneterSqlSessionFactory">
<property name="dataSource" ref="dataSource"></property>
<property name="mapperLocations" value="classpath*:mapperConfig/center/*.xml"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--center db end-->
<!--exdb-->
<bean id="dataSourceEx" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
<property name="url" value="${ex.connectionURL}"/>
<property name="username" value="${userName}"/>
<property name="password" value="${password}"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.xxx.dao.ex"/>
<property name="sqlSessionFactoryBeanName" value="exSqlSessionFactory"/>
</bean>
<bean id="sqlSessionFactoryEx" class="org.mybatis.spring.SqlSessionFactoryBean" name="exSqlSessionFactory">
<property name="dataSource" ref="dataSourceEx"></property>
<property name="mapperLocations" value="classpath*:mapperConfig/ex/*.xml"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<bean id="transactionManagerEx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceEx"/>
</bean>
Add answer with java config example we use in our project:
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
#Configuration
#ComponentScan(basePackages = "com.mycompany")
#EnableTransactionManagement(proxyTargetClass = true)
public class ApplicationConfig2 {
public static final String DATA_SOURCE_NAME_1 = "jdbc/dataSource1";
public static final String DATA_SOURCE_NAME_2 = "jdbc/dataSource2";
public static final String SQL_SESSION_FACTORY_NAME_1 = "sqlSessionFactory1";
public static final String SQL_SESSION_FACTORY_NAME_2 = "sqlSessionFactory2";
public static final String MAPPERS_PACKAGE_NAME_1 = "com.mycompany.mappers.dao1";
public static final String MAPPERS_PACKAGE_NAME_2 = "com.mycompany.mappers.dao2";
#Bean
public DataSource dataSource1() {
JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
return dsLookup.getDataSource(DATA_SOURCE_NAME_1);
}
#Bean
public DataSource dataSource2() {
JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
return dsLookup.getDataSource(DATA_SOURCE_NAME_2);
}
#Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
#Bean(name = SQL_SESSION_FACTORY_NAME_1)
public SqlSessionFactory sqlSessionFactory1(DataSource dataSource1) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setTypeHandlersPackage(DateTimeTypeHandler.class.getPackage().getName());
sqlSessionFactoryBean.setDataSource(dataSource1);
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBean.getObject();
sqlSessionFactory.getConfiguration().setMapUnderscoreToCamelCase(true);
sqlSessionFactory.getConfiguration().setJdbcTypeForNull(JdbcType.NULL);
return sqlSessionFactory;
}
#Bean(name = SQL_SESSION_FACTORY_NAME_2)
public SqlSessionFactory sqlSessionFactory2(DataSource dataSource2) throws Exception {
SqlSessionFactoryBean diSqlSessionFactoryBean = new SqlSessionFactoryBean();
diSqlSessionFactoryBean.setTypeHandlersPackage(DateTimeTypeHandler.class.getPackage().getName());
diSqlSessionFactoryBean.setDataSource(dataSource2);
SqlSessionFactory sqlSessionFactory = diSqlSessionFactoryBean.getObject();
sqlSessionFactory.getConfiguration().setMapUnderscoreToCamelCase(true);
sqlSessionFactory.getConfiguration().setJdbcTypeForNull(JdbcType.NULL);
return sqlSessionFactory;
}
#Bean
public MapperScannerConfigurer mapperScannerConfigurer1() {
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage(MAPPERS_PACKAGE_NAME_1);
configurer.setSqlSessionFactoryBeanName(SQL_SESSION_FACTORY_NAME_1);
return configurer;
}
#Bean
public MapperScannerConfigurer mapperScannerConfigurer2() {
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage(MAPPERS_PACKAGE_NAME_2);
configurer.setSqlSessionFactoryBeanName(SQL_SESSION_FACTORY_NAME_2);
return configurer;
}
}
In my experience, you should also add #Primary to one of the DataSource beans. Otherwise it will throw NoUniqueBeanDefinitionException.
#Bean
#Primary
public DataSource dataSource1() {
JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
return dsLookup.getDataSource(DATA_SOURCE_NAME_1);
}
#Bean
public DataSource dataSource2() {
JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
return dsLookup.getDataSource(DATA_SOURCE_NAME_2);
}
You can use spring's AbstractRoutingDataSource by extending it and overriding the method determineCurrentLookupKey().
Spring Configuration
You can define separate datasource in spring configuration.
<!-- db2 data source -->
<bean id="db2DataSource" class="com.ibm.db2.jdbc.app.DB2Driver">
<property name="serverName" value="${db2.jdbc.serverName}" />
<property name="portNumber" value="${db2.jdbc.portNumber}" />
<property name="user" value="${db2.jdbc.username}" />
<property name="password" value="${db2.jdbc.password}" />
<property name="databaseName" value="${db2.jdbc.databaseName}" />
</bean>
<!-- mysql data source -->
<bean id="mysqlDataSource" class="com.mysql.jdbc.Driver">
<property name="serverName" value="${mysql.jdbc.serverName}" />
<property name="portNumber" value="${mysql.jdbc.portNumber}" />
<property name="user" value="${mysql.jdbc.username}" />
<property name="password" value="${mysql.jdbc.password}" />
<property name="databaseName" value="${mysql.jdbc.databaseName}" />
</bean>
Associate the datasource with customer:
<bean id="customer" class="com.example.Customer">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="com.example.datasource.CustomerRoutingDataSource">
<property name="targetDataSources">
<map key-type="com.example.Customer">
<entry key="db2" value-ref="mysqlDataSource"/>
<entry key="mysql" value-ref="db2DataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="mysql"/>
</bean>
Java
package com.example;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class CustomerRoutingDataSource extends AbstractRoutingDataSource {
#Bean
CustomerContextHolder context;
#Override
protected Object determineCurrentLookupKey() {
return context.getCustomerType();
}
}
Basically, each request will have its context. You can associate datasource with request using mapped key. You can find more details here dynamic-datasource-routing
<bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource1" />
<property name="configLocation">
<value>classpath:com/dtcc/dao/impl/DaoSqlMapConfig_MyBatis1.xml</value>
</property>
<property name="transactionFactory">
<bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" />
</property>
<property name="mapperLocations" value="classpath*:com/dtcc/dao/impl/DaoEmfMyBatis.sp.xml"/>
</bean>
<bean id="sqlSession1" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory1" />
</bean>
<!-- MyBatis Changes Ends -->
<bean id="daoEmf" class="com.dtcc.dao.DaoEmfImpl">
<property name="connectionType"><ref local="com.dtcc.sharedservices.utils.resources.ConnTypes.IBM_DB2_CONNECTION" /></property>
<property name="jndiNameForLogging"><ref local="dataSourceName1" /></property>
<property name="sqlSessionTemplate"> <ref local="sqlSession1" /></property>
<property name="applicationLog"><ref local="appLog" /></property>
</bean>
As mentioned above, we need to give corresponding sessionFactory in your DaoImpl. You can not autowire SqlSessionTemplate in your DaoImpl class if you have more than one sessionFactory. Give unique name for each session factory and map it to your respective DaoImpl class.
All you have to do is just to create object for SqlSessionTemplate with Setter method in DaoImpl class and you can make your db call using sqlSessionTemplate object as below,
this.sqlSessionTemplate.selectList("ProcedureID", parameter);
Hi im new to this Spring , MVC and JdBC support.
I want to be able to connect to a mysql database..but when i run my web it returns null. Below codes i believe should be easy,What am i missing here ? Thanks for all replies
Below is my error when try to query the URL
java.lang.NullPointerException
com.simple.myacc.dao.JdbcContactDao.findAll(JdbcContactDao.java:55)
com.simple.myacc.ContactController.getAll(ContactController.java:44)
My spring.xml
.....
<context:component-scan base-package="com.simple.myacc" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/webcontact" />
<property name="username" value="root" />
<property name="password" value="password" />
</bean>
<bean id="jdbcContactDao" class="com.simple.myacc.dao.JdbcContactDao">
<property name="dataSource" ref="dataSource" />
</bean>
My JdbcContactDao
public class JdbcContactDao {
protected static Logger logger = Logger.getLogger("service");
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
public JdbcContactDao() {
}
public List<Contact> findAll() {
String sql = "select * from contact";
List<Contact> contacts = new ArrayList<Contact>();
List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql);
for (Map rs : rows) {
Contact contact = new Contact();
contact.setId((Integer) rs.get("id"));
contact.setFirstname((String) rs.get("firstname"));
contact.setLastname((String) rs.get("lastname"));
contact.setEmail((String) rs.get("email"));
contact.setPhone((String) rs.get("phone"));
contacts.add(contact);
}
return contacts;
}
#Resource(name = "dataSource")
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
} }
My controller , some part of it
#RequestMapping(value="/contact/list2",method = RequestMethod.GET)
public String getAll(ModelMap model) {
dao=new JdbcContactDao();
List<Contact> contacts = dao.findAll();
// Attach persons to the Model
model.addAttribute("contacts", contacts);
return "contact.list";
}
This is the line that says the NULL
List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql);
A common idiom when using the JdbcTemplate class is to configure a DataSource in your Spring configuration file, and then dependency inject that shared DataSource bean into your DAO classes; the JdbcTemplate is created in the setter for the DataSource.
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
You can read more on this here
Your code will look like this
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/webcontact" />
<property name="username" value="root" />
<property name="password" value="password" />
You don't need this
<bean id="jdbcContactDao" class="com.simple.myacc.dao.JdbcContactDao">
<property name="dataSource" ref="dataSource" />
Instead do this
#Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
and annotate your JdbcContactDao class with #Repository
I think that should work
You have your dataSource and your JdbcContactDAO bean configured in the config file.
So in the same way you need to inject the jdbcContactDAO bean into your Controller.
<bean id="myController" class="mypath.MyController">
<property name="dao" ref="jdbcContactDao"/>
</bean>
And in your controller....
public JdbcContactDao dao;
#Resource(name="dao")
public void setDao(JdbcContactDao dao){
this.dao = dao;
}
#RequestMapping(value="/contact/list2",method = RequestMethod.GET)
public String getAll(ModelMap model) {
List<Contact> contacts = dao.findAll();
// Attach persons to the Model
model.addAttribute("contacts", contacts);
return "contact.list";
}
I'm guessing line 55 of JdbcContactDao is this one List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql); You declare jdbcTemplate, but never give it a value and it's also not annotated for injection, so it's always going to be null. So, when you attempt to use it, you will get the NPE.
Had a similar problem was connecting to old tables using java/jdbc
String sql = "select user_name from table"
jdbc.queryForList(sql);
queryReturnList = jdbc.queryForList(sql);
for (Map mp : queryReturnList){
String userName = (String)mp.get("user_name");
}
userName was always null. looking through the map of returned values found that the map was not using user_name but a label set up on the table of "User Name" Had to have DBA's fix. Hope this helps