Unit test cannot delete database due to open JDBC connection - java

We're making a small Java app using unit test classes which may not be modified. The app uses an SQLite database, and one test class, DAOTest, tests the DAO class. When run individually, all of the tests in DAOTest are passed, however when run one after another only one is passed, the others fail.
DAO.java:
import java.sql.*;
public class DAO {
private Connection conn = null;
private static final String URL = "jdbc:sqlite:database.db";
public DAO() {
try {
conn = DriverManager.getConnection(URL);
} catch (SQLException e) {
e.printStackTrace();
}
}
public void close() {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
// methodA(), methodB(), ...
}
DAOTest.java
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import static org.junit.jupiter.api.Assertions.fail;
public class DAOTest {
DAO dao = null;
/**/
#Test
void initDb() {
if (dao != null) {
dao.close();
}
File dbfile = new File("database.db");
ClassLoader classLoader = getClass().getClassLoader();
File srcfile = new File(classLoader.getResource("db/databaseToBeCopied.db").getFile());
Boolean isDbDeleted = false;
try {
isDbDeleted = dbfile.delete();
Files.copy(srcfile.toPath(), dbfile.toPath());
} catch (IOException e) {
e.printStackTrace();
fail("Database deleted: " + isDbDeleted);
}
dao = new DAO();
}
#Test
void testA() {
initDb();
// Call dao.methodA()...
}
#Test
void testB() {
initDb();
// Call dao.methodB()...
}
}
I understand what is causing of the problem. initDb() is called at the start of every test, to copy databaseToBeCopied.db to database.db. The copy function expects the database.db file not to already exist, or it throws a FileAlreadyExistsException. Because the dao variable in DAOTest.java is a non-staticattribute, it is null at every invocation of initDb(), so dao.close() will not be called, and the connection to the database will not get closed. This now disables the deletion of database.db, so the copy functions throws the exception, and the tests fail. I cannot modify the test class.
The only possible solution I could think of is to open and close the database connection in every single method of DAO.java, so the close() function would not be required. There are a couple of problems with this approach:
I don't know how efficient this would be
DAO.java methods can call each other, which might cause complications
It seems like a bad practice at first sight
Could this be solved by another approach, perhaps with multi-threading? This is one of the topics we've shortly covered, however I'm not very familiar with it, so I don't know how, or if it could be done at all.

Related

Force static block to be executed before start() method

I am using JavaFX for my project and I have two classes - MainApp class and Database class.
Very simplified implementation would look like this:
public class MainApp extends Application {
#Override
public void start(Stage stage) throws Exception {
// Getting username & password doing some initialization, etc.
Database.setUserName(username);
Database.setPassword(password);
Database.testConnection();
}
// This method was pretty much generated by IDE
public static void main(String[] args)
{
launch(args);
}
}
Only relevant part of Database class implementation is as follows (note that I have declared and implemented variables that appear in mentioned methods, I just don't paste them here to keep the code short)
public class Database {
private static OracleDataSource dataSource;
static {
try {
dataSource = new OracleDataSource();
dataSource.setURL("myjdbcaddress");
dataSource.setUser(userName);
dataSource.setPassword(password);
System.out.print("Static block executed...");
}
catch (SQLException e)
{
System.out.print("Static block caught...");
throw new ExceptionInInitializerError("Initial Database Connection not established. Sorry.");
}
}
public static Connection getConnection()
{
Connection conn = null;
try
{
conn = dataSource.getConnection();
if (conn != null)
isConnected = true;
}
catch (SQLException e)
{
e.printStackTrace();
}
return conn;
}
}
I am getting null pointer exception because of this: static block in Database class is executed after overridden start() method. Therefore, when I access properties of Database class, they are not initialized yet.
Is there a way to force call static block before start method? Did I choose wrong approach? Should I start working with database somewhere else than start() method?
I am getting null pointer exception because of this: static block in Database class is executed after overridden start() method. Therefore, when I access properties of Database class, they are not initialized yet.
No this is not the issue. The static initializer is executed when the class is loaded, which should happen right before (It's always done before anything other than a static constant in the class is used.)
Database.setUserName(username);
or earlier.
The problem probably is that the userName and password not being assigned yet (although it's hard to tell without more code).
I don't recommend using static data to pass information but instead design the application in a way that allows access to a non-static object for communication with the database where it's needed.
However you could fix your problem by moving the code from the static initializer to a static method:
public class Database {
private static OracleDataSource dataSource;
public static void login(String userName, String password) {
try {
dataSource = new OracleDataSource();
dataSource.setURL("myjdbcaddress");
dataSource.setUser(userName);
dataSource.setPassword(password);
System.out.print("Static block executed...");
} catch (SQLException e) {
throw new IllegalStateException("Initial Database Connection not established. Sorry.", e);
}
}
...
}
Database.login(username, password);
Database.testConnection();
But again: Try to avoid using such a Database class that allows access from everywhere.
BTW: If you need to initialize something before the start method of the Application runs, it should be done in a overriden init() method of the application class.

How do i control the order in which jUnit tests run [duplicate]

This question already has answers here:
How to run test methods in specific order in JUnit4?
(23 answers)
Closed 7 years ago.
I have this jUnit test class
public class TestRaavareBatch {
#Before
public void prep(){
try { new Connector(); }
catch (InstantiationException e) { e.printStackTrace(); }
catch (IllegalAccessException e) { e.printStackTrace(); }
catch (ClassNotFoundException e) { e.printStackTrace(); }
catch (SQLException e) { e.printStackTrace(); }
}
MySQLRaavareBatchDAO rvb = new MySQLRaavareBatchDAO();
#Test
public void testgetRaavareBatch() throws DALException{
RaavareBatchDTO rvbOBJ = rvb.getRaavareBatch(7);
assertEquals(7, rvbOBJ.getRaavareId());
assertEquals(100.0, rvbOBJ.getMaengde(),0.0);
assertEquals(7, rvbOBJ.getRbId());
}
#Test
public void testgetRaavareBatchList() throws DALException{
List<RaavareBatchDTO> rvbOBJ = rvb.getRaavareBatchList();
assertEquals(rvbOBJ.size(), 8);
}
#Test
public void testgetRaavareBatchListId() throws DALException{
List<RaavareBatchDTO> rvbOBJ = rvb.getRaavareBatchList(5);
assertEquals(rvbOBJ.size(), 2);
}
#Test
public void testcreateRaavareBatch() throws DALException{
RaavareBatchDTO test;
rvb.createRaavareBatch(test = new RaavareBatchDTO(8, 8, 200.0));
RaavareBatchDTO rvbOBJ = rvb.getRaavareBatch(8);
assertEquals(8, rvbOBJ.getRbId());
assertEquals(200.0, rvbOBJ.getMaengde(),0.0);
assertEquals(8, rvbOBJ.getRbId());
}
#Test
public void testupdateRaavareBatch() throws DALException{
RaavareBatchDTO test;
rvb.updateRaavareBatch(test = new RaavareBatchDTO(8, 7, 100.0));
RaavareBatchDTO rvbOBJ = rvb.getRaavareBatch(8);
assertEquals(7, rvbOBJ.getRaavareId());
assertEquals(100.0, rvbOBJ.getMaengde(),0.0);
}
}
It connects to a database with 7 rows, and after i run the last test "updateRaavareBatch" i have created a new row so the size of the list in testgetRaavareBatchList() will be 8. But it gives me an error because it counts the size before i create a new row..
How can i run testgetRaavareBatchList() after i create the new row and update it.
I once got something like that in testing queries, insertions and deletions in a database.
I ended with the following infra in order to ensure test independance :
prepare the database connection in a #Before method
rollback in #After
put some inserts in private not #Test annotated utility methods to avoid duplication
when needed the #Test methods called utility methods, and did their job with assertions
In another harder case, I created an embedded database in a #BeforeClass method and destroyed it in #AfterClass
But you should never rely on test order.
You can use #FixedMethodOrder annotation on your test class.
A simple example is the following:
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
//Running test cases in order of method names in ascending order
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class OrderedTestCasesExecution {
#Test
public void secondTest() {
System.out.println("Executing second test");
}
#Test
public void firstTest() {
System.out.println("Executing first test");
}
#Test
public void thirdTest() {
System.out.println("Executing third test");
}
}
Output:
Executing first test
Executing second test
Executing third test
Just one thing about your particular test scenario though. It is better in your case to have a proper #Before and #After methods to setup and rollback database tests. Later on, if your codebase is big enough you might run into cases where one test does not clean up properly and makes another random testcase fail.
References:
Simple TestCase source
JUnit Javadoc for the #FixMethodOrder
Another decent page on JUnit

Thinking about unit tests structure

I am thinking about how to write tests for my project. At the moment, tests structure is like this:
RealClass
{
method1;
method2;
...
}
and exactly same test class structure:
TestClass {
testMethod1;
testMethod2;
...
}
But, I do not like it, because I am putting too much test cases in one test method...
May be I should use structure like this:
TestClass {
testMethod1Opt1;
testMethod1Opt2;
...
testMethod2Opt1;
...}
How are you writing Unit tests?
Example of my test code: (Very simple test)
public void testIsAppUser() {
// My (Artem`s Zinnatullin) uId
final long artemZinnatullinUId = 172672179;
try {
assertTrue(usersApi.isAppUser(artemZinnatullinUId));
} catch (Exception e) {
fail(e.getMessage());
}
// Pavel`s Durov uId
final long durovUId = 1;
try {
assertFalse(usersApi.isAppUser(durovUId));
} catch (Exception e) {
fail(e.getMessage());
}
// By default uId == current user`s (who has authorized) uId
try {
assertTrue(usersApi.isAppUser(null));
} catch (Exception e) {
fail(e.getMessage());
}
}
What I am thinking about:
public void testIsAppUser1() {
// My (Artem`s Zinnatullin) uId
final long artemZinnatullinUId = 172672179;
try {
assertTrue(usersApi.isAppUser(artemZinnatullinUId));
} catch (Exception e) {
fail(e.getMessage());
}
}
public void testIsAppUser2() {
// Pavel`s Durov uId
final long durovUId = 1;
try {
assertFalse(usersApi.isAppUser(durovUId));
} catch (Exception e) {
fail(e.getMessage());
}
}
public void testIsAppUser3() {
// By default uId == current user`s (who has authorized) uId
try {
assertTrue(usersApi.isAppUser(null));
} catch (Exception e) {
fail(e.getMessage());
}
}
Give me advice please.
Comments:
Instead of try{} catch(){ fail() } just add throws Exception to the test method. JUnit will automatically fail the test for you and preserve the stack trace. This will make bug fixing much easier.
Create small test methods. That creates a name problem: How to come up with lots of good names? The solution here is to name the test after what it logically tests, not which methods it calls.
If you want to see what methods are called, use a code coverage tool like JaCoCo.
So the first test should be called testIsArtemsZinnatullinAppUser(). As a guideline: Whenever you feel like you need a comment to explain what a test does, the test name is wrong. Use whatever you'd write in the comment to create a test name.
The reason why you should have smaller tests is that JUnit stops for the first problem. So if you have 20 tests in one test case and the 3rd fails, 17 tests won't run. But these 17 tests could contain valuable information helping to figure out what is wrong.
If they all succeed, then this is probably a specific problem. If many tests fail, the problem must be in shared code.
Your second way of structuring the tests is a lot better. That way each test method covers a different way for the method to break, so you won't have the case where you fix one bug in a method, then have the test fail a little further down (so that one error prevents you from seeing others). It is a lot more important that the test methods be small and sharply-focused than that the test methods map to the methods of the object being tested.
Also, don't catch exceptions, JUnit will do that for you. Add throws Exception to the signature of each of your test methods. If you want to check that an exception is really thrown, then you can catch it in a test, like:
try {
objectUnderTest.doSomethingThatShouldThrowFooException();
fail("should've thrown an exception before getting here");
}
catch (FooException fooEx) {
// yay. my test passed
}
, but the boilerplate of:
} catch (Exception e) {
fail(e.getMessage());
}
is unnecessary.
I won't repeat what's in the other responses. But just adding this:
Avoid duplication of code construct in your test classes.
Don't hesitate to write explicit failure messages.
Here is something to illustrate what I mean:
public void testIsAppUser1() {
// My (Artem`s Zinnatullin) uId
assertAppUser(172672179,true,"artemZinnatullinUId");
}
public void testIsAppUser2() {
// Pavel`s Durov uId
assertAppUser(1,false,"Pavel`s Durov");
}
public void testIsAppUser3() {
// By default uId == current user`s (who has authorized) uId
assertAppUser(null,true,"current user");
}
private void assertAppUser(Long id, boolean isExpectedAppUser, String userName){
boolean isAppUser = usersApi.isAppUser(id);
if(isExpectedAppUser){
assertTrue("User with id:"+id+"and named:"+userName+" must be an appUser" ,isAppUser);
}else{
assertFalse("User with id:"+id+"and named:"+userName+" cannot be an appUser" ,isAppUser);
}
}
}
Only throw when you have an error that might happen because of an 'exception' don't necassarily throw because you can. The following assumes you are creating your own testing enviorment.
I don't know what your assert methods look like but really they should be the ones throwing if anything. You also don't need a try catch to throw an exception you can do the following:
throw new Exception("msg"); // Or another type of Exception
So implementation:
public void AssertEqual(Object obj1, Object obj2) throws Exception
{
if (!obj1.equals(obj2))
throw new Exception("Objects are not equal");
}

Make DB fail deterministically for testing

I have a Java application that uses lots of java.sql.Connection to a database.
I want to test that, if the database is unavailable, my services return the appropriate error codes (distinguishing between temporary and permanent problems e.g. HTTP 500 and 503).
For testing, my application connects to an embedded, local, in-memory h2 database; the application is not aware of this, only my integration test is.
How can I make writes to the database fail deterministically, e.g. hook into commits and make them throw a custom SQLException? I want a global 'database is unavailable' boolean in the test code that affects all connections and makes my application exercise its reconnect logic.
(I had started by proxying Connection and putting an if(failFlag) throw new MySimulateFailureException() in commit(); but this didn't catch PreparedStatement.executeUpdate(); before I embark on proxying the PreparedStatement too - its a lot of methods! - I'd like to be taught a better way...)
I think this is a good candidate for using aspects. With eg. Spring it is supremely easy to pointcut entire packages or just certain methods that you wish to fail - specifically you could have a before advice always throwing a ConnectException or do something more advanced with the around advice.
Cheers,
I ended up making my own Java reflection wrapper that intercepts Connection.commit and the PreparedStatement.execute... methods.
My final code in my 'DBFactory' class:
#SuppressWarnings("serial")
public class MockFailureException extends SQLException {
private MockFailureException() {
super("The database has been deliberately faulted as part of a test-case");
}
}
private class MockFailureWrapper implements InvocationHandler {
final Object obj;
private MockFailureWrapper(Object obj) {
this.obj = obj;
}
#Override public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
if(dbFailure && ("commit".equals(m.getName()) || m.getName().startsWith("execute")))
throw new MockFailureException();
Object result;
try {
result = m.invoke(obj, args);
if(result instanceof PreparedStatement)
result = java.lang.reflect.Proxy.newProxyInstance(
result.getClass().getClassLoader(),
result.getClass().getInterfaces(),
new MockFailureWrapper(result));
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
}
return result;
}
}
public Connection newConnection() throws SQLException {
Connection connection = DriverManager.getConnection("jdbc:h2:mem:"+uuid+";CREATE=TRUE;DB_CLOSE_ON_EXIT=FALSE");
return (Connection)java.lang.reflect.Proxy.newProxyInstance(
connection.getClass().getClassLoader(),
connection.getClass().getInterfaces(),
new MockFailureWrapper(connection));
}

How do I implement a DAO manager using JDBC and connection pools?

My problem is as follows. I need a class that works as a single point to a database connection in a web system, so to avoid having one user with two open connections. I need it to be as optimal as possible and it should manage every transaction in the system. In other words only that class should be able to instantiate DAOs. And to make it better, it should also use connection pooling! What should I do?
You will need to implement a DAO Manager. I took the main idea from this website, however I made my own implementation that solves some few issues.
Step 1: Connection pooling
First of all, you will have to configure a connection pool. A connection pool is, well, a pool of connections. When your application runs, the connection pool will start a certain amount of connections, this is done to avoid creating connections in runtime since it's a expensive operation. This guide is not meant to explain how to configure one, so go look around about that.
For the record, I'll use Java as my language and Glassfish as my server.
Step 2: Connect to the database
Let's start by creating a DAOManager class. Let's give it methods to open and close a connection in runtime. Nothing too fancy.
public class DAOManager {
public DAOManager() throws Exception {
try
{
InitialContext ctx = new InitialContext();
this.src = (DataSource)ctx.lookup("jndi/MYSQL"); //The string should be the same name you're giving to your JNDI in Glassfish.
}
catch(Exception e) { throw e; }
}
public void open() throws SQLException {
try
{
if(this.con==null || !this.con.isOpen())
this.con = src.getConnection();
}
catch(SQLException e) { throw e; }
}
public void close() throws SQLException {
try
{
if(this.con!=null && this.con.isOpen())
this.con.close();
}
catch(SQLException e) { throw e; }
}
//Private
private DataSource src;
private Connection con;
}
This isn't a very fancy class, but it'll be the basis of what we're going to do. So, doing this:
DAOManager mngr = new DAOManager();
mngr.open();
mngr.close();
should open and close your connection to the database in an object.
Step 3: Make it a single point!
What, now, if we did this?
DAOManager mngr1 = new DAOManager();
DAOManager mngr2 = new DAOManager();
mngr1.open();
mngr2.open();
Some might argue, "why in the world would you do this?". But then you never know what a programmer will do. Even then, the programmer might forger from closing a connection before opening a new one. Plus, this is a waste of resources for the application. Stop here if you actually want to have two or more open connections, this will be an implementation for one connection per user.
In order to make it a single point, we will have to convert this class into a singleton. A singleton is a design pattern that allows us to have one and only one instance of any given object. So, let's make it a singleton!
We must convert our public constructor into a private one. We must only give an instance to whoever calls it. The DAOManager then becomes a factory!
We must also add a new private class that will actually store a singleton.
Alongside all of this, we also need a getInstance() method that will give us a singleton instance we can call.
Let's see how it's implemented.
public class DAOManager {
public static DAOManager getInstance() {
return DAOManagerSingleton.INSTANCE;
}
public void open() throws SQLException {
try
{
if(this.con==null || !this.con.isOpen())
this.con = src.getConnection();
}
catch(SQLException e) { throw e; }
}
public void close() throws SQLException {
try
{
if(this.con!=null && this.con.isOpen())
this.con.close();
}
catch(SQLException e) { throw e; }
}
//Private
private DataSource src;
private Connection con;
private DAOManager() throws Exception {
try
{
InitialContext ctx = new InitialContext();
this.src = (DataSource)ctx.lookup("jndi/MYSQL");
}
catch(Exception e) { throw e; }
}
private static class DAOManagerSingleton {
public static final DAOManager INSTANCE;
static
{
DAOManager dm;
try
{
dm = new DAOManager();
}
catch(Exception e)
dm = null;
INSTANCE = dm;
}
}
}
When the application starts, whenever anyone needs a singleton the system will instantiate one DAOManager. Quite neat, we've created a single access point!
But singleton is an antipattern because reasons!
I know some people won't like singleton. However it solves the problem (and has solved mine) quite decently. This is just a way of implementing this solution, if you have other ways you're welcome to suggest so.
Step 4: But there's something wrong...
Yes, indeed there is. A singleton will create only ONE instance for the whole application! And this is wrong in many levels, especially if we have a web system where our application will be multithreaded! How do we solve this, then?
Java provides a class named ThreadLocal. A ThreadLocal variable will have one instance per thread. Hey, it solves our problem! See more about how it works, you will need to understand its purpose so we can continue.
Let's make our INSTANCE ThreadLocal then. Modify the class this way:
public class DAOManager {
public static DAOManager getInstance() {
return DAOManagerSingleton.INSTANCE.get();
}
public void open() throws SQLException {
try
{
if(this.con==null || !this.con.isOpen())
this.con = src.getConnection();
}
catch(SQLException e) { throw e; }
}
public void close() throws SQLException {
try
{
if(this.con!=null && this.con.isOpen())
this.con.close();
}
catch(SQLException e) { throw e; }
}
//Private
private DataSource src;
private Connection con;
private DAOManager() throws Exception {
try
{
InitialContext ctx = new InitialContext();
this.src = (DataSource)ctx.lookup("jndi/MYSQL");
}
catch(Exception e) { throw e; }
}
private static class DAOManagerSingleton {
public static final ThreadLocal<DAOManager> INSTANCE;
static
{
ThreadLocal<DAOManager> dm;
try
{
dm = new ThreadLocal<DAOManager>(){
#Override
protected DAOManager initialValue() {
try
{
return new DAOManager();
}
catch(Exception e)
{
return null;
}
}
};
}
catch(Exception e)
dm = null;
INSTANCE = dm;
}
}
}
I would seriously love to not do this
catch(Exception e)
{
return null;
}
but initialValue() can't throw an exception. Oh, initialValue() you mean? This method will tell us what value will the ThreadLocal variable hold. Basically we're initializing it. So, thanks to this we can now have one instance per thread.
Step 5: Create a DAO
A DAOManager is nothing without a DAO. So we should at least create a couple of them.
A DAO, short for "Data Access Object" is a design pattern that gives the responsibility of managing database operations to a class representing a certain table.
In order to use our DAOManager more efficiently, we will define a GenericDAO, which is an abstract DAO that will hold the common operations between all DAOs.
public abstract class GenericDAO<T> {
public abstract int count() throws SQLException;
//Protected
protected final String tableName;
protected Connection con;
protected GenericDAO(Connection con, String tableName) {
this.tableName = tableName;
this.con = con;
}
}
For now, that will be enough. Let's create some DAOs. Let's suppose we have two POJOs: First and Second, both with just a String field named data and its getters and setters.
public class FirstDAO extends GenericDAO<First> {
public FirstDAO(Connection con) {
super(con, TABLENAME);
}
#Override
public int count() throws SQLException {
String query = "SELECT COUNT(*) AS count FROM "+this.tableName;
PreparedStatement counter;
try
{
counter = this.con.PrepareStatement(query);
ResultSet res = counter.executeQuery();
res.next();
return res.getInt("count");
}
catch(SQLException e){ throw e; }
}
//Private
private final static String TABLENAME = "FIRST";
}
SecondDAO will have more or less the same structure, just changing TABLENAME to "SECOND".
Step 6: Making the manager a factory
DAOManager not only should serve the purpose of serving as a single connection point. Actually, DAOManager should answer this question:
Who is the one responsible of managing the connections to the database?
The individual DAOs shouldn't manage them, but DAOManager. We've answered partially the question, but now we shouldn't let anyone manage other connections to the database, not even the DAOs. But, the DAOs need a connection to the database! Who should provide it? DAOManager indeed! What we should do is making a factory method inside DAOManager. Not just that, but DAOManager will also hand them the current connection!
Factory is a design pattern that will allow us to create instances of a certain superclass, without knowing exactly what child class will be returned.
First, let's create an enum listing our tables.
public enum Table { FIRST, SECOND }
And now, the factory method inside DAOManager:
public GenericDAO getDAO(Table t) throws SQLException
{
try
{
if(this.con == null || this.con.isClosed()) //Let's ensure our connection is open
this.open();
}
catch(SQLException e){ throw e; }
switch(t)
{
case FIRST:
return new FirstDAO(this.con);
case SECOND:
return new SecondDAO(this.con);
default:
throw new SQLException("Trying to link to an unexistant table.");
}
}
Step 7: Putting everything together
We're good to go now. Try the following code:
DAOManager dao = DAOManager.getInstance();
FirstDAO fDao = (FirstDAO)dao.getDAO(Table.FIRST);
SecondDAO sDao = (SecondDAO)dao.getDAO(Table.SECOND);
System.out.println(fDao.count());
System.out.println(sDao.count());
dao.close();
Isn't it fancy and easy to read? Not just that, but when you call close(), you close every single connection the DAOs are using. But how?! Well, they're sharing the same connection, so it's just natural.
Step 8: Fine-tuning our class
We can do several things from here on. To ensure connections are closed and returned to the pool, do the following in DAOManager:
#Override
protected void finalize()
{
try{ this.close(); }
finally{ super.finalize(); }
}
You can also implement methods that encapsulate setAutoCommit(), commit() and rollback() from the Connection so you can have a better handling of your transactions. What I also did is, instead of just holding a Connection, DAOManager also holds a PreparedStatement and a ResultSet. So, when calling close() it also closes both. A fast way of closing statements and result sets!
I hope this guide can be of any use to you in your next project!
I think that if you want to do a simple DAO pattern in plain JDBC you should keep it simple:
public List<Customer> listCustomers() {
List<Customer> list = new ArrayList<>();
try (Connection conn = getConnection();
Statement s = conn.createStatement();
ResultSet rs = s.executeQuery("select * from customers")) {
while (rs.next()) {
list.add(processRow(rs));
}
return list;
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e); //or your exceptions
}
}
You can follow this pattern in a class called for example CustomersDao or CustomerManager, and you can call it with a simple
CustomersDao dao = new CustomersDao();
List<Customers> customers = dao.listCustomers();
Note that I'm using try with resources and this code is safe to connections leaks, clean, and straightforward, You probably don't want to follow the full DAO pattern with Factorys, interfaces and all that plumbing that in many cases don't add real value.
I don't think that it's a good idea using ThreadLocals, Bad used like in the accepted answer is a source of classloader leaks
Remember ALWAYS close your resources (Statements, ResultSets, Connections) in a try finally block or using try with resources

Categories