Spring Boot 2.x Connection Factory - java

I have the below class that works well in Spring Boot 1.5.x
public ConnectionFactory connectionFactory() {
org.springframework.data.jdbc.config.oracle.AqJmsFactoryBeanFactory f=new AqJmsFactoryBeanFactory();
f.setDataSource(dataSource);
f.setCoordinateWithDataSourceTransactions(true);
f.setNativeJdbcExtractor(new org.springframework.jdbc.support.nativejdbc.Jdbc4NativeJdbcExtractor());
f.setConnectionFactoryType(ConnectionFactoryType.QUEUE_CONNECTION);
try {
return f.getObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
I have now upgraded to 2.0.4 version where NativeJdbcExtractor is not present. Can someone help me how to reconfgure the above to get the connectionFactory.

The whole nativejdbc hierarchy doesn't exists anymore and what isn't there anymore cannot be used.So either don't upgrade or figure out a way to not need it (jdbc 4 can use unwrap to get the actual underlying connection).
To get the actual underlying connection there is an unwrap method on the Connection. So something along the lines of connection.unwrap(OracleConnection.class) should get you the actual underlying connection. However that might require an additional upgrade of the aq-jms-connection-factory which I'm not certain supports Spring 5.0.

I have used something like this which can help you :
public OracleConnection getOracleConnection(Connection connection) throws SQLException {
OracleConnection oconn = null;
try {
if (connection.isWrapperFor(oracle.jdbc.OracleConnection.class)) {
oconn = (OracleConnection) connection.unwrap(oracle.jdbc.OracleConnection.class)._getPC();
}
} catch (SQLException e) {
throw e;
}
return oconn;
}

Related

Why is db connection closed after trying and failing to get a lock with spring-data-jpa?

So I would like to wrap a PessimisticLockingFailureException that gets thrown in a jpa repo when trying to get a lock for an entity that is already locked. And handle the wrapped exception in my exception handlers.
But it seems that when spring tries to end the transaction the connection is already closed and spring throws a new exception that overwrites the exception I would like to see.
In the logs I get "Application exception overridden by rollback exception" and it is this I would like to avoid. (Cause of rollback ex is that "Connection is closed")
Is there a solution to this? Or am I doing something wrong?
(Here's some pseudo code of what I'm doing)
String restControllerMethod(String args) {
try {
return service.serviceMethod(args);
} catch (Exception e1) {
throw e1; // org.springframework.orm.jpa.JpaSystemException caused by org.hibernate.TransactionException caused by java.sql.SQLException
}
}
#Transactional
String serviceMethod(String args) {
Entity entity;
try {
entity = repo.repoFindMethod(args);
} catch (Exception e2) {
throw new WrappingException(e2); // org.springframework.dao.PessimisticLockingFailureException caused by org.hibernate.PessimisticLockException
}
// do some processing with entity
return result;
}
#Lock(LockModeType.PESSIMISTIC_READ)
String repoFindMethod(String args);
I'm using spring-boot-starter-parent 2.3.2.RELEASE with spring-boot-starter-web spring-boot-starter-data-jpa and an emmbedded h2 db
Fixed this by adding a com.zaxxer.hikari.SQLExceptionOverride implementation and pointing the
spring.datasource.hikari.exception-override-class-name to it.
This causes hikari to not close the connection when the db throws an exception with the specified error code.
I've also added #QueryHints({#QueryHint(name = "javax.persistence.lock.timeout", value = "0")}) to the locking query since default lock wait times can be vendor specific
The issue with this solution is that it is vendor specific (both for h2 and hikari). And not all vendors support a custom timeout for obtaining locks (h2 for example does not support this but it matters less since it's timeout is very short anyway)
Example of my solution (for h2):
spring.datasource.hikari.exception-override-class-name=com.example.H2SQLExceptionOverride
public class H2SQLExceptionOverride implements SQLExceptionOverride {
private static final Logger logger = LoggerFactory.getLogger(H2SQLExceptionOverride.class);
public static final int LOCK_TIMOUT_ERROR_CODE = 50200;
#java.lang.Override
public Override adjudicate(SQLException sqlException) {
if (sqlException.getErrorCode() == LOCK_TIMOUT_ERROR_CODE) {
logger.debug("Diverting from default hikari impl and continuing transaction with errorCode: "
+ sqlException.getErrorCode() + " and sqlState: " + sqlException.getSQLState());
return Override.DO_NOT_EVICT;
}
return Override.CONTINUE_EVICT;
}
}

Quarkus and JDBC error attempting to commit transaction

I am in the process of experimenting with Quarkus, building a small REST application. For this I have elected to use the Agroal datasource but neither Panache or plain Hibernate (as is shown in their examples), but rather I'm using plaing JDBC.
For database interaction I have created a small service that injects the AgroalDataSource and uses it to open up database connections. Said service exposes two methods one for running no transactional queries and one or transactional ones. For the first part everything works fine, but when I attempt to update a database entry the action fails upon attempting to commit the connection.
public <E> E update(TransactionalRunner<E> runner) {
try (var connection = dataSource.getConnection()) {
return attemptTransactional(runner, connection);
} catch (SQLException e) {
log.error("Establishing a database connection has failed", e);
throw new DataAccessException(e);
}
}
private <E> E attemptTransactional(TransactionalRunner<E> runner, Connection connection) {
try {
connection.setAutoCommit(Boolean.FALSE);
E result = runner.run(new QueryRunner(), connection);
connection.commit();
return result;
} catch (SQLException e) {
try {
log.error("Committing a transaction has failed. Attempting rollback", e);
DbUtils.rollback(connection);
throw new DataAccessException(e);
} catch (SQLException ex) {
log.error("Rolling back a transaction has failed. Giving up...", ex);
throw new DataAccessException(ex);
}
}
}
The stacktrace I'm getting is the following:
2019-12-21 14:25:59,350 ERROR [com.ari.rev.dat.DatabaseAccessService] (vert.x-worker-thread-1) Committing a transaction has failed. Attempting rollback: java.sql.SQLException: Attempting to commit while taking part in a transaction
at io.agroal.pool.wrapper.ConnectionWrapper.commit(ConnectionWrapper.java:183)
at com.ariskourt.revolut.database.DatabaseAccessService.attemptTransactional(DatabaseAccessService.java:44)
at com.ariskourt.revolut.database.DatabaseAccessService.update(DatabaseAccessService.java:33)
at com.ariskourt.revolut.database.DatabaseAccessService_ClientProxy.update(DatabaseAccessService_ClientProxy.zig:114)
at com.ariskourt.revolut.services.AccountTransferService.transferAmount(AccountTransferService.java:66)
at com.ariskourt.revolut.services.AccountTransferService_Subclass.transferAmount$$superaccessor2(AccountTransferService_Subclass.zig:164)
at com.ariskourt.revolut.services.AccountTransferService_Subclass$$function$$2.apply(AccountTransferService_Subclass$$function$$2.zig:51)
at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:54)
at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:119)
at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:92)
at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.doIntercept(TransactionalInterceptorRequired.java:32)
at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.intercept(TransactionalInterceptorBase.java:53)
at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.intercept(TransactionalInterceptorRequired.java:26)
at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired_Bean.intercept(TransactionalInterceptorRequired_Bean.zig:168)
at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
I'm using the default Agroal datasource offered by Quarkus without any custom configuration. As for the QueryRunner these are just part of the Apache DbUtils suite.
Has anyone got any idea on how to resolve this?

