Very simple Spring transactions of JDBC not roll back (even log said yes) - jdbc

I'm new to Spring transactions. I use Spring 3.2.2 and MySQL 5.5.20(InnoDB). I can see in the log file that it did roll back, but in the database, the record still being updated to 9. What did I miss? Thanks.
The config.xml:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/javatest?useUnicode=true&characterEncoding=UTF-8" />
<property name="username" value="root" />
<property name="password" value="xxx" />
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="hello" class="com.xol.oss.HelloService">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="txManager"/>
The Java code:
public void setDataSource(BasicDataSource dataSource) {
this.dataSource = dataSource;
}
#Transactional
public void getData() {
Connection con=null;
try {
con = dataSource.getConnection();
Statement stat = con.createStatement();
stat.executeUpdate("update testdata set foo=9 where id=1");
throw new RuntimeException("an Exception for test");
} catch (SQLException e) {
e.printStackTrace();
} finally{
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
The log said it did roll back:
15:15:36,936 DEBUG DataSourceTransactionManager:366 - Creating new transaction with name [com.xol.oss.HelloService.getData]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
15:15:37,525 DEBUG DataSourceTransactionManager:205 - Acquired Connection [jdbc:mysql://127.0.0.1:3306/javatest?useUnicode=true&characterEncoding=UTF-8, UserName=root#localhost, MySQL-AB JDBC Driver] for JDBC transaction
15:15:37,535 DEBUG DataSourceTransactionManager:222 - Switching JDBC Connection [jdbc:mysql://127.0.0.1:3306/javatest?useUnicode=true&characterEncoding=UTF-8, UserName=root#localhost, MySQL-AB JDBC Driver] to manual commit
15:15:37,581 DEBUG DataSourceTransactionManager:844 - Initiating transaction rollback
15:15:37,582 DEBUG DataSourceTransactionManager:280 - Rolling back JDBC transaction on Connection [jdbc:mysql://127.0.0.1:3306/javatest?useUnicode=true&characterEncoding=UTF-8, UserName=root#localhost, MySQL-AB JDBC Driver]
15:15:37,583 DEBUG DataSourceTransactionManager:323 - Releasing JDBC Connection [jdbc:mysql://127.0.0.1:3306/javatest?useUnicode=true&characterEncoding=UTF-8, UserName=root#localhost, MySQL-AB JDBC Driver] after transaction
15:15:37,583 DEBUG DataSourceUtils:327 - Returning JDBC Connection to DataSource
Exception in thread "main" java.lang.RuntimeException: an RuntimeException for test
at com.xol.oss.HelloService.getData(HelloService.java:31)
at com.xol.oss.HelloService$$FastClassByCGLIB$$3d7d84e8.invoke(<generated>)

The problem is that you are not using the connection managed by spring, instead you are opening a new connection. Change the code to the following and try.
import org.springframework.jdbc.datasource.DataSourceUtils;
#Transactional
public void getData() {
Connection con=null;
try {
// Get the connection associated with the transaction
con = DataSourceUtils.getConnection(dataSource);
Statement stat = con.createStatement();
stat.executeUpdate("update testdata set foo=9 where id=1");
throw new RuntimeException("an Exception for test");
} catch (SQLException e) {
e.printStackTrace();
} finally{
DataSourceUtils.releaseConnection(dataSource, con);
}
}
If you are writing new code you should be using the JdbcTemplate instead of raw jdbc.
class HelloService {
JdbcTemplate jdbcTemplate;
public setDataSource(DataSource dataSource) {
jdbcTemplate = new JDBCTemplate(dataSource);
}
#Transactional
public void getData() {
jdbcTemplate.update(update testdata set foo=9 where id=1);
throw new RuntimeException("an Exception for test");
}

Related

ActiveMQ Broker inside WSL or docker throws invalid username/password; logon denied

I wrote a simple program to connect to a DB using a spring bean from inside Docker or WSl2. It connects fine.
The moment I add a broker tag to the XML it fails with error.
NOTE : the 'broker' tag, even when added as empty, commenting everything out, causes the same issue.
Below is my program and the bean XML
public class TestDB {
public static void main(String[] args) {
Connection connection1 = null, connection2 = null;
try (ConfigurableApplicationContext mContext = new ClassPathXmlApplicationContext("test.xml");) {
String jdbcClassName="oracle.jdbc.OracleDriver";
String url="jdbc:oracle:thin:#xyz.company.com:1521:abc";
String user="TEST_DB";
String password="test";
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(jdbcClassName);
ds.setUrl(url);
ds.setUsername(user);
ds.setPassword(password);
connection1 = ds.getConnection();
BasicDataSource ds2 = (BasicDataSource) mContext.getBean("application-ds");
connection2 = ds2.getConnection();
}
catch (SQLException e) {
e.printStackTrace();
} finally {
if (connection1!=null) {
System.out.println("Connected successfully.");
try {
connection1.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection2!=null) {
System.out.println("Connected via bean.");
try {
connection2.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
Here is the test.xml:
<beans .....>
<broker xmlns="http://activemq.apache.org/schema/core" persistent="true" brokerName="ABC_MESSAGING_SERVER" advisorySupport="false" useJmx="false">
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry queue="my_account" />
</policyEntries>
</policyMap>
</destinationPolicy>
<persistenceAdapter>
<jdbcPersistenceAdapter dataSource="#application-ds"/>
</persistenceAdapter>
</broker>
<bean id="application-ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:#xyz.company.com:1521:abc" />
<property name="username" value="TEST_DB" />
<property name="password" value="test" />
<property name="maxActive" value="200" />
<property name="poolPreparedStatements" value="true" />
</bean>
</beans>
The output, with the broker part commented is:
and with broker :
org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (ORA-01017: invalid username/password; logon denied)
at org.apache.commons.dbcp.BasicDataSource.createPoolableConnectionFactory(BasicDataSource.java:1549)
at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1388)
at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
at TestDB.main(TestDB.java:29)
Caused by: java.sql.SQLException: ORA-01017: invalid username/password; logon denied
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:509)
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:456)
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:451)
at oracle.jdbc.driver.T4CTTIfun.processError(T4CTTIfun.java:1040)
at oracle.jdbc.driver.T4CTTIoauthenticate.processError(T4CTTIoauthenticate.java:552)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:550)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:268)
at oracle.jdbc.driver.T4CTTIoauthenticate.doOAUTH(T4CTTIoauthenticate.java:501)
at oracle.jdbc.driver.T4CTTIoauthenticate.doOAUTH(T4CTTIoauthenticate.java:1292)
at oracle.jdbc.driver.T4CTTIoauthenticate.doOAUTH(T4CTTIoauthenticate.java:1025)
at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:743)
at oracle.jdbc.driver.PhysicalConnection.connect(PhysicalConnection.java:793)
at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:57)
at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:747)
at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:562)
at org.apache.commons.dbcp.DriverConnectionFactory.createConnection(DriverConnectionFactory.java:38)
at org.apache.commons.dbcp.PoolableConnectionFactory.makeObject(PoolableConnectionFactory.java:582)
at org.apache.commons.dbcp.BasicDataSource.validateConnectionFactory(BasicDataSource.java:1556)
I am answering my own question.
The problem was my Database was version Oracle 12.2.0.1 and my ojdbc jar was ojdbc8-19.3.0.0.
It did not matter when testing on widnows or linux machines.
However using it alongside activemq broker in WSL2 or Docker (virtual envs basically) then it caused 'Invalid username password logon error'.
had to replace the jar with ojdbc8-12.2.0.1 on client side

Access to postgresql server from Spring application

I have a Spring application (Hibernate c3p0) that connects well to an Oracle DB server.
I want to do the same thing with a PostgreSQL DB server, but it sends me the error java.net.UnknownHostException.
In applicationContext, my dataSource is :
<bean id="appDS" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="org.postgresql.Driver" />
<property name="jdbcUrl" value="jdbc:postgresql://THE_SERVER:5432/schema" />
<property name="user" value="appc0" />
<property name="password" value="THE_PWD" />
</bean>
I tried a connection from another very simple java application (from the same server):
public static void main(String[] args) {
try {
System.out.println("TEST Driver Manager START");
Class.forName("org.postgresql.Driver");
String url = "jdbc:postgresql://THE_SERVER:5432/schema";
String user = "appc0";
String passwd = "THE_PWD";
Connection conn = DriverManager.getConnection(url, user, passwd);
System.out.println("Schema : " + conn.getSchema());
System.out.println("ClientInfo : " + conn.getClientInfo());
System.out.println("TEST Driver Manager END");
System.out.println("TEST C3P0 START");
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass( "org.postgresql.Driver" );
cpds.setJdbcUrl( "jdbc:postgresql://THE_SERVER:5432/schema" );
cpds.setUser("appc0");
cpds.setPassword("THE_PWD");
System.out.println("User :"+ cpds.getUser());
System.out.println("TEST C3P0 END");
} catch(Exception e) {
e.printStackTrace();
}
And no mistake in this case, I connect well. This does not seem to be a network problem.
Does anyone know where the problem may come from?
Versions are:
postgresql-42.2.0.jre7.jar
c3p0-0.9.1.2.jar

Can Spring JdbcTemplate connect to hive?

I am developing a java web project which is based on spring. And I want to use Spring JdbcTemplate to connect to hive. But when I tested my service, it came out this error message
"org.springframework.jdbc.CannotGetJdbcConnectionException: Could not
get JDBC Connection; nested exception is
org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver
class 'org.apache.hadoop.hive.jdbc.HiveDrive'".
The project is created by idea maven, but the hive jdbc driver is a local jar(it is located at WEB-INF/lib). So I am not sure whether the error is caused by the problem that my project still can't recognize the local jdbc driver jar or just because JdbcTemplate does not support hive connection. Can someone help me figure it out? thank you in advance.
Here are my code:
JdbcTemplate definition:
<bean id="dataSourceTDW" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSourceTDW"/>
</bean>
DAO class:
#Repository(value = "tdwQueryImp")
public class QueryDAOImp implements QueryDAO {
#Autowired
JdbcTemplate jdbcTemplate;
public List<Map<String,Object>> execute(String sql) {
return jdbcTemplate.queryForList(sql);
}
}
Kerberos Example:
#Component
public class HiveDataSource extends SimpleDriverDataSource {
private static final Logger logger = LoggerFactory.getLogger(HiveDataSource.class);
private final Subject subject;
#Autowired
HiveDataSource(Subject subject, Driver hiveDriver, String jdbcUrl) {
this.subject = subject;
setUrl(jdbcUrl);
setDriver(hiveDriver);
}
#Override
protected Connection getConnectionFromDriver(final Properties props) throws SQLException {
try {
return Subject.doAs(subject, (PrivilegedExceptionAction<Connection>)() -> getDriver().connect(getUrl(), props));
} catch (Exception e) {
e.printStackTrace();
logger.error("Failed to get Hive JDBC connection.", e);
}
return null;
}
}
Here HiveDriver bean is defined as :
#Bean
HiveDriver hiveDriver() {
HiveDriver impl = new HiveDriver() {
#Override
public Connection connect(String url, Properties info) throws SQLException {
return acceptsURL(url) ? new HiveConnection(url, info) {
#Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
/* do nothing */
};
} : null;
}
};
return impl;
}
I solved my problem by changing the class of datasource from org.apache.commons.dbcp.BasicDataSource to org.springframework.jdbc.datasource.SimpleDriverDataSource.
Here are the beans configuration:
<bean id="hiveDriver" class="org.apache.hadoop.hive.jdbc.HiveDriver"/>
<bean id="dataSourceTDW" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<constructor-arg name="driver" ref="hiveDriver"/>
<constructor-arg name="url" value="${url}"/>
<constructor-arg name="username" value="${username}" />
<constructor-arg name="password" value="${password}" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSourceTDW"/>
</bean>
you can refer bellow link :
http://saurzcode.in/2015/01/connect-hiveserver2-service-jdbc-client/
and more
http://hadooptutorial.info/hive-jdbc-client-example/
https://community.hortonworks.com/articles/53629/writing-a-spring-boot-microservices-to-access-hive.html

why transaction management using annotation is failing in spring with the below configuration

Why transaction management is failing in spring with the following configuration. The transaction is not rolled back even though a RuntimeException is thrown. Well, i am manually throwing it for learning purposes.
My Dao class:
#Transactional(rollbackFor=ArithmeticException.class)
public class TransactionAnnotationDaoImpl extends JdbcDaoSupport implements JdbcDao {
public void create(Student student) {
try {
String sql = "insert into student values (?,?,?)";
getJdbcTemplate().update(sql,student.getAge(), student.getName(), student.getId());
String marksSql="insert into marks values (?,?,?)";
int i=2/0; //added to depict roll back behaviour of the transaction when exception occurs
getJdbcTemplate().update(marksSql,student.getId(),student.getSubject(),student.getMarks());
System.out.println("transaction committed");
} catch (RuntimeException e) {
e.printStackTrace();
System.out.println("transaction rolled back");
}
}
}
My spring configuration file contents:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver"></property>
<property name="url" value="jdbc:derby://localhost:1527/db;create=true"></property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="annotationTransactionDaoImpl" class="org.sujay.springjdbc.dao.TransactionAnnotationDaoImpl">
<property name="dataSource" ref="dataSource"></property></bean>
I am making call to dao from main method with the following call:
JdbcDao annotationDao = context.getBean("annotationTransactionDaoImpl", JdbcDao.class);
annotationDao.create(new Student(25, "p", 4, 19, "cn2"));
But the transaction is not rolled back. There is an entry in the student table but marks table doesnt have which means transaction is failing. Please help me with this issue.
Because you catched the exception.
Remove the try catch or rethrow the exception:
try{
...
} catch (RuntimeException e) {
e.printStackTrace();
System.out.println("transaction rolled back");
throw e; //rethrow so spring will recognize it
}

Spring AOP Declarative Transaction Management Not Working

I'm trying to write a simple application to learn how Transaction works in Spring (Declarative AOP Style). I'm inserting one record into customer table and then throwing NullPointerException to see if the inserted data is rolled back or not. But to my surprise it's not rolling back the data. Here is the code
ApplicationContext.xml file
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
">
<aop:aspectj-autoproxy />
<bean id="BoardingService" class="com.learning.maven.services.BoardingServiceImpl"/>
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
<tx:attributes>
<tx:method name="getUser*" rollback-for="throwable" propagation="REQUIRES_NEW"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="boardingServiceOperation" expression="execution(* com.learning.maven.services.BoardingService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="boardingServiceOperation"/>
</aop:config>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</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:#localhost:1521:XE"/>
<property name="username" value="system"/>
<property name="password" value="Manager"/>
</bean>
<bean id="customerDAO" class="com.learning.maven.dao.CustomerDAOImpl">
<property name="dataSource" ref="dataSource" />
</bean>
This is how I call method
public static void main(String[] args) {
context = new FileSystemXmlApplicationContext("C:\\workspace\\learning\\cxf\\SpringTransaction\\cxf.xml");
BoardingService bean = (BoardingService) context.getBean("BoardingService");
bean.getUser("1");
}
And the BoardingService Class Looks like below
public class BoardingServiceImpl implements BoardingService, ApplicationContextAware {
ApplicationContext context = null;
public String getUser(String id) {
String response = "SUCCESS";
try{
System.out.println("Testing");
CustomerDAO customerDAO = (CustomerDAO) Testing.context.getBean("customerDAO");
Customer c = new Customer();
c.setAge(31);
c.setCustId(1);
c.setName("Jagadeesh");
customerDAO.insert(c);
customerDAO.insert(null);
}
catch(Exception e){
throw new RuntimeException();
}
return response;
}
#Override
public void setApplicationContext(ApplicationContext arg0)
throws BeansException {
this.context = arg0;
}
and the CustomerDAOImpl
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void insert(Customer customer) {
String sql = "INSERT INTO CUSTOMER (CUST_ID, NAME, AGE) VALUES (?, ?, ?)";
Connection conn = null;
try {
conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, customer.getCustId());
ps.setString(2, customer.getName());
ps.setInt(3, customer.getAge());
ps.executeUpdate();
ps.close();
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {}
}
}
}
public Customer findByCustomerId(int custId) {
// TODO Auto-generated method stub
return null;
}
Not sure where am I going wrong. Any Pointers will be of great help.
You have rollback-for "throwable" instead of "Throwable".
P.S. Not sure that you should use REQUIRES_NEW as the default strategy.
update: From DataSourceTransactionManager documentation
Application code is required to retrieve the JDBC Connection via
DataSourceUtils.getConnection(DataSource) instead of a standard
J2EE-style DataSource.getConnection() call. Spring classes such as
JdbcTemplate use this strategy implicitly. If not used in combination
with this transaction manager, the DataSourceUtils lookup strategy
behaves exactly like the native DataSource lookup; it can thus be used
in a portable fashion.
In your case you open connection directly and then Oracle commits transaction on close(this is a feature of Oracle RDBMS).
You can also just omit the rollback-for attribute. Then it will rollback if any RuntimeException occurs.

Resources