简体   繁体   中英

Proper way to handle SQL database connections in java OOP style

I recently did a programming challenge. It was an OOP challenge to expose an API for managing a fictitious movie rental company. I chose to use sparkjava, and worked from a heroku template. I have posted the code to Github here , and invite anyone to query the URL provided in the README doc. The secret password is secret .

I received a lot of good feedback about things that need improvement. One objection is:

Handling connections directly vi the driver, without any handling for closing connections properly.

So I am trying to figure out what this means, why its a problem, and how to fix it and make my code better.

For example I have this method, in the main class :

public static int validate_customer(String cust) throws SQLException, URISyntaxException {
    int customer = 0;
    try{
        customer = Integer.parseInt(cust);
    }catch (Exception e){
        throw new SQLException("Invalid customer integer -> " + cust);
    }

    Connection connection = DatabaseUrl.extract().getConnection();
    PreparedStatement stmt = connection.prepareStatement("SELECT count(*) from customers where id = ?;");
    stmt.setInt(1, customer);
    ResultSet rs = stmt.executeQuery();
    rs.next();
    if ( rs.getInt(1) < 1 ){
        throw new SQLException("Invalid customer id -> " + customer);
    }
    rs.close();
    stmt.close();
    return customer;
}

Why is this an incorrect way to handle interaction with the DB? There are other methods there in the main class which interact with the DB but the technique is basically the same as this example.

To me, if there was a problem with the connection to the database, it would occur in the line: Connection connection = DatabaseUrl.extract().getConnection(); and throw an SQLException . If somethings happens to the connection, and some part of the query doesn't work, an exception will be thrown from this validate_customer method. Why is this a problem in my code?

Handling connections directly vi the driver

This means you are not using a DB connection pool (like HikariCP ), which means it's possible exhaust your connection limit very quickly. The Heroku example does not use a DB connection pool to limit dependencies in the example.

without any handling for closing connections properly.

This means the stmt.close(); is not a in a finally block, which means if an exception is thrown in the method the stmt will never be closed.

I often see the closing of connections done in a the finally block of a try catch. Perhaps handling the SQL Exception inside the method within a try catch statement would be better and closing the resources within the finally block. This is just what I often see. I hope it may have helped.


To update this, Java 7 introduces a feature named 'try-with-resources'. This seems to be the new preferred way of cleaning up resources. To provide an example:

try(Connection connection = Connection.getConnection();
    PreparedStatement stmt = connection.prepareStatement("..."))
{
    ...
}

A try with resources will automatically close any resources which implement the AutoClosable interface and are declared in the parenthesis of a try statement.

Here is a link to the official tutorial documentation: https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

I'd recommend you to use a datasource . Have you in your classpath the dependencies for the datasource and the jdbc database specific driver. In this case I'm using dbcp2 and mysql .

public long count(final int customerId) {

    int count;

    final String query = "SELECT count(*) FROM customers WHERE id = ?";

    try (final Connection conn = dataSource.getConnection();
         final PreparedStatement prepStatement = conn.prepareStatement(query)) {

        prepStatement.setInt(1, customerId);

        final ResultSet rs = prepStatement.executeQuery();

        rs.next();
        count = rs.getInt(1);

    } catch (SQLException e) {

        // You may want to log and throw a custom exception here.
        throw new RuntimeException(e);
    }

    return count;
}

public DataSource dataSource() {

    final org.apache.commons.dbcp2.BasicDataSource dataSource =
        new org.apache.commons.dbcp2.BasicDataSource();

    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    dataSource.setUrl(environment.getProperty("jdbc:mysql://localhost:3306/myDatabase"));
    dataSource.setUsername(environment.getProperty("root"));
    dataSource.setPassword(environment.getProperty("password"));

    return dataSource;
}

Notice I'm not explicitly closing the connection on a finally block because since Java 7 the Connection and the PreparedStatement are auto-closeable resources .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM