The JDBC Tutorial recommends using a DataSource
object to obtain database connections rather than using the DriverManager
class. To quote the Connecting with DataSource Objects page:
DataSource
objects … the preferred means of getting a connection to a data source.
How do I get such an object for a JDBC connection to Postgres? I have a JDBC driver in place.
Right now, I do not want to fiddle around with JNDI like this or this.
Can I instantiate a DataSource
programmatically within my Java app? Or must I implement that DataSource
interface myself?
JDBC driver’s implementation
Your JDBC driver may provide for you an implementation of the DataSource
interface.
An object of this implementation contains the information needed to make and configure a connection to the database, such as:
- Name & password of database user
- IP address & port number of database server
Up to three kinds of implementation provided may be available:
- Often such an implementation is a thin wrapper around the
DriverManager
. Each time you call DataSource::getConnection
on the object of such an implementation, you get a fresh database connection.
- Alternatively, an implementation may be using a connection pool underneath to supply already-existing connections. These connections are handed out and checked back in, like books in a library, to be recycled for repeated use.
- An implementation may support the Java Transaction API, supporting X/Open XA, for sophisticated needs like coordinating the transactions across multiple resources such as databases and message queues. Not as commonly used, so I ignore this type here.
Driver from jdbc.postgresql.org
The open-source free-of-cost driver from jdbc.postgresql.org provides all three types of DataSource
implementation. But the authors do not recommend actually using their connection pool type in production; if you want pooling, use a third-party connection pooling library. And we are ignoring the XA type.
So let's look at the simple fresh-connection-each-time implementation of DataSource
: org.postgresql.ds.PGSimpleDataSource
Configuring the data source object
Instantiate an empty object, then call a series of setter methods to configure for your particular database scenario. The setter methods are inherited from org.postgresql.ds.common.BaseDataSource
.
We are not yet upcasting to the interface DataSource
, so that we can call the various setter methods. See example code and discussion on the Data Sources and JNDI page.
PGSimpleDataSource ds = new PGSimpleDataSource() ; // Empty instance.
ds.setServerName( "localhost" ); // The value `localhost` means the Postgres cluster running locally on the same machine.
ds.setDatabaseName( "testdb" ); // A connection to Postgres must be made to a specific database rather than to the server as a whole. You likely have an initial database created named `public`.
ds.setUser( "testuser" ); // Or use the super-user 'postgres' for user name if you installed Postgres with defaults and have not yet created user(s) for your application.
ds.setPassword( "password" ); // You would not really use 'password' as a password, would you?
Generally I would use these separate setter methods. Alternatively, you construct a String, a URL, with the various pieces of info to be set on the DataSource
in one stroke. If you want to go that route, call setUrl
.
That covers the basics. But you might want or need some of the other setters. Most of these are setting Postgres property values on the server. The properties all have smart defaults, but you may wish to override for special situations.
ds.setPortNumber( 6787 ) ; // If not using the default '5432'.
ds.setApplicationName( "whatever" ) ; // Identify the application making this connection to the database. Also a clever way to back-door some information to the Postgres server, as you can encode small values into this string for later parsing.
ds.setConnectTimeout( … ) ; // The timeout value used for socket connect operations, in whole seconds. If connecting to the server takes longer than this value, the connection is broken.
ds.setSocketTimeout( … ) ; // The timeout value used for socket read operations. If reading from the server takes longer than this value, the connection is closed. This can be used as both a brute force global query timeout and a method of detecting network problems.
ds.setReadOnly( boolean ) ; // Puts this connection in read-only mode.
If using TLS (formerly known as SSL) to encrypt the database connection to protect against eavesdropping or malevolent manipulation, use several setters for that.
For any Postgres property without a specific setter method, you may call setProperty( PGProperty property, String value )
.
You can inspect or verify settings on this data source by calling any of the many getter methods.
After configuring your PGSimpleDataSource
, you can pass off to the rest of your codebase as simply a DataSource
object. This insulates your codebase from the shock of changing to another DataSource
implementation or changing to another JDBC driver.
DataSource dataSource = ds ; // Upcasting from concrete class to interface.
return dataSource ;
Using the data source
Using a DataSource
is utterly simple as it provides for only two methods, a pair of variations on getConnection
to get a Connection
object for your database work.
Connection conn = dataSource.getConnection() ;
When finished with your Connection
, best practice is to be sure to close it. Either use a try-with-resources syntax to automatically close the connection, or explicitly close it.
conn.close() ;
Keep clear in your mind that a DataSource
is not actually a data source. A DataSource
is really a source for generating/accessing connections to the database. To my mind, this is a misnomer, as I think of it as ConnectionSource
. The DataSource
talks to your database only long enough to sign-in with user name and password. After that sign-in, you use the Connection
object to interact with the database.
Storing your DataSource
Once configured, you want to keep that DataSource
object around, cached. No need to re-configure repeatedly. The implementation should be written to be thread-safe. You may call getConnection
at anytime from anywhere.
For a simple little Java app, you may want to store it as a field on a singleton or in a static global variable.
For a Servlet-based app such as a Vaadin app, you would create a class implementing ServletContextListener
interface. In that class your would establish your DataSource
object when your web app is launching. From there you would store the object in the ServletContext
object by passing to setAttribute
. Context
is the technical term for 'web app'. Retrieve by calling getAttribute
and casting to DataSource
.
In an enterprise scenario, the DataSource
may be stored in a JNDI-compliant implementation. Some Servlet containers such as Apache Tomcat may provide a JNDI implementation. Some organizations use a server such as an LDAP server. Registering & retrieving your DataSource
object with JNDI is covered in many other Questions & Answers on Stack Overflow.