简体   繁体   中英

Should Java JDBC Select statements always be in a Try-Catch block?

Is it good practice to put all Java JDBC Select statements in a try-catch block? Currently I write most of my code without it. However, I do try-catch for insert/update/delete.

Note: Currently using Sprint Boot.

String sqlQuery = "Select productId, productName, productStartDate from dbo.product where productId = 5"

public getProductData() {
    ....
    List<Product> productList =  namedJdbcTemplate.query(sqlQuery, new ProductMapper());

First of all, if you're working with raw JDBC API, you should always use PreparedStatement .

Yes, you'll just have to wrap the code with try-catch block at some point, though it's a good practice to catch exceptions just right away or at the point where it's logically suits. In case of SQL queries, you actually should wrap all of them into some Service class that will give you an access to modify your database objects without running through JDBC API every time. For example:

public class UserService {
    private static final String CREATE_USER_SQL = "...";
    private final Connection jdbcConnection;

    public @Nullable User createUser(final String name) {
        try (final PreparedStatement stmt = jdbcConnection.prepareStatement(CREATE_USER_SQL)) {
            jdbcConnection.setAutoCommit(false);
            stmt.setString(1, name);
            stmt.executeQuery();
            jdbcConnection.commit();
            return new User(name);
        } catch (final SQLException createException) {
            System.out.printf("User CREATE failed: %s\n", createException.getMessage());
            try {
                jdbcConnection.rollback();
            } catch (final SQLException rollbackException) {
                System.out.printf("Rollback failed: %s\n", rollbackException.getMessage());
            }
            return null;
        }
    }
}

This solves two problems right away:

  1. You won't need to put boilerplate JDBC code everywhere;

  2. It will log any JDBC errors right away, so you won't need to go through a complex debugging process.

Since this question is tagged with spring-boot and you are using JdbcTemplate, I'm giving you a Spring-specific answer.

One of the points of Spring is to avoid boilerplate from developers. If you find yourself adding things repetitively, like putting try-catch blocks around code executing DML, that's cause for suspecting you're not doing something right. Adding your own try-catches in code using Spring isn't always wrong, but it usually is.

SpringJdbc takes care of a lot of things for you. It handles closing JDBC resources and returning connections to their pool, and converts exceptions from SQLException to a hierarchy of unchecked DataAccessExceptions. In Spring unchecked exceptions thrown from a method wrapped in a transactional proxy cause the transaction to get rolled back. If you do your own try-catch logic you can prevent rollback from occurring when it needs to, if you catch the exception and the proxy never sees it.

Exceptions do need to be caught somewhere. In a Spring web application, you can set up an exception handler that catches anything thrown from the controller layer, so that you can log it. Exceptions are thrown in order to escape the current context, which isn't able to deal with the problem, and relocate control to somewhere safe. For most exceptions coming from JDBC, they aren't anything you can fix, you just want to let it be thrown, let the current transaction rollback, then let the central exception handler catch and log it.

Try-catch for DB Layer code is important if you're querying with JDBC.

Think about, what if the connection broke? Or what if Database crashed? Or some other unfortunate scenario comes up.

For these things, I will recommend you to always keep the DB layer code within try-catch.

It's also recommended for you to have some fallback mechanism in-case of the above events.

Brief explanation:

  1. First of all any resource involving I/O access (database access is I/O access) must always be closed or it will cause a memory leak .
  2. Secondly, it is better to rely on try-with-resources to close any resource as having to call the .close() method manually is always exposed to the risk of not being effectively executed at runtime due to a potential Exception / RuntimeException / Error getting thrown beforehand; even closing the resource in a finally method is not preferable as such block executes at a different phase compared to the try-with-resources - auto closure of try-with-resources happens at the end of the try block, while finally executes at the end of all try / catch block -, in addition to the basic problem that it is not a secure solution as a throw might happen even inside the finally block, preventing it from completing correctly.

This said, you always need to close:

  1. Statement / PreparedStatement / CallableStatement
  2. any ResultSet
  3. the whole Connection when you don't need DB access anymore

You should always handle it with try cactch.

Why: For example you started a connection to db then some exception happened, if you don't rollback your transaction it stay on db and performance will be decreased, and memory leak will be happen.

Imagine, if your connection limit is 100 and 100 exception throwed after transaction started and you didn't rollback it your system will be locked because of you can't create any new connection to your database.

But if you want an alternative for "try catch finally" you can use like this:

EmUtil.consEm(em -> {
    System.out.println(em.createNativeQuery("select * from temp").getResultList().size());
});

Source code:

public final class EmUtil {

    interface EmCons {
        public void cons(EntityManager em);
    }

    public static void consEm(EmCons t) {
        EntityManager em = null;
        try {
            em = getEmf().createEntityManager();
            t.cons(em);
        } finally {
            if (em != null && em.getTransaction().isActive())
                em.getTransaction().rollback();
            if (em != null && em.isOpen())
                em.close();
        }
    }

    private static EntityManagerFactory getEmf() {
        //TODO
    }
   
}

Spring translates those exceptions as DataAccessException (for more detail link ). It will be good to catch those exceptions and you can rollback with @Transactional .

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