UCP PoolDataSourceImpl with ConnectionInitializationCallback - java

I am attempting to configure a UCP PoolDataSourceImpl with a ConnectionInitializationCallback. Here's my configuration:
private static DataSource createDataSource(Properties properties) throws SQLException, UniversalConnectionPoolException {
UniversalConnectionPoolManager ucpm = UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager();
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
final String editionName = properties.getProperty("jdbc.editionName", "ora$base");
pds.registerConnectionInitializationCallback(new oracle.ucp.jdbc.ConnectionInitializationCallback() {
public void initialize(Connection connection) throws SQLException {
LOG.debug("Attempting to set edition to: {}", editionName);
try (Statement statement = connection.createStatement()) {
statement.executeUpdate("ALTER SESSION SET EDITION = " + editionName);
}
LOG.debug("Edition set to: {}", editionName);
}
});
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setUser(properties.getProperty("jdbc.username"));
pds.setPassword(properties.getProperty("jdbc.password"));
pds.setURL(properties.getProperty("jdbc.url"));
pds.setConnectionPoolName("demo-pool");
pds.setInitialPoolSize(3);
pds.setMaxPoolSize(3);
pds.setValidateConnectionOnBorrow(true);
LOG.debug("Created DataSource Pool");
ucpm.createConnectionPool((UniversalConnectionPoolAdapter)pds);
ucpm.startConnectionPool("demo-pool");
return pds;
}
However the initialize method is never called. I'm using java 1.7.0_51 with the following Oracle jars:
ojdbc6.jar - v12.1.0.1.0
ucp.jar - v12.1.0.0.0
I have managed to make this work by removing the call to "registerConnectionInitializationCallback" and replacing it with a call to "registerConnectionLabelingCallback" but from my understanding this will exeute the ALTER SESSION each time a connection is requested from the pool rather then when it is actually created.
Any help with getting the ConnectionInitializationCallback to work would be much appreciated.
Kind Regards

The ConnectionInitializationCallback mechanism appears to be a part of the "Application Continuity" feature released with 12c. Application Continuity requires that you use one of the following DataSource implementations:
oracle.jdbc.replay.OracleDataSourceImpl
oracle.jdbc.replay.OracleConnectionPoolDataSourceImpl
I haven't tried it for myself, but I'm guessing if you use one of these DataSource implementations, your ConnectionInitializationCallback will work.
It would've been nice if this had been included as part of the standard UCP implementation, much like Connection Labeling.

Related

Tomcat doesn't sync with the mysql database

I'm currently working on a college project, and I'm creating a very simple e-commerce style website.
I'm using JDBC driver manager and connection pool for the connection to the db, while using Tomcat 9.0 as the container.
The problem is: when I modify some product through the website (let's say the amount available for example), the website doesn't always reflect the changes, while I can always see the data correctly in MySql Workbench.
It actually works one time out of two on the same query:
I run the query for the first time after the changes -> it shows the old value
I run the query for the second time after the changes -> it shows the new value
I run the query for the third time after the changes -> it shows the old value
And so on.
I've already tried to set caching off (from the query, using the SQL_NO_CACHE), but it didn't seem to solve the problem, I've tried to use Datasource instead, but it causes other problems that most likely I won't have the time to solve.
This is the connection pool file, which I think might be problem, I'm not that sure tho:
public class DriverManagerConnectionPool {
private static List<Connection> freeDbConnections;
static {
freeDbConnections = new LinkedList<Connection>();
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
System.out.println("DB driver not found:"+ e.getMessage());
}
}
private static synchronized Connection createDBConnection() throws SQLException {
Connection newConnection = null;
String ip = "localhost";
String port = "3306";
String db = "storage";
String username = "root";
String password = "1234";
newConnection = DriverManager.getConnection("jdbc:mysql://"+ ip+":"+ port+"/"+db+"?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC", username, password);
newConnection.setAutoCommit(false);
return newConnection;
}
public static synchronized Connection getConnection() throws SQLException {
Connection connection;
if (!freeDbConnections.isEmpty()) {
connection = (Connection) freeDbConnections.get(0);
freeDbConnections.remove(0);
try {
if (connection.isClosed())
connection = getConnection();
} catch (SQLException e) {
connection.close();
connection = getConnection();
}
} else {
connection = createDBConnection();
}
return connection;
}
public static synchronized void releaseConnection(Connection connection) throws SQLException {
if(connection != null) freeDbConnections.add(connection);
}
}
I really hope you can help me, I haven't found any solution online!
I guess it is because of auto-commit is disabled. Please try using #Transactional or set auto-commit to true. You can also try to use db.commit after each statement.
As per your connection pool implementation, all connection in your pool seems to be auto committed false.
Please check you have properly committed the connection after executing the query or not.
So it might be the case that, when executing the query after changes with same connection it reflects those changes, done earlier and on other connections, old values are might get returned.

Multi-tenant hibernate doesn't switch between tenants

