I've been reading about static vs instance methods in Java (specifically because I'm hitting pain points with unit testing) and am trying to figure out how to write factories that interact with my database. I know there are many related questions; but it seems like my specific case should be very common, and it would be great to know what the alternatives are when it comes to DB factories. My current practice (with a stripped-down example of a common use case):
public class UserFactory {
public static User getUserById(Integer userId) throws SQLException {
Connection c = null;
PreparedStatement stmt = null;
ResultSet rs = null;
User user = null;
try {
c = ConnectionFactory.getConnection(); // Uses java.sql.DriverManager to get a connection to our DB. This is also a static method.
stmt = c.prepareStatement(STATEMENT);
stmt.setInt(1, userId);
rs = stmt.executeQuery();
if (rs.next()) {
user = new User();
// Set user properties
}
} finally {
ConnectionFactory.closeConnection(c, stmt, rs); // Calls .close() on c, stmt and rs. This is also a static method.
}
return user;
}
}
This allows me to get a User with User user = UserFactory.getUser(1);
. But, I can't mock this unless I use something like PowerMock, which makes testing more annoying. Additionally, many consider having written code in such a way to be bad practice.
An alternative might be:
public class UserFactory {
// ** This class is identical to the class
// above except this method is not static. **
public User getUserById(Integer userId) throws SQLException {
Connection c = null;
PreparedStatement stmt = null;
ResultSet rs = null;
User user = null;
try {
c = ConnectionFactory.getConnection(); // Uses java.sql.DriverManager to get a connection to our DB
stmt = c.prepareStatement(STATEMENT);
stmt.setInt(1, userId);
rs = stmt.executeQuery();
if (rs.next()) {
user = new User();
// Set user properties
}
} finally {
ConnectionFactory.closeConnection(c, stmt, rs); // Calls .close() on c, stmt and rs
}
return user;
}
}
To get a User now, I would have to do something like User user = new UserFactory().getUser(1);
. Of course, I could instantiate a UserFactory
object and reuse it (I could even pass it around and/or inject it into other classes; but we often interact with various such factories, so we'd in many cases need to inject/pass-in many factories, which seems unwieldy):
UserFactory userFactory = new UserFactory();
User user = userFactory.getUser(1);
This would make testing easy (with Mockito, for example), since I could just create a mock instance of UserFactory
(which I guess means the new UserFactory().getUser(1)
example usage wouldn't be very helpful--I'd need to be passing in an instance to whatever I'm testing, right?). It seems like using an instance of UserFactory
is unnecessary to achieve my goal (retrieving a User from the database), but I like that it makes testing easier (don't want unit tests hitting a database).
What am I missing here? What are other alternatives and pros/cons that I'm not considering? Is my current practice a "bad" practice simply because it makes testing annoying or for other (more important?) reasons?