Proper way of obtaining the DBConnection for a Web Application

We have a Web Application which will serve more than 1000 concurrent users
Currently , the Utility class for obtaining the DB Connnection is
public static Connection getDBConnection()
{
Connection conn = null;
try
{
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("java:/comp/env/jdbc/MyLocalDB");
try
{
conn = ds.getConnection();
}
catch ( SQLException sqlEx )
{
System.out.println( "cannot get JDBC connection: " + sqlEx );
}
}
catch ( NamingException nEx )
{
nEx.printStackTrace();
}
return conn;
}
Option 2 :
public class DBConnection2 {
private static DataSource dataSource;
static {
try {
dataSource = (DataSource) new InitialContext().lookup("java:/comp/env/jdbc/MyLocalDB");
} catch (NamingException e) {
try {
throw new Exception("'jndifordbconc' not found in JNDI",e);
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
public static Connection getConnection() {
try {
return dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
}
Please let me know whats the better option (I guess its second operation as lookup is a costly operation and i am doing it only for once in a application .)
please share your views .
You should adopt the second approach. As you say you don't need to keep looking up the datasource in JNDI.
I am assuming that you are using a pooled data source as provided by the tomcat-jdbc project, otherwise performance will be terrible.
On the second approach you should be sure to use connection pooling. Otherwise users will have to wait for others.
For example To Tomcat brings it own connection pool.
Or if you don't use Tomcat maybe you could have a look at C3p0: JDBC DataSources/Resource Pools.
see: Instantiating and Configuring a ComboPooledDataSource
The second option initialize dataSource only once when the class is loaded, then it is shared around all DBConnection2 instances. The first option initialize a new DataSource instance every time you call getDBConnection(). Thus the second option has better performance. But please be pay attention to connection release issue. Make sure close the connection if you choose the first option. I prefer use a framework to handle DB connection, like Spring JDBC: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/jdbc.html

Java connecting to multiple databases

I am creating a java application that connects to multiple databases. A user will be able to select the database they want to connect to from a drop down box.
The program then connects to the database by passing the name to a method that creates an initial context so it can talk with an oracle web logic data source.
public class dbMainConnection {
private static dbMainConnection conn = null;
private static java.sql.Connection dbConn = null;
private static javax.sql.DataSource ds = null;
private static Logger log = LoggerUtil.getLogger();
private dbMainConnection(String database) {
try {
Context ctx = new InitialContext();
if (ctx == null) {
log.info("JDNI Problem, cannot get InitialContext");
}
database = "jdbc/" + database;
log.info("This is the database string in DBMainConnection" + database);
ds = (javax.sql.DataSource) ctx.lookup (database);
} catch (Exception ex) {
log.error("eMTSLogin: Error in dbMainConnection while connecting to the database : " + database, ex);
}
}
public Connection getConnection() {
try {
return ds.getConnection();
} catch (Exception ex) {
log.error("Error in main getConnection while connecting to the database : ", ex);
return null;
}
}
public static dbMainConnection getInstance(String database) {
if (dbConn == null) {
conn = new dbMainConnection(database);
}
return conn;
}
public void freeConnection(Connection c) {
try {
c.close();
log.info(c + " is now closed");
} catch (SQLException sqle) {
log.error("Error in main freeConnection : ", sqle);
}
}
}
My problem is what happens if say someone forgets to create the data source for the database but they still add it to the drop down box? Right now what happens is if I try and connect to a database that doesn't have a data source it errors saying it cannot get a connection. Which is what I want but if I connect to a database that does have a data source first, which works, then try and connect to the database that doesn't have a data source, again it errors with
javax.naming.NameNotFoundException: Unable to resolve 'jdbc.peterson'. Resolved 'jdbc'; remaining name 'peterson'.
Which again I would expect but what is confusing me is it then grabs the last good connection which is for a different database and process everything as if nothing happened.
Anyone know why that is? Is weblogic caching the connection or something as a fail safe? Is it a bad idea to create connections this way?
You're storing a unique datasource (and connection, and dbMainConnection) in a static variable of your class. Each time someone asks for a datasource, you replace the previous one by the new one. If an exception occurs while getting a datasource from JNDI, the static datasource stays as it is. You should not store anything in a static variable. Since your dbMainConnection class is constructed with the name of a database, and there are several database names, it makes no sense to make it a singleton.
Just use the following code to access the datasource:
public final class DataSourceUtil {
/**
* Private constructor to prevent unnecessary instantiations
*/
private DataSourceUtil() {
}
public static DataSource getDataSource(String name) {
try {
Context ctx = new InitialContext();
String database = "jdbc/" + name;
return (javax.sql.DataSource) ctx.lookup (database);
}
catch (NamingException e) {
throw new IllegalStateException("Error accessing JNDI and getting the database named " + name);
}
}
}
And let the callers get a connection from the datasource and close it when they have finished using it.
You're catching JNDI exception upon lookup of the nonexistent datasource but your singleton still keeps the reference to previously looked up datasource. As A.B. Cade says, null reference to ds upon exception, or even before that.
On a more general note, perhaps using Singleton is not the best idea.

How do I dynamically replace the class loader for an Eclipse plug-in?

I am developing an Eclipse plug-in that fits a client-server model. Its a commercial project so we cannot re-distribute the JDBC drivers for the various databases we support with the plug-in.
So I developed a preference page to allow the user locate the jars and have a simple discovery mechanism that iterates through the classes in the jar files, loading each one to verify that it implements the java.sql.Driver interface. This all works great.
But the catch is that I am using Hibernate. And Hibernate uses Class.forName() to instantiate the JDBC driver.
If I try to use the following I get ClassNotFoundException.
public Object execute(final IRepositoryCallback callback)
{
final DatabaseDriverClassLoader loader = new DatabaseDriverClassLoader(
Activator.getDefault().getDatabaseDriverRegistry());
final ClassLoader oldLoader = Thread.currentThread()
.getContextClassLoader();
try
{
Thread.currentThread().setContextClassLoader(loader);
try
{
final SessionFactory sessionFactory = this.configuration
.buildSessionFactory();
if (sessionFactory != null)
{
final Session session = sessionFactory
.openSession();
if (session != null)
{
// CHECKSTYLE:OFF
try
// CHECKSTYLE:ON
{
return callback.doExecute(session);
}
finally
{
session.close();
}
}
}
connection.close();
}
finally
{
}
}
// CHECKSTYLE:OFF
catch (Exception e)
// CHECKSTYLE:ON
{
RepositoryTemplate.LOG.error(e.getMessage(), e);
}
finally
{
Thread.currentThread().setContextClassLoader(oldLoader);
}
return null;
}
And if I try creating the driver myself as follows I get a SecurityException.
public Object execute(final IRepositoryCallback callback)
{
final DatabaseDriverClassLoader loader = new DatabaseDriverClassLoader(
Activator.getDefault().getDatabaseDriverRegistry());
final ClassLoader oldLoader = Thread.currentThread()
.getContextClassLoader();
try
{
Thread.currentThread().setContextClassLoader(loader);
final Class driverClass = loader.loadClass(this.connectionDriverClassName);
final Driver driver = (Driver)driverClass.newInstance();
DriverManager.registerDriver(driver);
try
{
final Connection connection = DriverManager.getConnection(
this.connectionUrl, this.connectionUsername,
this.connectionPassword);
final SessionFactory sessionFactory = this.configuration
.buildSessionFactory();
if (sessionFactory != null)
{
final Session session = sessionFactory
.openSession(connection);
if (session != null)
{
// CHECKSTYLE:OFF
try
// CHECKSTYLE:ON
{
return callback.doExecute(session);
}
finally
{
session.close();
}
}
}
connection.close();
}
finally
{
DriverManager.deregisterDriver(driver);
}
}
// CHECKSTYLE:OFF
catch (Exception e)
// CHECKSTYLE:ON
{
RepositoryTemplate.LOG.error(e.getMessage(), e);
}
finally
{
Thread.currentThread().setContextClassLoader(oldLoader);
}
return null;
}
EDIT: I am not sure it is the best option but I took the approach of implementing my own ConnectionProvider which allowed me instantiate the driver using Class.forName() and I then open the connection using Driver.connect() instead of DriverManager.getConnection(). Its pretty basic but I don't need connection pooling in my specific use case.
The configure() method was as follows:
public void configure(final Properties props)
{
this.url = props.getProperty(Environment.URL);
this.connectionProperties = ConnectionProviderFactory
.getConnectionProperties(props);
final DatabaseDriverClassLoader classLoader = new DatabaseDriverClassLoader(
Activator.getDefault().getDatabaseDriverRegistry());
final String driverClassName = props.getProperty(Environment.DRIVER);
try
{
final Class driverClass = Class.forName(driverClassName, true,
classLoader);
this.driver = (Driver)driverClass.newInstance();
}
catch (ClassNotFoundException e)
{
throw new HibernateException(e);
}
catch (IllegalAccessException e)
{
throw new HibernateException(e);
}
catch (InstantiationException e)
{
throw new HibernateException(e);
}
}
And the getConnection() method is as follows:
public Connection getConnection()
throws SQLException
{
return this.driver.connect(this.url, this.connectionProperties);
}
Class.forName() in OSGi is a major pain. This is not really anyone's fault, just that both use class loaders which do not work the way the other's client is expecting (i.e. OSGi class loader doesn't work in the way that hibernate is expecting).
I think you can go one of a few ways, but the ones I can think of right now are:
the clean way, which is to package the JDBC drivers as OSGi bundles. Contribute the class as a service. You can do this with declarative services (probably better) or write an activator which you'll need to manage starting. When you're ready to get the driver, get the JDBCDriver service, and look for the class that you're interested in.
the less clean way, but will work for less effort than the first - use DynamicImport-Package to add the exported packages from your bundled drivers. This way, the client code can still see the class that it'll use, but it doesn't have to know about it until runtime. You may have to experiment with the package pattern, however, to cover all cases (that's why it's less clean).
the less OSGi way; which is to add your drivers to the eclipse classpath, and add the application parent classloader. You can add this: osgi.parentClassloader=app to your config.ini. This may not fit in with your deployment, especially if you haven't got control of the config.ini file.
the non-OSGi way, instead of using the context class loader, use the URLClassLoader. This will only work if you have a directory full of driver jars, or the user can directly or indirectly specify the location of the driver jar.

Categories

Resources