I am in the middle of changing my spring + hibernate + mysql setup to be multi-tenant. First of all, I have the following in my application.properties:
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
I am not sure if I'm supposed to make it connect to a specific schema here (test) after introducing multi-tenancy? It doesn't make sense to me, as it's supposed to use a default schema if no tenant is provided, otherwise connect to the schema associated with the tenant. However, if I remove it I get an error that no database was provided.
Secondly, the multi-tenant part doesn't seem to be working. All my queries are made in the test schema. I have implemented the following MultiTenantConnectionProvider:
#Component
public class TenantConnectionProvider implements MultiTenantConnectionProvider {
private Datasource datasource;
public TenantConnectionProvider(DataSource datasource) {
this.datasource = datasource;
}
...
#Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
logger.info("Get connection for tenant {}", tenantIdentifier);
final Connection connection = getAnyConnection();
connection.setSchema(tenantIdentifier);
return connection;
}
#Override
public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
logger.info("Release connection for tenant {}", tenantIdentifier);
connection.setSchema(DEFAULT_TENANT);
releaseAnyConnection(connection);
}
}
I am getting no errors, and when I make a query it correctly prints Get connection for tenant with the correct tenantIdentifier that matches the name of one of my other schemas. Still, it queries the test schema. What am I missing? Thanks!
EDIT:
It seems like connection.setSchema(tenantIdentifier) has no effect. In the method's description, it says the following: If the driver does not support schemas, it will silently ignore this request. So I'm guessing my driver does not support it? What to do in that case?
Using connection.createStatement().execute("USE " + tenantIdentifier); instead of connection.setSchema(tenantIdentifier); solved my problem.

How to create and configure MariaDBDataSource manually

I'm having problems with creating unpooled datasource with MariaDBDataSource class.
MariaDbDataSource mysqlDs = new MariaDbDataSource(connectionUrl);
mysqlDs.setPassword(password);
mysqlDs.setUser(username);
return wrapWithPool(mysqlDs);
wrapWithPool simply wraps the given datasource with a pooled one (c3p0 pool).
But I fail to checkout a connection from the pool. Whenever I do
datasource.getConnection()
I get
org.mariadb.jdbc.internal.util.dao.QueryException: Could not connect: Access denied for user 'someuser'#'somehost' (using password: NO)
Not sure why? I do set non empty password. Is there anything else to set on the MariaDbDatasource class to make it use the password?
edit:
Ok, so it seems that when I do not wrap the MariaDbDataSource all works ok.
So c3p0 is breaking up the connection, and from debug I see it fails to get the password...
The wrap method is quite simple
private static DataSource wrapWithPool(DataSource unpooled) throws SQLException {
unpooled.setLoginTimeout(HOST_REACH_TIMEOUT.getValue());
Map<String, Object> poolOverrideProps = new HashMap<>();
poolOverrideProps.put("maxPoolSize", CONNECTION_POOL_SIZE.getValue());
poolOverrideProps.put("minPoolSize", 1);
poolOverrideProps.put("checkoutTimeout", HOST_REACH_TIMEOUT.getValue() * 2);
return DataSources.pooledDataSource(unpooled, poolOverrideProps);
}
And it works perfecly fine with other drivers (oracle, jtds). Why not with mariaDb?
Ok, so I discovered the problem. For some reason, the c3p0 when creating the pool, wraps the given DataSource class within own WrapperConnectionPoolDataSourceBase class. Then it tries to detect the authentication parameters from it using reflection. Since MariaDBDataSource does not provide the getPassword method, the discovered value is null, and thus the error message about not using the password.
So as a workaround I did a simple wrapper
private static class MariaDbDExtender extends MariaDbDataSource {
private String password;
public MariaDbDExtender(String connectionUrl) throws SQLException {
super(connectionUrl);
}
#Override
public void setPassword(String pass) {
this.password = pass;
super.setPassword(pass);
}
//this method is required to allow c3p0 magically use reflection to get correct password for connection
public String getPassword() {
return password;
}
}
and later on
MariaDbDExtender mysqlDs = new MariaDbDExtender(connectionUrl);
mysqlDs.setPassword(password);
mysqlDs.setUser(username);
return wrapWithPool(mysqlDs);
And it magically starts to work. This is some driver specific issue, since oracle datasource does not have the getPassword method, but works. So some very specific implementation details of those 2 libraries just make it incompatible in my use case.

ormlite JdbcPooledConnectionSource correct usage

