Using DataSource with Transaction using Spring - java

I am working as part of a legacy j2ee application (plain application no Spring or Hibernate support)
The application exposes the following method:
public DataSource getConnectionDataSource();
DataSource is properly initiated to a specific DB schema by the product.
When I want to query the DB I create a jdbcTemplate object and query the like so:
MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("PRINT_LOCA",printerLocation);
DataSource printersSchemaDS = context.getCommonServices().getConnectionDataSource("printersSchema");
NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(printersSchemaDS);
String printerId = jdbcTemplate.queryForObject("select printerId from printers where printer_location=:PRINT_LOCA ",parameters,String.class);
My question is how can I execute multiple Update SQL statements in a single transaction when I only have the DataSource object?
I see that there is TransactionTemplate in Spring, but is it possible to initialize it with DataSource object alone?
Thank you!

Try to take single connection from datasource, then use ordinary manual jdbc transaction:
try (Connection con = datasource.getConnection();) {
con.setAutoCommit(false);
// insert logic
con.commit();
} catch (SQLException e) {
// handle exception
con.rollback();
}
Full example here:
https://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html

Related

GenerationType.SEQUENCE breaks spring application

I really want to try batching insert operations for myself in spring, but there is a huge problem that is driving me insane.
As written on web, Hibernate will silently disable batching queries whenever you have entity with GenerationType.IDENTITY (https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#batch-session-batch).
Therefore, I have tried to change my entities generationType to Sequence, but whenever i do this, my project seems to be completely broken (it won't even start).
I start to think that there are several problems in compatibility of postgresql and batching operations in spring.
Could you please explain to me, why is that?
My datasource configuration method:
#Bean
public DataSource dataSource() {
Properties props = new Properties();
props.setProperty("dataSourceClassName", "org.postgresql.ds.PGSimpleDataSource");
props.setProperty("dataSource.user", "tesstuser");
props.setProperty("dataSource.password", "12345");
props.setProperty("dataSource.databaseName", "testdatabase");
props.put("dataSource.logWriter", new PrintWriter(System.out));
HikariConfig config = new HikariConfig(props);
HikariDataSource dataSource = new HikariDataSource(config);
return ProxyDataSourceBuilder.create(dataSource)
.name("Batch-Insert-Logger")
.asJson().countQuery().logQueryToSysOut().build();
}
Error message:
org.hibernate.exception.SQLGrammarException: could not extract ResultSet
PostgreSql exception:
org.postgresql.util.PSQLException: ERROR: relation "hibernate_sequence" does not exist

How to write an exception to allow application to run if database is not found?

I currently have a jdbcUrl oracle database binded to my cloud foundry application. I want my application to be able to build and run even if the service is not binded. Meaning it would throw a null exception, but still allow the app to run. (just wouldn't display any data).
SQLREPO
#Repository
public class myTableRepo {
#Autowired
JdbcTemplate jdbcTemplate;
#Transactional(readOnly=true)
public List<myTable_Model> getAll(String id) {
String sql = "SELECT * from myTable order by last_modified desc";
return jdbcTemplate.query(sql,
new myTableRowMapper());
}
}
RowMapper class
class myTableRowMapper implements RowMapper<myTable_Model>
{
#Override
public myTable_Model mapRow(ResultSet rs, int rowNum) throws SQLException {
myTable_Model model = new myTable_Model();
model.setId(rs.getString("id"));
model.setName(rs.getString("name"));
model.setlast_modified(rs.getString("last_modified"));
return model;
}
}
How would I write an exception if database is not found. Catch it and continue my application?
It looks like you're using Spring, so I would suggest that you take a look at profiles.
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-definition-profiles
With profiles, you can configure a different set of beans for different environments. For example, you could configure a "local" profile that runs and configures an in-memory database, and you can configure a "cloud" profile for when your app is running on Cloud Foundry. When you deploy your app to Cloud Foundry, the Java build pack will activate the "cloud" profile automatically (this can work without CF too, you just manually have to enable the profile). Instructions for enabling profiles are here.
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-definition-profiles-enable
Spring Boot makes enabling profiles even easier. It also allows you to configure conditional beans, for example, if bean A doesn't exist then configure X. This way you could have a default in-memory database that is configured if no other datasource is configured.
You could catch only exceptions related to SQL connection and then just log it.
#Transactional(readOnly=true)
public List<myTable_Model> getAll(String id) {
List<myTable_Model> result = new ArrayList<>();
try {
String sql = "SELECT * from myTable order by last_modified desc";
result = jdbcTemplate.query(sql, new Clone_HistoryRowMapper());
}
catch(SQLException ex) {
logger.log("An error happened when interacting to the database.", ex)
}
return result;
}
This way the app would continue if an error only related to SQL happens and stop id some other error occurs.

How to use Postresql Large Object API with Spring's JdbcTemplate

I need to access a Large Object using postgresql API. This is done like this:
PGConnection pgConn=(PGConnection)c
LargeObjectManager lobj =pgConn.getLargeObjectAPI();
LargeObject obj = lobj.open(imageOid, LargeObjectManager.READ);
InputStram is=obj.getInputStream();
I need to run this code from my DAO while using Spring's JdbcTemplate. How can I get access to the connection I'm using?
Assuming that DataSource has been configured correctly,
PGConnection pgConn = (PGConnection) jdbcTemplate.getDataSource().getConnection();

How to get current Connection object in Spring JDBC

How can I get the current Connection object for an Oracle database? I'm using the JDBC module in Spring 3.0.5.
Obtain the Connection from the DataSource bean.
You can access the dataSource by using Spring dependency injection to inject it into your bean, or by accessing ApplicationContext statically:
DataSource ds = (DataSource)ApplicationContextProvider.getApplicationContext().getBean("dataSource");
Connection c = ds.getConnection();
Just an Info :
I am using Spring JDBC Template, which holds the current connection object for me, which can be received as follows.
Connection con;
con = getJdbcTemplate().getDataSource().getConnection();
Use DataSourceUtils.getConnection().
It returns connection associated with the current transaction, if any.
I'm not sure if this method was available when this question was originally posted, however, it seems the preferred way to do it in the latest version of Spring is with JdbcTemplate and PreparedStatementCreator. See https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jdbc/core/JdbcTemplate.html#query-org.springframework.jdbc.core.PreparedStatementCreator-org.springframework.jdbc.core.PreparedStatementSetter-org.springframework.jdbc.core.ResultSetExtractor- or any of the other query methods that take a PreparedStatementCreator as the first param:
jdbcTemplate.query(con -> {
// add required logic here
return con.prepareStatement("sql");
}, rs -> {
//process row
});
This has the advantage over the other provided answers (DataSourceUtils.getConnection() or jdbcTemplate.getDataSource().getConnection() as a new connection is not allocated, it uses the same connection management it would as calling any of the other jdbcTemplate querying methods. You also therefore do not need to worry about closing / releasing the connection, since spring will handle it.

Spring DatasourceTransaction Manager Problem

final DataSource ds = DataSourceLocator.getInstance()
.getDataSource(sg.cmpl.starhub.lprs.Constants.APP_KEY);
final DataSourceTransactionManager txManager = new DataSourceTransactionManager();
txManager.setDataSource(ds);
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
final TransactionStatus status = txManager.getTransaction(def);
Connection conn = null;
PreparedStatement ps = null;
try {
/***************************************************************************/
conn = DataSourceUtils.getConnection(ds);
ps = conn.prepareStatement(sql);
ps.execute();
/***************************************************************************/
txManager.commit(status);
} catch (Exception e) {
txManager.rollback(status);
}
Is there something wrong with my transaction manager logic? It looks like unstable. When I insert new data, First time it seems to save and later I can't find the data in mysql database. Please help. Thanks a lot.
Assuming there's a special reason you want to do programmatic connection and transaction management I suggest taking a look at Spring's JdbcTemplate and wrap it's usage in a TransactionTemplate.
However as stated in the previous comment, you should try to avoid programmatic transaction management as much as possible and use annotations (#Transactional) or XML configuration (TransactionProxyFactoryBean or <tx:advice/>) instead.
Yes, there is something wrong. This is not the Spring way. You should not be putting commit/rollback logic in code like this. The advantage comes when you can do it declaratively, in configuration.
Have a look at the Spring transaction reference docs.
As a side note: according to Spring documentation if commit operation fails with TransactionException then rollback was already called and calling it again in catch block will trigger IllegalTransactionStateException.

Categories

Resources