We have a system whereby the database connection is get once using a common method, and being pass throughout the relevant class to be used. There are doubts that passing the database connection as a parameter to different classes would cause problem, so i'm checking here to see whether this is actually viable, and are there any better patterns to do it?
I know there are some ORM tools to do the persistence but we can't go into that, yet..
Any feedback is welcomed, thanks.
-
What sort of problems are you referring to? Who has these doubts? (Not you, I assume.)Greg Hewgill– Greg Hewgill2013年02月14日 04:08:03 +00:00Commented Feb 14, 2013 at 4:08
-
1Problems like causing developer forgetting to close the connection, generally i'm just trying to see whether is it a good practice to pass around the database connection to various methods as a parameter. Ya the doubts comes from another developer.ipohfly– ipohfly2013年02月14日 09:28:12 +00:00Commented Feb 14, 2013 at 9:28
5 Answers 5
Yes it is safe to pass around a connection. You handle the connection in an outer controlling block. There is nothing unsafe about it.
What is unsafe is writing code that does not guarantee the connection is properly disposed in a timely manner. Forgetting to clean up a resource is unrelated to passing it around. You could just as easily write code that leaves a hanging connection without passing it anywhere.
In C++, you are protected by RAII if you allocate on the stack or use smart pointers. In C# make a hard rule that all disposable objects (such as connections) be declared in a "using" block. In Java clean up with try-finally logic. Have code reviews on all data layer code to ensure this.
The most common use-case is when you have several operations that can be combined in many permutations. And each of these permutations need to be an atomic-transaction (all succeed or rollback). then you must pass the transaction (and therefore the corresponding connection) around to all the methods.
Suppose we have many foobar() actions that can be combined in various ways as atomic-transactions.
//example in C#
//outer controlling block handles clean up via scoping with "using" blocks.
using (IDbConnection conn = getConn())
{
conn.Open();
using (IDbTransaction tran = conn.BeginTransaction())
{
try
{//inner foobar actions just do their thing. They don't need to clean up.
foobar1(tran);
foobar2(tran);
foobar3(tran);
tran.Commit();
}
catch (Exception ex)
{ tran.Rollback(); }
}
}//connection is returned to the pool automatically
BTW you will want to open connections as late as possible, dispose them as soon as possible. Your teammates could be right if you are treating connections as object members, introducing them as unnecessary state, and leaving connections open much longer than necessary. But the act of passing a connection or transaction as a parameter is not inherently wrong.
BTW. Depending on your language's support for first class functions you may take in a list of foobar() actions. So one function could handle all permutations of the actions. Eliminating duplication of the outer controlling block for each permutation.
-
marking this as the answer as it gives me more idea on how the situation isipohfly– ipohfly2013年03月01日 06:50:26 +00:00Commented Mar 1, 2013 at 6:50
It sounds like you're after Dependency Injection. That is, the pooled connection gets created once and injected whereever it's needed. Certainly passing in the connection via a method parameter is one way to dependency inject, but an IoC container such as Guice, PicoContainer or Spring is another (safer) way you can do this.
Using DI means you can neatly wrap up the logic around the creation, opening, usage and closing of the connection - away from your core business logic.
Spring JDBC et al are otehr examples of performing this sort of behaviour for you
-
Emm not really looking at dependency injection. Just trying to find out whether generally is it a good practice to do that, and if it's not, what is the better way of managing the database connection (DI is one way of doing it though).ipohfly– ipohfly2013年02月14日 09:29:36 +00:00Commented Feb 14, 2013 at 9:29
-
-1. One connection does not fit a multi-user system. It may appear to work due to low user volume and fast execution. With pooling, it is better to instantiate a connection object per-action even in a single user system.mike30– mike302013年02月14日 16:21:17 +00:00Commented Feb 14, 2013 at 16:21
Passing around database things rather than data things can lead to problems. To that extent, whenever it practical, don't pass a database thing unless one can guarantee proper database hygiene.
The problem with passing around database things is that it can be sloppy. I have seen more than one bug in code with someone passing around a database connection, that someone then grabs a result set to and stashes in a local object (the result set, still connected to the database) and then ties up a cursor in the database for a significant time. Another instance someone passed a result set to someone else (which was then stashed) and then method that passed the result set closed it (and the statement) leading to errors when other methods tried to work with the result set that wasn't anymore.
All of this stems from not respecting the database, the connection, the statement, the result set, and their lifecycles.
To avoid this, there are existing patterns and structures that play more nicely with databases and don't have database things need to get out of the classes they are confined in. Data goes in, data goes out, the database stays put.
-
1+1 db connections should have the shortest timespan possible. Open it, use it, close it as fast as possible. Nowadays there a plenty of connection pool implementations so using a connection for multiple operations a false economy. And an invitation for bugs or performance problems (holding locks on tables, using connection resources)jqa– jqa2013年02月14日 16:05:12 +00:00Commented Feb 14, 2013 at 16:05
-
What are the names of some of these existing patterns and structures?Daniel Kaplan– Daniel Kaplan2013年02月14日 19:26:59 +00:00Commented Feb 14, 2013 at 19:26
-
@tieTYT The primary ones that comes to the forefront is the Data access object which serves to hide the database from the rest of the application. See also Data Access Layer and Object-Relational Mappinguser40980– user409802013年02月14日 19:41:13 +00:00Commented Feb 14, 2013 at 19:41
-
When I think of those patterns I feel they're at a higher level of abstraction than what he's asking about. Let's say you've got a way to get a
Root
from a Dao. But then you realize you also want a way to get aNode
without pulling out the wholeRoot
object with it. How do you make theRoot
Dao calls theNode
Dao code (ie: reuse), but make sure theNode
Dao only closes the connection when theNode
Dao is directly called and keeps the connection open when theRoot
Dao is called?Daniel Kaplan– Daniel Kaplan2013年02月14日 19:47:48 +00:00Commented Feb 14, 2013 at 19:47 -
1Just wanted to add that if you're not in auto-commit mode, passing a connection around could lead to a situation where one object updates the database, then another (possibly unrelated) object gets the connection, has an error, and winds up rolling back the first object's changes. These types of errors can be very difficult to debug.TMN– TMN2013年02月14日 21:44:08 +00:00Commented Feb 14, 2013 at 21:44
Passing Connection
instances around is not usually a problem, even though in most situation only the DAO implementations should have anything to do with them. Now, with your problem being about connections not being closed after used, it is actually easy to fix: the Connection
object needs to be closed at the same level it is opened, i.e. in the same method. I personally use the following code pattern :
final Connection cnx = dataSource.getConnection();
try {
// Operations using the instance
} finally {
cnx.close();
}
That way I ensure all connections are always closed, even if an exception is thrown within the block. I actually go as long as using the exact same pattern for Statement
and ResultSet
instances, and everything has been smooth sailing so far.
Edit 2018年03月29日: As indicated by user1156544 in the comments below, starting with Java 7 the use of the try-with-resources construct should be favoured. Using it, the code pattern I provided in my initial answer can be simplified like so:
try (final Connection cnx = dataSource.getConnection()) {
// Operations using the instance
}
-
1I use something similar. I have function doInTransaction(DbTask task), where DbTask is my interface with method with connection parameter. doInTransaction obtains connection, calls task and commit (or rollback if there was exception) and close that connection.user470365– user4703652013年02月15日 08:46:45 +00:00Commented Feb 15, 2013 at 8:46
-
judging from your example, it would mean that the DataSource object is a singleton?ipohfly– ipohfly2013年02月15日 09:48:53 +00:00Commented Feb 15, 2013 at 9:48
-
@ipohfly Actually I should have named that object
dataSource
rather thanDataSource
(I'll fix my answer regarding that point). The exact type of that object would bejavax.sql.DataSource
. In old code I used to have a singleton manage all the available data sources within my applications. My DAOs did not have to know about that through, as theDataSource
instance is provided through dependency injection.KevinLH– KevinLH2013年02月15日 19:53:21 +00:00Commented Feb 15, 2013 at 19:53 -
If you use this schema, use try-with-resources betteruser1156544– user11565442018年03月29日 01:47:23 +00:00Commented Mar 29, 2018 at 1:47
-
Back when I answered, I wasn't yet using Java 7. But you are right that this should be the preferred way these days. I'll update my answer to include your suggestion.KevinLH– KevinLH2018年03月29日 08:49:01 +00:00Commented Mar 29, 2018 at 8:49
there's a tradeoff to doing things this way rather than using a singleton which you can get as needed. I have done things both ways in the past.
In general, you need to think about the consequences of database connection management, and this may or may not be orthogonal to database query usage. For example, if you have one db connection for a given application instance and it gets closed when not in use, that would be orthogonal. Put the management in a singleton class and don't pass it around. This allows you to manage the db connection as you need. For example, if you want to close a connection on every commit (and re-open on the next call) this is easier to do on a singleton because the API for this can be centralized.
On the other hand, suppose you need to manage a pool of connections where a given call may need to use any arbitrary connection. This might happen when doing distributed transactions across multiple servers, for example. In this case you are usually far better off passing the db connection object than you are working with singletons. I think this is usually the rarer case, but there isn't anything wrong with doing it when you need to.
Explore related questions
See similar questions with these tags.