So I am trying to understand how to build connection pools and to connect via java to an Oracle DB. i am trying to use dbcp2 to learn more about how all of this works. If I use the BasicDataSource I can connect and I see 5 connections via the number I set up when I created the pools like this.
private static BasicDataSource getDataSource() {
{
if (dataSource == null)
{
BasicDataSource ds = new BasicDataSource();
ds.setUrl("jdbc:oracle:thin:#pdb_tac");
ds.setUsername("hr");
ds.setPassword("my_password");
//ds.setDriverClassName("oracle.jdbc.driver.OracleDriver");
ds.setDriverClassName("oracle.jdbc.replay.OracleDataSourceImpl");
//ds.setDriverClass("oracle.jdbc.replay.OracleDataSourceImpl");
ds.setDefaultAutoCommit(false);
ds.setInitialSize(5);
ds.setMinIdle(5);
ds.setMaxIdle(10);
ds.setMaxOpenPreparedStatements(100);
dataSource = ds;
}
return dataSource;
}
}
Well, my issue is I need to be able to create a PoolingDataSource to deal with connecting to an Oracle RAC for high availability.
So I create my data source like this.
private static DataSource setupDataSource(){
String connectURI = "jdbc:oracle:thin:hr/my_password#pdb_tac";
ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(connectURI,null);
PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory,null);
ObjectPool objectPool = new GenericObjectPool(poolableConnectionFactory);
poolableConnectionFactory.setPool(objectPool);
PoolingDataSource dataSource = new PoolingDataSource(objectPool);
return dataSource;
}
Well when I connect I only see 1 connection to the database not 5 like I do when I use BasicDataSource. Can someone help me understand what I am doing wrong? Thanks.
Related
In Spring Boot, does jdbcTemplate not close the connection automatically once after the it executes the query?
In this case, I am executing a query using jdbcTemplate(where it connects to teradata) but the session is not closing after the query is executed. How can I close the session?
This is my dao file -
#Component
public class DDLReviewDao {
#Autowired
#Qualifier("devbJdbc")
private JdbcTemplate jdbcTemplate;
public static final Logger logger = LogManager.getLogger(DDLReviewDao.class);
public List<DDLObject> getDDLReviewData(DDLQuery ddlQuery) {
String selectSql = MacroGenerator.generateMacro(ddlQuery);
List<DDLObject> ddlObject = jdbcTemplate.query(selectSql, new DDLMapper());
logger.info(ddlObject);
return ddlObject;
}
}
JdbcTemplate gets its connections from javax.sql.DataSource implementation - which is passed to its constructor link.
The DataSources can be basic (creates Connection object for each request) or pooling (has pool of connections and just 'borrows' one for given request's use).
So, it appears that the connection is not closing because you have passed some pooling datasource to JdbcTemplate named devbJdbc. If you realy want to close every connection opened to do the JdbcTemplate job, you can use a basic DataSource implementation: org.springframework.jdbc.datasource.SimpleDriverDataSource just like that:
#Configuration
class DevbConfig {
#Bean(name = "devbDataSource")
DataSource devbDataSource() {
try {
return new SimpleDriverDataSource(DriverManager.getDriver("jdbc:..."), "jdbc:...", "username", "password");
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
#Bean(name = "devbJdbc")
JdbcTemplate devbJdbc(#Qualifier("devbDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
In Spring Boot, does jdbcTemplate not close the connection
automatically once after the it executes the query?
Should it close the connection or return it to the connection pool (in case the DataSource is pooled)?
If you read the source code at http://grepcode.com/file/repo1.maven.org/maven2/org.springframework/spring-jdbc/4.1.7.RELEASE/org/springframework/jdbc/core/JdbcTemplate.java#JdbcTemplate.execute%28org.springframework.jdbc.core.StatementCallback%29it boils down to:
public static void doReleaseConnection(Connection con, DataSource dataSource) throws SQLException {
if (con == null) {
return;
}
if (dataSource != null) {
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && connectionEquals(conHolder, con)) {
// It's the transactional Connection: Don't close it.
conHolder.released();
return;
}
}
logger.debug("Returning JDBC Connection to DataSource");
doCloseConnection(con, dataSource);
}
and
public static void doCloseConnection(Connection con, DataSource dataSource) throws SQLException {
if (!(dataSource instanceof SmartDataSource) || ((SmartDataSource) dataSource).shouldClose(con)) {
con.close();
}
}
Most-likely, if the DataSource instance is pooled, connections are release back for reuse and not closed.
According to the spring boot docs you can allocate a maximum number of connections to a spring pool like so:
spring.datasource.tomcat.max-active=50
This will obviously only work with the embedded webserver. If you are deploying it to something like a Jboss you'll have to configure that property in the Server config file.
Created a Datasource class based on C3P0 ComboPooledDataSource class. It works with testing code but won't work in servlet under tomcat container. Does anyone know why?
class Datasource{
private ComboPooledDataSource cpds;
private Datasoruce datasource;
private Datasource(){
//setup cpds
cpds.new ComboPooledDataSource();
cpds.setUser("abcd");
...
}
public static getInstance(){
if( datasource == null ) datasource = new Datasource();
return datasource;
}
public Connection getConnection(){
return cpds.getConnection();
}
}
//test code works if put in indepedent main() with multithreading.
Datasource ds = Datasource.getInstance();
Connection conn = ds.getConnection();
//Above test code won't work if put inside servlet doGet() with tomcat.
The error is "An attempt by a client to checkout a Connection has timed out right at the line of getConnection().
I'm using postgres jdbc driver to connect to Amazon RedShift. Here are also BasicDataSource from DBCP 2.0.1 and JdbcTemplate from Spring 4. I use DataSourceTransactionManager with Transactional annotations.
It looks like DataSource still keeps on creating new connections!
// that is how dataSource is created
BasicDataSource dataSource = new BasicDataSource() {
public Connection getConnection() throws SQLException {
Connection c = super.getConnection();
System.out.println("New connection: " + c);
return c;
}
};
dataSource.setUsername(env.getProperty(USERNAME));
dataSource.setPassword(env.getProperty(PASSWORD));
dataSource.setDriverClassName(env.getProperty(DRIVER_CLASS));
dataSource.setUrl(env.getProperty(CONNECTION_URL));
and I see in console for each operation another Connection object (they have different hashcodes). If I switch to SingleConnectionDataSource all works as expected, with a single connection object.
Before call to jdbcTemplate#execute I use TransactionSynchronizationManager.isActualTransactionActive to see that transactions are working (they are)...
What could I miss then? Why transactions are closed? Or what more can I do to investigate the problem. The url also have tcpKeepAlive=true parameter...
UPD thanks to Evgeniy, I've changed the code to see when connections are really created:
BasicDataSource dataSource = new BasicDataSource() {
protected ConnectionFactory createConnectionFactory() throws SQLException {
final ConnectionFactory cf = super.createConnectionFactory();
return new ConnectionFactory() {
public Connection createConnection() throws SQLException {
Connection c = cf.createConnection();
System.out.println("New connection from factory: " + c);
return c;
}
};
}
};
//dataSource.setMaxIdle(0);
Now I really see that only two connections were created (and if I add setMaxIdle(0) they are instead recreated before each query).
So my suspicion was wrong and pool works as expected. Thanks a lot!
Different hash codes do not prove they are different physical connections. Try to watch sessions on the database and you will see that close on connection from BasicDataSource does not close a physical connection.
I have a server that submits a query to a remote host to access account information from a database to log into a gameserver. The problem is the connections time out randomly however, I am using DataSource which should automatically re-establish any lost connections. Anyone have a clue how to resolve the issue?
public class Database {
private final PoolingDataSource source;
public Database(String driver, String url, String user, String password)
throws ClassNotFoundException {
Class.forName(driver);
source = createSource(url, user, password);
}
#SuppressWarnings({ "rawtypes", "unchecked", "unused" })
private PoolingDataSource createSource(String connectURI, String user,
String password) {
GenericObjectPool.Config config = new GenericObjectPool.Config();
config.maxActive = 150;
config.maxIdle = 100;
config.minIdle = 30;
config.maxWait = 1000;
config.testWhileIdle = true;
ObjectPool connectionPool = new GenericObjectPool(null, config);
ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
connectURI, user, password);
PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(
connectionFactory, connectionPool, null, null, false, true);
PoolingDataSource poolingDataSource = new PoolingDataSource(
connectionPool);
return poolingDataSource;
}
public Connection getConnection() throws SQLException {
return getSource().getConnection();
}
public PoolingDataSource getSource() {
return source;
}
}
"Back in the day...", in 2003, I was working with Tomcat 4.1 and was unpleasantly surprised to find that its implementation of DataSource required you to give it a validationQuery or else it would initialize the Connection only once and never verify that database server side would still respect the connection. Our Oracle server would simply eliminate connections that hadn't been used for, I think, 60 minutes. The effect of this was that as my server went to low volume, some number of my pooled connections would get killed on the database server side, but the application server didn't know, until it tried to use them at higher volumes. At this point they just stacktraced and that was the end of it. Adding the validationQuery had the effect of the DataSource itself keeping the connection alive and everything just worked. The gory details of this discovery process is here https://groups.yahoo.com/neo/groups/seajug/conversations/messages/4902 , if you are interested.
I recommend that you check if your GenericObjectPool implementation has a concept of validation query or heartbeat or something and figure out how to leverage it to keep your connections "fresh".
I'm trying to create my first connection pool. I'm creating a Java web aplication with Tomcat 7 and a MySQL DB, and I'd like to create the simplest connection pool possible.
I've taken a look at several tutorials but it's not really clear for me, so I'd like you to confirm if I'm doing well.
I've written the following class as a connection pool manager:
package dao.mysql;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
public class MySQLConnectionPool {
private static DataSource datasource;
private static String driver = "com.mysql.jdbc.Driver";
private static String url = "jdbc:mysql://localhost:3306/mydb";
private static String username = "user";
private static String password = "password";
public MySQLConnectionPool() {
datasource = new DataSource(configurePoolProperties(driver, url, username, password));
}
private PoolProperties configurePoolProperties(String driver, String url, String username, String password) {
PoolProperties properties = new PoolProperties();
properties.setDriverClassName(driver);
properties.setUrl(url);
properties.setUsername(username);
properties.setPassword(password);
return properties;
}
public static synchronized Connection getConnection() {
Connection connection = null;
try {
connection = datasource.getConnection();
} catch (SQLException ex) {
System.out.println("Error while getting a connection from the pool! \nSQL state:" + ex.getSQLState() + "\nMESSAGE" + ex.getMessage());
}
return connection;
}
}
I'm not sure about the static properties nor the synchronized.
And I'm not sure about the "client" classes of the pool. I understand they have only to get a connection using
Connection con = MySQLConnectionPool.getConnection();
and finally close this connection using
con.close();
And that's it?
And also, is there any simpler or better way to do this?
Thanks very much!
This is the wrong way to do it.
Tomcat already has a connection pool and you can configure and setup without any code through the context.xml in the conf directory.
Once it is defined there, all you need to do is to lookup the JNDI DataSource in your code. Hardcoding all that (and re-inventing the wheel) is a very bad idea.
To learn how to configure a JNDI DataSource check out the manual: http://tomcat.apache.org/tomcat-7.0-doc/jndi-datasource-examples-howto.html
The Tomcat manual also has an example on how to obtain a connection from the pool:
InitialContext cxt = new InitialContext();
DataSource ds = (DataSource) cxt.lookup( "java:/comp/env/jdbc/dsname" );
where dsname is the name you provided in the context.xml
Check out the JNDI Datasource HOW-TO and the Tomcat JDBC Connection Pool Tomcat documentation. Letting Tomcat do it is preferable especially since it avoids class loader leaks.