Spring Transaction Management with legacy JDBC - spring-jdbc

I am trying to inject Spring Transaction Management on to legacy JDBC code.But it fails in transaction management.
My Spring.xml
<tx:annotation-driven proxy-target-class="true"
transaction-manager="transactionManager" />
<bean id="dbcpDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#sbsvmlx101.suntecsbs.com:1521:orcl11g" />
<property name="username" value="XLRT2_TEST_MAIN" />
<property name="password" value="XLRT2_TEST_MAIN" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
and annotated my service method with #Transactional.
#Transactional
public void createEmp(Employee employee, DataSourceTransactionManager dataSourceTransactionManager)throws Exception {
generaldao.createEmployee(employee,dataSourceTransactionManager);
}
I am trying to insert data into two different tables.First one got successfully inserted,and knowingly i made an error in second insertion ,expecting that the both insertions won't happen.
But surprisingly ,the first insertion got committed, and second has shown oracle error.
DAO
con = DataSourceUtils.doGetConnection(dataSource);
con.setAutoCommit(false);
insert = con.prepareStatement
("insert into emp500 values(?,?,?)");
insert.setInt(1, 1);
insert.setString(2, "test");
insert.setString(3, "data");
insert.execute();
insertdept = con.prepareStatement
("insert into department values(?,?)");
insertdept.setInt(1, 2);
insertdept.setString(2, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); //too large value for the column
insertdept.execute();
Please advice.
Thanks in Advance

Related

Switch on/off IBM MQ listener based on flag from DB

