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?
To create and deploy a DataSource object, you need to perform these tasks: Create an instance of the appropriate DataSource implementation. Set the properties of the DataSource object. Register the object with the Java Naming and Directory Interface (JNDI) naming service.
For an environment without an application server, PostgreSQL provides two implementations of DataSource which an application can use directly. One implementation performs connection pooling, while the other simply provides access to database connections through the DataSource interface without any pooling.
A Data Source object enables JDBC applications to obtain a DBMS connection from a connection pool. Each Data Source object binds to the JNDI tree and points to a connection pool or MultiPool. Applications look up the Data Source on the JNDI tree and then request a connection from the Data Source.
The PGSimpleDataSource
class bundled with the JDBC driver from jdbc.postgresql.org implements DataSource
interface. Configure your database connection details in a PGSimpleDataSource
object, and pass around as a DataSource
object.
PGSimpleDataSource ds = new PGSimpleDataSource() ;
ds.setServerName( "localhost" );
ds.setDatabaseName( "your_db_name_here" );
ds.setUser( "scott" );
ds.setPassword( "tiger" );
Use that object to make a connection to the database as needed. Use convenient try-with-resources syntax.
try
(
Connection conn = ds.getConnection() ;
)
{ … }
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:
Up to three kinds of implementation provided may be available:
DriverManager
. Each time you call DataSource::getConnection
on the object of such an implementation, you get a fresh database connection.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
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 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With