I am beginning with spring and I am working on the web mvc. When not in MVC, as pointed in some tutorial, I would specify the data source in beans.xml and call this file with ApplicationContext object, and creating an object by passing the data source. And that worked for me. and when I came back to MVC, I created the data source in my name-servlet.xml file by using
<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/hello"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</bean>
and I have a superclass used for my service classes for data access, only with method setDataSource. and here is my sample.
#Resource(name="dataSource")
public void setDataSource(DataSource dataSource){
this.dataSource=dataSource;
this.jdbcTemplateObject = new JdbcTemplate(dataSource);
}
but I am still getting a null pointer exception when working with the dataSource. please what am i missing?
Related
With MyBatis-Spring-Boot-Starter, we can easily integrate MyBatis with Spring Boot, it works perfectly for one data source. However, now we'd like to add an extra data source in our project, unfortunately it seems not easy.
In MyBatis official documentation, I see the following content:
MyBatis-Spring-Boot-Starter will:
Autodetect an existing DataSource.
Will create and register an instance of a SqlSessionFactoryBean passing that DataSource as an input.
Will create and register an instance of a SqlSessionTemplate got out of the SqlSessionFactoryBean.
It looks like MyBatis-Spring-Boot-Starter supports only one data source at this moment. So, the question is how to configure multiple MyBatis datasources in Sping Boot?
You outlined 3 beans that are needed for MyBatis+Spring integration. These are automatically created for single data source.
If you need two data sources, you need to create 3 beans for each data source explicitly. So you'll be creating 6 beans (2 of type DataSource, 2 of type SqlSessionFactoryBean and 2 of type SqlSessionFactoryBean).
To bind DAO with certain datasource, you will need to use sqlSessionTemplateRef or sqlSessionFactoryRef parameter of #MapperScan annotation.
Also I don't recommend to go down the XML hell. I was using it this way in PROD, with two data sources, without any ugly XML configs on various projects. Also SQL queries were annotated.
Shame is that MyBatis documentation is not great and most examples out there are in XML.
Something this like this to your spring servlet.xml:
<bean id="db2dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>${db2.database.driver}</value></property>
<property name="url"><value>${db2.database.url}</value></property>
<property name="username"><value>${db2.database.username}</value></property>
<property name="password"><value>${db2.database.password}</value></property>
<property name="maxActive"><value>${db2.database.maxactiveconnections}</value></property>
<property name="maxIdle"><value>${db2.database.idleconnections}</value></property>
<property name="initialSize"><value>${db2.database.initialSize}</value></property>
</bean>
<bean id="db2SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="db2dataSource" />
<property name="configLocation" value="/WEB-INF/mybatis-config.xml"/>
</bean>
<bean id="db2Dao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="db2SqlSessionFactory"/>
<property name="mapperInterface" value="com.dao.db2Dao" />
</bean>
<bean id="oracledataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>${oracle.database.driver}</value></property>
<property name="url"><value>${oracle.database.url}</value></property>
<property name="username"><value>${oracle.database.username}</value></property>
<property name="password"><value>${oracle.database.password}</value></property>
<property name="maxActive"><value>${oracle.database.maxactiveconnections}</value></property>
<property name="maxIdle"><value>${oracle.database.idleconnections}</value></property>
<property name="initialSize"><value>${oracle.database.initialSize}</value></property>
</bean>
<bean id="oracleSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="oracledataSource" />
<property name="configLocation" value="/WEB-INF/mybatis-config.xml"/>
</bean>
<bean id="oracleoardDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="oracleSqlSessionFactory"/>
<property name="mapperInterface" value="com.lodige.clcs.dao.oracleoardDao" />
</bean>
Maybe this is what you need
#Configuration
#MapperScan(basePackages = "com.neo.mapper.test1", sqlSessionTemplateRef =
"test1SqlSessionTemplate")
public class DataSource1Config {
#Bean(name = "test1DataSource")
#ConfigurationProperties(prefix = "spring.datasource.test1")
#Primary
public DataSource testDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "test1SqlSessionFactory")
#Primary
public SqlSessionFactory testSqlSessionFactory(#Qualifier("test1DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
return bean.getObject();
}
#Bean(name = "test1TransactionManager")
#Primary
public DataSourceTransactionManager testTransactionManager(#Qualifier("test1DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
#Bean(name = "test1SqlSessionTemplate")
#Primary
public SqlSessionTemplate testSqlSessionTemplate(#Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
I am trying to test my DAO that uses JPA EntityManager to fetch and update entities. I have marked my unit test as Transactional and set the defaultRollback property to false. However, I don't see my transactions rolling back at the end of the test when throwing a rune time exception. The data is getting persisted in the DB. Here is my unit test code along with spring configuration. I am clearly missing something but havent been able to identify what.
Btw, the transaction is RESOURCE_LOCAL in the persistence.xml
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath:spring/test-jpa.xml"})
#TestExecutionListeners(
{ DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class
})
#TransactionConfiguration(defaultRollback=false)
#Transactional
public class JpaTests {
#PersistenceContext
EntityManage em;
#Test
public void testTransactionQueueManager() {
Object entity = em.find(1);
//code to update entity omitted.
entity = em.merge(entity);
em.flush();
throw new RuntimeException
}
}
Spring Configuration
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jpa.driverclassname}" />
<property name="url" value="${jpa.url}" />
<property name="username" value="${jpa.username}" />
<property name="password" value="${jpa.password}" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="${jpa.persistenceunitname}"/>
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter">
<property name="databasePlatform" value="org.apache.openjpa.jdbc.sql.DBDictionary"/>
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
Your configuration seems fine.
There could be different reasons for the unexpected commit, maybe a datasource with autocommit mode or a non transaction compliant database (mysql with MyISAM ?)
Did you check this thread Why are transactions not rolling back when using SpringJUnit4ClassRunner/MySQL/Spring/Hibernate ?
#TransactionConfiguration(defaultRollback=false)
might be the culprit. Try defaultRollback=true, that should rollback the transaction.
Adding rollbackFor may help, it's a common pitfall.
#Transactional(rollbackFor=Exception.class)
Hello I'm new to Hibernate.
I have generated with Hibernate Tools a database access module. The generator generates the code of the DAOS and Hibernate Beans.
When I test this module in a simple Java application all works fine, but when I test it in a Spring Web application I get a very strange error. Since my module is an independent jar it should access the database without regarding the circumstance of being executed in a simple Java application or a Web application. The code of my web application is:
#Controller
#RequestMapping("/")
public class Controller implements ApplicationContextAware
{
private ApplicationContext applicationContext;
#RequestMapping(value = "/purchased/songs", method = RequestMethod.GET)
public String home(Model model)
{
SessionManager.startOperation();
ChargeTryDAOBase ctdb=new ChargeTryDAOBase();
List <ChargeTry> data=ctdb.findByRemoteId("dsfsdfsdf8");
SessionManager.endOperation();
model.addAttribute("result", "data" );
return "home";
}
#Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException
{
this.applicationContext = arg0;
}
}
When running this code on Tomcat I get following error:
org.springframework.web.util.NestedServletException: Handler processing
nested exception is java.lang.NoSuchMethodError:
org.hibernate.SessionFactory.getCurrentSession()Lorg/hibernate/Session;
.....
java.lang.NoSuchMethodError:
org.hibernate.SessionFactory.getCurrentSession()Lorg/hibernate/Session;
When I change some Hibernate dependencies I get following error:
java.lang.IllegalStateException: Could not locate SessionFactory in JNDI
When I test the above code in a simple Java application all works fine.
Is this a spring-hibernate configuration problem?
Thank you for your help.
Please study
1: http://www.javatpoint.com/hibernate-and-spring-integration
and
2 http://viralpatel.net/blogs/spring3-mvc-hibernate-maven-tutorial-eclipse-example/
to get insight of Spring MVC and Hibernate Integration.
You can work with Hibernate Configuration file - here is the link -
Spring and hibernate.cfg.xml
But as your application is within a spring managed container, We will highly recommend to use applicationcontext.xml for better maintenance and management of codebase and performance.
thank you for your help finally I got all working. I followed your link and googled a little bit. The problem was that I didn't enable in my hibernate.cfg.xml file the datasource parameter, I also have configured C3P0 jdbc connection provider.
My final hibernate.cfg.xml file is:
<hibernate-configuration>
<session-factory>
<property name="hibernate.bytecode.use_reflection_optimizer">true</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
<property name="current_session_context_class">thread</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mydb</property>
<property name="hibernate.connection.username">userdb</property>
<property name="hibernate.connection.password">12345</property>
<property name="hibernate.connection.datasource">java:comp/env/jdbc/mydb</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.use_sql_comments">true</property>
<property name="hibernate.connection.provider_class">org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider</property>
<property name="hibernate.c3p0.min_size">2</property>
<property name="hibernate.c3p0.numHelperThreads">4</property>
<property name="hibernate.c3p0.max_size">10</property>
<property name="hibernate.c3p0.timeout">300</property>
<property name="hibernate.c3p0.max_statements">100</property>
<property name="hibernate.c3p0.idle_test_period">1800</property>
<property name="hibernate.c3p0.acquire_increment">2</property>
<hibernate-configuration>
<session-factory>
In my web.xml I have added following lines:
<resource-ref>
<description>This is a MySQL database connection</description>
<res-ref-name>jdbc/mydb</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
In the Spring context file I have added following lines:
<beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<beans:property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<beans:property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<beans:property name="username" value="userdb"/>
<beans:property name="password" value="12345"/>
</beans:bean>
<beans:bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<beans:property name="dataSource" ref="dataSource" />
<beans:property name="configLocation">
<beans:value>classpath:hibernate.cfg.xml</beans:value>
</beans:property>
</beans:bean>
The strange thing is, that with the default Hibernate connection provider, the above solution didn't work but when I configured C3P0 all started to work.
Thank you for your help.
I am using Play Framework along with Spring JbbcTemplates. I am using spring DriverManagerDataSource as the datasource for JdbcTemplate. Now, for some tables, I would like to use the Play Model bean. Is this possible? If so, how to make use of the same datasource (used by spring) to load the Play Model Beans. Please advise.
I found a hack for the above mentioned problem. Just created a plugin, and in the onApplicationStartUp(), assing the spring datasource to DB.datasource. Also, we need change the priority of the plugins so tat, spring plugin gets loaded before the JPA plugin.
Please find the code snippet,
play.plugins (place it under the "app" folder of your play project.
388:play.modules.spring.SpringPlugin
399:com.ebay.pricing.catgyautomn.plugin.LoadSpringDsPlugin
//LoadSpringDsPlugin.java
public class LoadSpringDsPlugin extends PlayPlugin {
#Override
public void onApplicationStart() {
Logger.debug("***********LoadSpringDsPlugin:onApplicationStart begins************");
DataSource dataSource = Spring.getBeanOfType(DataSource.class);
DB.datasource = dataSource;
Logger.debug("***********LoadSpringDsPlugin:onApplicationStart ends************");
}
application-context.xml
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
I use spring transaction to include a few db update operation into a single transaction. let say there 2 db updates within a single transaction. the update 1 is successful while the second fails. my problem is when such a case happens, the first db update get committed to db even though the second db update failed which leads to transaction rollback.
XML declaration:
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test" />
<property name="username" value="test" />
<property name="password" value="test" />
</bean>
<bean id="testDao" class="dao.TestDao">
<constructor-arg >
<ref local="simpleJdbcTemplate" />
</constructor-arg>
<constructor-arg >
<ref local="txManager" />
</constructor-arg>
</bean>
java code:
public class DaoCallback extends TransactionCallbackWithoutResult {
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
try{
dbUpdate1();
dbUpdate2();
}catch(Exception e){
arg0.setRollbackOnly();
}
}
i intentionally make the dbUpdate1 to success and the dbUpdate2 to fail so as the test out whether the rollback really works. When I debug through my code, i can see that the control flow run into the catch exception and the "setRollbackOnly()" method is called.
But when I check the database, I can see the change from dbUpdate1(). So please help explain what is wrong here?
Dara kok,
I've found out the problem. It's not the code i've that cause the problem. it's MySQL data storage configuration. MyISAM doesn't support transaction.
Spring should have shown some kind of error message so that developer can know that a transaction is being called on a database engine without transaction support.