A dba in my office proposed the following to audit db access (oracle):
Put the current logged in user in my application as a property on the db connection (java.sql.Connection).
public class ClientInfoProvider {
void enrichConnection(Connection connection, String userName) throws SQLException {
OracleConnection oConn = (OracleConnection) connection;
String[] metrics = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
metrics[OracleConnection.END_TO_END_CLIENTID_INDEX] = userName;
oConn.setEndToEndMetrics(metrics, (short) 0);
}
}
public class AuditableDataSource implements DataSource {
...
#Override
public Connection getConnection() throws SQLException {
Connection connection = dataSource.getConnection();
clientInfoProvider.enrichConnection(connection, loggedInUserString);
return connection;
}
...
}
My main concern is that connections are pooled. Wil users not end up using the same connection? The last user which changes the properties of the connection might influence the other users that are already using this connection. Is this a correct assumption?
Using the Spring jdbc datasource, a jdbc connection lives only long enough to execute it's query. Once the query is executed and the resultset has been closed, the connection will also be closed and released to the pool to be used for another query.
Related
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.
I tried to find the connection pool which allows to get connection by jdbc url but failed. Hikari connection pool doesn't allow do it, the same situation in c3po.
My use case is:
ConnectionPool.getConnection(jdbcUrl);
Does anybody know such connection pool in java world?
A Simple Guide to Connection Pooling in Java
Author - Baeldung
About Author
A Simple Implementation
To better understand the underlying logic of connection pooling, let's
create a simple implementation.
Let's start out with a loosely-coupled design, based on just one
single interface:
public interface ConnectionPool {
Connection getConnection();
boolean releaseConnection(Connection connection);
String getUrl();
String getUser();
String getPassword();
}
The ConnectionPool interface defines the public API of a basic
connection pool.
Now, let's create an implementation, which provides some basic
functionality, including getting and releasing a pooled connection:
public class BasicConnectionPool
implements ConnectionPool {
private String url;
private String user;
private String password;
private List<Connection> connectionPool;
private List<Connection> usedConnections = new ArrayList<>();
private static int INITIAL_POOL_SIZE = 10;
public static BasicConnectionPool create(
String url, String user,
String password) throws SQLException {
List<Connection> pool = new ArrayList<>(INITIAL_POOL_SIZE);
for (int i = 0; i < INITIAL_POOL_SIZE; i++) {
pool.add(createConnection(url, user, password));
}
return new BasicConnectionPool(url, user, password, pool);
}
// standard constructors
#Override
public Connection getConnection() {
Connection connection = connectionPool
.remove(connectionPool.size() - 1);
usedConnections.add(connection);
return connection;
}
#Override
public boolean releaseConnection(Connection connection) {
connectionPool.add(connection);
return usedConnections.remove(connection);
}
private static Connection createConnection(
String url, String user, String password)
throws SQLException {
return DriverManager.getConnection(url, user, password);
}
public int getSize() {
return connectionPool.size() + usedConnections.size();
}
// standard getters
}
Connection pooling is a well-known data access pattern, whose main
purpose is to reduce the overhead involved in performing database
connections and read/write database operations.
In a nutshell, a connection pool is, at the most basic level, a
database connection cache implementation, which can be configured to
suit specific requirements.
In this tutorial, we'll make a quick roundup of a few popular
connection pooling frameworks, and we'll learn how to implement from
scratch our own connection pool.
Why Connection Pooling?
The question is rhetorical, of course.
If we analyze the sequence of steps involved in a typical database
connection life cycle, we'll understand why:
Opening a connection to the database using the database driver Opening
a TCP socket for reading/writing data Reading / writing data over the
socket Closing the connection Closing the socket It becomes evident
that database connections are fairly expensive operations, and as
such, should be reduced to a minimum in every possible use case (in
edge cases, just avoided).
Here's where connection pooling implementations come into play.
By just simply implementing a database connection container, which
allows us to reuse a number of existing connections, we can
effectively save the cost of performing a huge number of expensive
database trips, hence boosting the overall performance of our
database-driven applications.
JDBC Connection Pooling Frameworks
From a pragmatic perspective, implementing a connection pool from the
ground up is just pointless, considering the number of
“enterprise-ready” connection pooling frameworks available out there.
From a didactic one, which is the goal of this article, it's not.
Even so, before we learn how to implement a basic connection pool,
let's first showcase a few popular connection pooling frameworks.
Apache Commons DBCP
public class DBCPDataSource {
private static BasicDataSource ds = new BasicDataSource();
static {
ds.setUrl("jdbc:h2:mem:test");
ds.setUsername("user");
ds.setPassword("password");
ds.setMinIdle(5);
ds.setMaxIdle(10);
ds.setMaxOpenPreparedStatements(100);
}
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
private DBCPDataSource(){ }
}
In this case, we've used a wrapper class with a static block to easily
configure DBCP's properties.
Here's how to get a pooled connection with the DBCPDataSource class:
connection con = DBCPDataSource.getConnection();
HikariCP
public class HikariCPDataSource {
private static HikariConfig config = new HikariConfig();
private static HikariDataSource ds;
static {
config.setJdbcUrl("jdbc:h2:mem:test");
config.setUsername("user");
config.setPassword("password");
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
ds = new HikariDataSource(config);
}
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
private HikariCPDataSource(){}
}
Similarly, here's how to get a pooled connection with the
HikariCPDataSource class:
Connection con = HikariCPDataSource.getConnection();
I have a spring boot application that does not use connection pool and we didn't want to open a DB connection at every request
So, here is what we have in a class called MySQLService which has methods with DB queries:
#Autowired
#Qualifier("mysqlDB")
private Connection connection;
This connection object is always used in all of the methods with queries.
In MySQLConnection class,
#Bean(name = "mysqlDB")
public Connection getConnection() {
Connection connection = null;
try {
Class.forName(mysqlDriver);
LOGGER.debug("get mysql connection...");
connection = DriverManager
.getConnection(jdbcUrl,
user, password);
} catch (Exception exception) {
LOGGER.error("ERROR :: {}", exception);
}
return connection;
}
}
So, we are never really closing the connection, it is being managed by spring context but since we are not using JDBCTemplates, it does not get closed. We have autoreconnect set to true in connection string.
In a day or two, we get the exception:
com.mysql.cj.jdbc.exceptions.CommunicationsException: The last packet successfully received from the server was 61,183,452 milliseconds ago.
I understand it is because SQL Server has connection lifetime set so it expires the connection but what is a way to handle this without using a connection pool
Schedule a ping to the MySQL Server every 6 hours or so, executing this query: select 1 from dual. For that, you need to enable scheduling:
#Configuration
#EnableScheduling
public class SpringConfig {
//...
}
then:
#Scheduled(cron = "0 */6 * * *")
public void schedulePingMySQL() {
// execute `select 1 from dual`
}
Anyway, using a connection pool is the recommended way. This case the code may look like:
#Autowired
private DataSource dataSource;
public void save (Dto dto) {
Connection con = dataSource.getConnection();
// finally, close the connection
}
I am using Java 1.7 and Postgres via the Postgres JDBC drivers. The database connection will be used from a Web Service. In testing, I got the following error:
FATAL: connection limit exceeded for non-superusers
I solved the error by making my connection static, and, only creating once. My question is, is a static connection safe? Is this the right way to do this?
I am using the connection via a ConnectionFactory that looks something like this:
public class ConnectionFactory
{
String driverClassName = "org.postgresql.Driver";
String connectionUrl = "jdbc:postgresql://localhost:5432/dbName";
String dbUser = "user";
String dbPwd = "password";
private static ConnectionFactory connectionFactory = null;
private static Connection conn = null;
private ConnectionFactory()
{
try
{
Class.forName(driverClassName);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
public Connection getConnection() throws SQLException
{
if (conn == null)
{
conn = DriverManager.getConnection(connectionUrl, dbUser, dbPwd);
}
return conn;
}
public static ConnectionFactory getInstance()
{
if (connectionFactory == null)
{
connectionFactory = new ConnectionFactory();
}
return connectionFactory;
}
}
Postgres JDBC driver is documented as thread safe and the connection can be used by multiple threads if so required. If a thread attempts to use the connection while another one is using it, it will wait until the other thread has finished its current operation.
Connection pooling may be used anyway for performance reasons.
Sorry, original post didn't look at your code carefully. Wow. I still can't read. Anyway, third time's the charm. If your code is single threaded - then your fine. If it's multi-threaded, use something like the Commons connection pools to manage your connections. It looks like the driver is thread safe but the connection shouldn't be viewed as thread safe. So, once the driver is loaded you can safely call getConnection on the driver from multiple threads, but the connection shouldn't be shared across threads.
Currently, I load the below custom driver (TestDriver.java), get a connection, create a Statement, execute a query, gets the results and close the connection. I open and close a connection for each query. Is this common practice or is there an standard way to share the open connections?
public static void main(String[] args) {
Class.forName("com.sql.TestDriver");
java.sql.Connection conn = DriverManager.getConnection("jdbc:test://8888/connectme", props);
Statement stmt = conn.createStatement;
ResultSet rs = stmt.executeQuery("select * from table");
//loop through rs and pull out needed data
conn.close();
}
public class TestDriver implements java.sql.Driver{
private final TestSchema schema;
private Properties props = null;
static {
try {
DriverManager.registerDriver(new TestDriver());
} catch (SQLException e) {
e.printStackTrace();
}
protected TestDriver() throws SQLException {
schema = TestSchemaFactory.getInstance().getDbSchemaFromFile(SCHEMA_FILE);
//loads in and parses a file containing tables, columns used for business logic
}
public Connection connect(String url, Properties info)
throws SQLException {
TestSqlConnection conn=null;
//connect logic here
return conn; //will return an instance of TestSqlConnection
}
#Override
public boolean jdbcCompliant() {
return false;
}
}
Yes, it's more common to use a database connection pool. This will allow connections to be reused without the overhead or closing/re-opening. Here's a link to DBCP which is one implementation of a database connection pool: http://commons.apache.org/dbcp/
Ideally you should write a separate factory class (can be static)
say ConnectionFactory which returns a connection object.
Also I see that you are not using try/catch/finally block while creating
connection.I strongly suggest to close the connection in finally
clause otherwise you program may suffer from connection leak if any
exception is raised and causes abrupt behavior.
Ideally you should close the connection after your operation is complete in finally
clause.In web based application if you are using connections pool
then closing connection will return the connection back to pool and
will be available for use.