This is an existing monolith app and fix is required without a drastic change of the setup.
Project setup:
project 1 (config) - > where all mq-xml files are present (e.g - ibm_mq_config.xml)
e.g - content of dev_bm_mq.xml
${hname}
${port}
${qmgr}
1
<!-- JMS Queue Connection Factory -->
<bean id="jmsQueueIdsConnectionFactory"
class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory">
<ref bean="mqIdsConnectionFactory" />
</property>
</bean>
<!-- JMS Destination Resolver -->
<bean id="jmsDestinationResolver"
class="org.springframework.jms.support.destination.DynamicDestinationResolver">
</bean>
<!-- JMS Queue Template -->
<bean id="jmsQueueIdsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<ref bean="jmsQueueIdsConnectionFactory" />
</property>
<property name="defaultDestinationName">
<value>${myQUEUE}</value>
</property>
<property name="pubSubDomain">
<value>false</value>
</property>
<property name="receiveTimeout">
<value>20000</value>
</property>
</bean>
<bean id="jmsContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="jmsQueueConnectionFactory" />
<property name="destinationName">
<value>${myQUEUE}</value>
</property>
<property name="messageListener" ref="simpleMessageListener" />
<property name="concurrentConsumers" value="2" />
<property name="maxConcurrentConsumers" value="3" />
<property name="idleTaskExecutionLimit" value="4" />
<property name="maxMessagesPerTask" value="4" />
<property name="receiveTimeout" value="5000" />
<property name="recoveryInterval" value="5000" />
<property name="sessionTransacted" value="true" />
</bean>
****Project B (App)****
loads the spring xml from project config as below:
WebContent/WEB-INF/spring/sprint-context.xm
<import resource="classpath*:com/my/package/${config.env}-${config-broker}.mq.xml"
public class TestMessageListener implements MessageListener {
public void onMessage(Message message) {
//process the message
}
}
When the server starts up, it's able to start the server and setup the listener without any issues.
Issue with the above setup : When we scale the app horizontally (add few nodes ), it's gives max channel issues which I am trying to solve.
Requirement:
based on a DB table I want to turn off the mq listener on few nodes on the fly. or when I horizontally scale the app.
e.g -
Table:mq-config
|host|broker|flag
-----------------------------
|qa5|ibm|false
|qa2|ibm|true
So, I want mq listener on qa5 not to start and qa2 to start and listen to the Queue. Also, I want to stop/start listener on the fly (just by updating the DB)
Question - Any thoughts on how do I achieve the above use case without re-writing the entire setup.
Inject the listener container (e.g. #Autowired).
Then
jmsContainer.stop();
jmsContainer.shutdown();
...
jmsContainer.initialize();
jmsContainer.start();
You can also set the autoStartup property to false to prevent the container from starting during application initialization (but don't call initialize() before the first start() - only after calling shutdown().

Invalidated object not currently part of this pool

When I use redisCacheManager to put something, it throws an exception "Invalidated object not currently part of this pool". But when I set the usePool to false, it can work. I think this is a Multi-threaded case. But I don't know why the spring-data-redis's annotation can work.
<code>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/applicationContext.xml");
UserServiceImpl userService = applicationContext.getBean(UserServiceImpl.class);
RedisCacheManager redisCacheManager = applicationContext.getBean(RedisCacheManager.class);
redisCacheManager.getCache("cacheName").put("key","value");
<cache:annotation-driven ></cache:annotation-driven>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="jedisConnectionFactory"/>
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="usePool" value="true"></property>
<property name="hostName" value="${redis.host}" />
<property name="port" value="${redis.port}" />
</bean>
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"
c:template-ref="redisTemplate"/>
</code>

Configuring MyBatis with Spring MVC for multiple datasources

I have been trying to configure MyBatis with Spring MVC to work with multiple databases. I have a page which is trying to connect to one of the DB's to fetch data, so that it can be populated into the drop-down boxes.
Now I am not sure what is wrong with this configuration, but I am receiving the following error:
]] Root cause of ServletException. org.springframework.jdbc.BadSqlGrammarException: ### Error querying database. Cause: java.sql.SQLException: ORA-06576: not a valid function or procedure name
I am providing the XML file here for your reference
<bean id="dataSource1"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#1.1.2.5:1529:DITOS" />
<property name="username" value="return" />
<property name="password" value="return" />
</bean>
<!-- scan for mappers and let them be autowired -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage"
value="p.com.ent.appl.return.dev.dao,p.com.ent.appl.return.dep.dao,p.com.ent.appl.return.dao.otheruser" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory1" />
</bean>
<!-- Declare a transaction manager -->
<bean id="cashReturnTx"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource1" />
</bean>
<!-- define the SqlSessionFactory, notice that configLocation is not needed
when you use MapperFactoryBean -->
<bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource1" />
<property name="configLocation" value="WEB-INF/mybatis/sqlmap-config.xml" />
<!-- <property name="mapperLocations" value="classpath:/com/vrn/ent/dev/daoxml/*.xml"
/> -->
<property name="mapperLocations"
value="classpath*:/p/com/ent/appl/return/**/daoxml/*.xml" />
</bean>
<bean id="sqlSession1" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory1" />
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#4.24.80.15:1522:LM" />
<property name="username" value="RETURN" />
<property name="password" value="OWNER" />
</bean>
<!-- scan for mappers and let them be autowired -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage"
value="p.com.ent.appl.return.dev.dao,p.com.ent.appl.return.dep.dao,p.com.ent.appl.return.dao.otheruser" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<bean id="otherUserTx"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- define the SqlSessionFactory, notice that configLocation is not needed
when you use MapperFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="WEB-INF/mybatis/sqlmap-config.xml" />
<!-- <property name="mapperLocations" value="classpath:/com/vrn/ent/dev/daoxml/*.xml"
/> -->
<property name="mapperLocations"
value="classpath*:/p/com/ent/appl/return/**/daoxml/*.xml" />
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
</beans>
According to the exception: BadSqlGrammarException: ### Error querying database. Cause: java.sql.SQLException: ORA-06576: not a valid function or procedure name, a DB is hit.
Read about error ORA-06576.
Issue is likeky be related to how the procedure is called. Right way is { CALL MyProcedure (#arg0, #arg1) }
Configuration shows that same mappers may be used with both datasources, what happens then if target schemas are different?

How to execute a spring batch job by clicking on a button in a jsp page?

I'm a newbie in spring batch and spring Mvc, and I want that a batch job (which extracts data from a database and writes it in another database) is executed from a jsp page by clicking on a button (or a link if it's possible) I'm using spring Mvc. This is my job configuration:
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseType" value="oracle" />
</bean>
<bean id="itemReader"
class="org.springframework.batch.item.database.JdbcCursorItemReader"
scope="step">
<property name="dataSource" ref="dataSource" />
<property name="sql"
value="select id,name,qual from users" />
<property name="rowMapper">
<bean class="tn.com.spring.UserRowMapper" />
</property>
</bean>
<bean id="oracleitemWriter"
class="org.springframework.batch.item.database.JdbcBatchItemWriter">
<property name="dataSource" ref="dataSource" />
<property name="sql">
<value>
<![CDATA[
insert into users2(id,name,qual)
values (:id,:name,:qual)
]]>
</value>
</property>
<property name="itemSqlParameterSourceProvider">
<bean
class="org.springframework.batch.item.database.BeanPropertyItemSqlParameterSourceProvider" />
</property>
</bean>
<batch:job id="Job" job-repository="jobRepository">
<batch:step id="step1">
<batch:tasklet transaction-manager="transactionManager">
<batch:chunk reader="itemReader" writer="oracleitemWriter"
commit-interval=" 10">
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
<jdbc:initialize-database data-source="dataSource">
<jdbc:script
location="org/springframework/batch/core/schema-drop-oracle10g.sql" />
<jdbc:script location="org/springframework/batch/core/schema-oracle10g.sql" />
</jdbc:initialize-database>
and this is my job controller (I found it in the net but still not working!)
#Component
#Controller()
public class JobController {
#Autowired
#Qualifier("JobLauncher")
private JobLauncher jobLauncher;
#Autowired
#Qualifier("Job")
private Job job;
#RequestMapping(value = "/job")
public void job() {
try {
JobExecution execution = jobLauncher.run(job, new JobParameters());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
and here is the button
<form action=" <%=application.getContextPath()%>/job" method="get">
<input type="submit" value="execute My job" />
</form>
Could you please help me ? Whats's missing in my configuration?
I'm confused !
Thanks in advance.
As a solution for this problem, I have made this javascript code which will invoke the execution of the job :
$(function() {
$('#JobBtn').click(function() {
$.get('${batchJobUrl}');
});
});
and in the jsp page you need to specify the Id of the button writen in the javascript code:
<input type="button" value="execute job " id='JobBtn' class="btn" />
I Hope it will help someOne..

Thymeleaf View not recognizing the specified action view

I am facing an issue with viewresolver. Thymeleaf viewresolver not picking up the specified view in my controller action method and it is taking action name as view path instead.
Below is my thymeleaf configuration and controller code.
Controller :
#ModelAttribute("user")
#RequestMapping(method = RequestMethod.POST, value = "/register")
public String register(Model model, #Valid User user, BindingResult result, HttpServletRequest request, final Locale locale) {if (result.hasErrors()) {
List<ObjectError> errors = result.getAllErrors();
for (ObjectError error : errors) {
log.error("Errors are :: " + error.getDefaultMessage());
}
return "registration/indexed";
}else{------my operations------return "profile/index"}}
thymeleaf config..
<!-- THYMELEAF: Template Resolver for email templates -->
<bean id="emailTemplateResolver"
class="org.thymeleaf.templateresolver.ClassLoaderTemplateResolver">
<property name="prefix" value="mail/" />
<property name="templateMode" value="HTML5" />
<property name="order" value="2" />
<!-- Template cache is true by default. Set to false if you want -->
<!-- templates to be automatically updated when modified. -->
<property name="cacheable" value="true" />
</bean>
<!-- Thymeleaf template resolver -->
<bean id="webTemplateResolver"
class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
<property name="prefix" value="/WEB-INF/pages/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML5" />
<property name="order" value="1" />
</bean>
<!-- THYMELEAF: Template Engine (Spring3-specific version) -->
<bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
<property name="templateResolvers">
<set>
<ref bean="emailTemplateResolver" />
<ref bean="webTemplateResolver" />
</set>
</property>
</bean>
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring3.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
<property name="order" value="1" />
<property name="characterEncoding" value="UTF-8" />
</bean>
But my Thymeleaf not recognizing "registration/index" and it is searching for "registration/register"
Would anybody please suggest what I have to do??
Thanks & Regards,
Gupta Katakam
Finally I figured out the problem. The problem is due to #ModelAttribute("user") . When I removed this line on top of my action method the problem got resolved.
Thanks & Regards,
Gupta Katakam

Resources