We are developing a new desktop application in JavaFx wherein for offline storage we are using SQLite and for orm we are using ormlite.
I want to implement DB connection pooling wherein a fixed number of connections should be set at the start and should be used, released and reused as required. Also, it would be good if we can make use of "readonly" and "writeonly" connections appropriately to maximize performance.
This is what we have written so far.
public class DAO {
private static JdbcPooledConnectionSource connectionSource;
private static DAO instance = null;
private DAO() throws SQLException {
try {
final File path = SystemUtils.getDatabaseFile();
final String DATABASE_URL = Constants.DATABASE_URL + path.getAbsolutePath();
Class.forName(Constants.DATABASE_DRIVER);
connectionSource = new JdbcPooledConnectionSource(DATABASE_URL);
//connectionSource.setMaxConnectionAgeMillis(5 * 60 * 1000);
connectionSource.setCheckConnectionsEveryMillis(5000);
connectionSource.setMaxConnectionsFree(5);
connectionSource.initialize();
init();
} catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void init() throws ClassNotFoundException, SQLException {
TableUtils.createTableIfNotExists(connectionSource, Customer.class);
TableUtils.createTableIfNotExists(connectionSource, Address.class);
TableUtils.createTableIfNotExists(connectionSource, Location.class);
TableUtils.createTableIfNotExists(connectionSource, City.class);
TableUtils.createTableIfNotExists(connectionSource, Area.class);
TableUtils.createTableIfNotExists(connectionSource, Category.class);
TableUtils.createTableIfNotExists(connectionSource, Product.class);
TableUtils.createTableIfNotExists(connectionSource, AddonCategory.class);
TableUtils.createTableIfNotExists(connectionSource, ProductAddon.class);
}
public synchronized <D extends Dao<T, ?>, T> D getDao(Class<T> cls) throws SQLException {
Dao<T, ?> dao = DaoManager.createDao(connectionSource, cls);
D daoImpl = (D) dao;
return daoImpl;
}
public synchronized static DAO getInstance() throws SQLException {
if (instance == null) instance = new DAO();
return instance;
}
}
The problem here is everytime we are creating table (TableUtils.createTableIfNotExists) the pooled connection source is making a new connection and not reusing the one earlier used/created.
Not finding enough code examples on Internet on how to correctly use JdbcPooledConnectionSource.
JdbcPooledConnectionSource correct usage
Which SQLite driver are you using? The Xerial driver has the Sqlite code actually compiled into the Jar. This means that you really aren't "connecting" to another database just making calls to the database directly even though it is fronted by a JDBC interface.
This means that you really don't need a JdbcPooledConnectionSource. The pooled connections really only help when you are making connections over the network to a database server. If you look at your application, you should see file-descriptors (in /prod/#/fd if in Linux) which show open FDs to the database but not to sockets.
The problem here is everytime we are creating table (TableUtils.createTableIfNotExists) the pooled connection source is making a new connection and not reusing the one earlier used/created.
Hrm. I have some good coverage in unit tests around the pooled connection source. I'm surprised to hear that it isn't reusing connections. Can you get me a unit test to demonstrate it?

Database connection leak, hibernate 4.3 + spring webflow 2.3.1

I'm currently facing the following known issue : https://jira.spring.io/browse/SWF-1525
I using Oracle9 and Ikaricp as connection pool. (At first i thought it was an issue with apache dbcp, this is why i switched to ikaricp)
I'm not using JPA but i tried to adapt one of the given workaround to the HibernateFlowExecutionListener.
Here is the code :
public class FixedHibernateFlowExecutionListener extends HibernateFlowExecutionListener {
private final Logger logger = LoggerFactory.getLogger(getClass());
public FixedHibernateFlowExecutionListener(SessionFactory sessionFactory, PlatformTransactionManager transactionManager) {
super(sessionFactory, transactionManager);
}
#Override
public void paused(RequestContext context) {
super.paused(context);
if (isPersistenceContext(context.getActiveFlow())) {
final Session session = getSession(context.getFlowExecutionContext().getActiveSession());
if (session != null && session.isConnected()) {
session.disconnect();
if (session.isConnected()) {
logger.error("Couldn't disconnect the connection from the session");
}
}
}
}
private boolean isPersistenceContext(FlowDefinition flow) {
return flow.getAttributes().contains(PERSISTENCE_CONTEXT_ATTRIBUTE);
}
private Session getSession(FlowSession session) {
return (Session) session.getScope().get(PERSISTENCE_CONTEXT_ATTRIBUTE);
}}
The problem (beside the bug in SWF) is that calling 'session.disconnect();' never disconnects the session from the connection so the connection remains in use.
The lazy init is triggered in a subflow in 10% of the cases, in the 'on-start' tag of the subflow using Hibernate.initialize() on each collection item.
I have to find a fix for this because this is a very heavy operation that must not necessarily be done.
My hibernate properties :
hibernate.connection.release_mode=after_transaction
hibernate.temp.use_jdbc_metadata_defaults=false
hibernate.default_schema=*****
hibernate.dialect=org.hibernate.dialect.Oracle9iDialect
hibernate.id.new_generator_mappings=true
hibernate.event.merge.entity_copy_observer=allow
Has anyone found a solution for this?
Note : there was a similar question but related to jpa Database connections not being closed with jpaFlowExecutionListener
Thanks for help.

Categories

Resources