Specific use of try-catch

13

I'm studying Hibernate and with it came some doubts that have come to me before but did not question in depth.

public void salvar(Usuario usuario){
    Session sessao = null;
    Transaction transacao = null;

    try{
        sessao = HibernateUtil.getSessionFactory().openSession();
        transacao = sessao.beginTransaction();
        sessao.save(usuario);
        transacao.commit();
    }catch(HibernateException e){
        System.out.println("Não foi possível inserir o usuário. Erro: "+e.getMessage());
    }finally{
        try{
            sessao.close();
        }catch(Throwable e){
            System.out.println("Não foi possível fechar operação de atualização. Messagem: "+e.getMessage());
        }
    }

}

The way it looks to me, it's kind of "dirty", I mean, it's overflowed with try-catch everywhere, is a clean code really like this?

Another question is, what is the use of using try with feature? Should I use whenever possible% w / resources ?

    
asked by anonymous 15.01.2016 / 14:22

4 answers

8

A peculiar situation

This example you gave is interesting because a HibernateException , which is not a checked exception (that is, exception that must be handled or passed to the level above) can be triggered by a SQLException of JDBC, which is checked , which in turn can be triggered by a IOException , which is also checked , and can happen when the connection to the bank is lost (after it has been established, of course). However, the fact that HibernateException is not checked disallows exception handling, which may or may not be advisable in this situation, to check below (and you will see that it needs to be handled). >

When the connection drops any of the methods beginTransaction() , save() or commit() will throw a HibernateException , indicating that the connection has been lost and the resource (socket opened with the bank) needs to be released. Or, in other words, signaling that sessao.close() needs to be called. But sessao.close() , which underneath the panels calls Socket.close() , in addition to releasing the socket (system resource) will also trigger an exception when trying to close the connection that has already been lost.

Note: According to the documentation of the Session.close() and Session.disconnect() methods, they only need to be called in applications that pass a persistent JDBC connection ( long- in> or long-conversation ) to Hibernate rather than letting you create a pool of them alone. If you use a pool of connections do not call sessao.close() and ignore what I say about the second exception.

Second exception: capture and then ignore

This second exception does not need to receive any special handling because it is a normal occurrence due to the loss of network connection. So just capture this second exception and do nothing more with it, not even logarithm. The socket will be released and will no longer be a busy resource on the system, which would be a potential problem if the number of open connections is (or might be) too large. The operating system is limited in the number of sockets that can keep busy, even if disconnected.

The only problem of this second exception, besides not having to log anything, is that catch (Throwable e) should be more specific: catch (HibernateException e) .

First exception: skip to level above

The catch for the first exception may or may not be necessary. In general in this situation the level above will want to handle some exception of bank failure, perhaps displaying a dialog box for the user or some other way, so instead of capturing HibernateException and simply logging you have the option to leave this exception go to the above level directly or else capture it and throw in its place a more "high-level" exception, which does not specify that it is a Hibernate exception. You may find that HibernateException is a specific thing of a certain persistence solution that your system must be free to exchange for others, and so you should exchange it for DaoException (Do DAs make sense in Hibernate? a lot of it's not hehehe), or PersistenceException or something like this (I'm giving you an example here of exception, just to stress this idea):

// Resolvi tornar checked também, mas isso é opcional
public class PersistenceException extends Exception {
    public PersistenceException(Throwable e) {
        super(e);
    }
}

public void salvar(Usuario usuario) throws PersistenceException {
    Session sessao = null;

    try {
        sessao = HibernateUtil.getSessionFactory().openSession();
        Transaction transacao = sessao.beginTransaction();
        sessao.save(usuario);
        transacao.commit();
    } catch (HibernateException e) {
        throw new PersistenceException(e);
    } finally {
        try{
            if (sessao != null) {
                sessao.close();
            }
        } catch (HibernateException e) {
            // Não precisa fazer nada
        }
    }
}

Conclusion

In this case, you may notice that even HibernateException not being checked and you are not required to handle the two exceptions that were generated, it is better to treat them in some way to prevent a system resource is left busy.

On the try with resources , Session does not implement the interface Closeable so in that case you can not use it.

    
15.01.2016 / 17:05
8

No. You're covered in reason. People catch far more exceptions than they should. Although Java requires or at least encourages you to catch many exceptions that should not be captured, at least they should be captured at a later time. I speak this everywhere on the site .

I find it unlikely that% w / w% more internal is required. If you happen to be unable to close the connection you are probably in such a catastrophic condition that this is a programming error or the environment is so compromised that the best thing to do is let the plagiarism break. You can even catch the exception elsewhere to give a cute message and maybe log in, but it should be a general treatment of the whole application and not something more localized and specific.

It is almost certain that capturing try is wrong, except in Exception or some place where it centralizes the execution of the application, where it is the point of entry and exit of it. I would say capturing main() is always wrong. In addition to being too generic (see why it's bad at link answers above), it catches programming errors or nothing can be done. These errors should not be handled, they should be resolved.

The other Throwable can be useful. Of course if it is only to show the error in the console and nothing else, I do not know if it is so useful, it may even be in some case. But I think this is not the reason for the question.

Capturing an error where an external resource is informing you of a failure is often a good thing. You just need to see if the location is the best it can be. You do not necessarily have to capture as fast as you can. Yes, it is often the case. That seems appropriate.

Java 7 introduced the try for resources where try passes be unnecessary. If Hibernate uses this pattern, it can be deleted. It looks like it does not use for this object. But here's the hint to eliminate in other cases.

    
15.01.2016 / 14:36
6

Closing resources properly is not always as simple as it sounds. More subtle problems can occur.

I've been reading this days How to Close JDBC Resources Properly - Every Time . For normal connections, that is, using Connection and Statement and not Hibernate or JPA, the most appropriate code is as follows:

Connection connection = dataSource.getConnection();
try {
    Statement statement = connection.createStatement();
    try {
        ResultSet resultSet = statement.executeQuery("some query");
        try {
            //recuperar resultado aqui
        } finally {
            resultSet.close();
        }
    } finally {
        statement.close();
    }
} finally {
    connection.close();
}

In your case, specifically, you could do this:

try {
    Session sessao = HibernateUtil.getSessionFactory().openSession();
    try {
        Transaction transacao = sessao.beginTransaction();
        try {
            sessao.save(usuario);
            transacao.commit();
        } catch (Exception e) {
            transacao.rollback();
            throw e;
        }
    } finally {
        sessao.close();
    }
} catch(Throwable e) {
    trataErro(e);
}

The downside of the code is the level of nesting, it can be confusing. The advantage is that you do not have to deal with values unnecessarily. All features are closed within scope and, at least for me, easier to understand.

However, a common mistake in large applications is to use boileroplate code everywhere. Then some new programmer forgets some finally and your application starts to leak memory.

One way to resolve this by using direct database access is through a library as JdbcTemplate of Spring.

For JPA or Hibernate you can set up using a Reverse Dependency Injection and Control framework such as Spring to inject your Session and automatically manage your closure within the scope of a request (thinking of a web system).

Or, if you can not or do not want to use frameworks, you can encapsulate this logic yourself. Example using Java 8 and lambdas:

public void salvar(Usuario usuario) {
    execute(session -> session.save(usuario));
}

public void execute(Consumer<Session> consumer) {
    try {
        Session sessao = HibernateUtil.getSessionFactory().openSession();
        try {
            Transaction transacao = sessao.beginTransaction();
            try {
                consumer.accept(sessao);
                transacao.commit();
            } catch (Exception e) {
                transacao.rollback();
                throw e;
            }
        } finally {
            sessao.close();
        }
    } catch (Throwable e) {
        trataErro(e);
    }
}

Okay, now you never have to repeat that code again.

And then, if you feel the need to add another try/catch to make a rollback in case of an error, you just need to move in one place. >

Example:

public void execute(Consumer<Session> consumer) {
    try {
        Session sessao = HibernateUtil.getSessionFactory().openSession();
        try {
            Transaction transacao = sessao.beginTransaction();
            try {
                consumer.accept(sessao);
                transacao.commit();
            } catch (Throwable e) {
                transacao.rollback(); 
                throw e;
            }
        } finally {
            sessao.close();
        }
    } catch (Throwable e) {
        trataErro(e);
    }
}
    
16.01.2016 / 01:24
3

Whenever you want to deal with a possible error, you will have to use try / catch. If the error you receive from Hibernate is enough for you in the above layers, just do not treat the exception.

Depending on your choices for the presentation layer, there are other ways to handle errors in an automated way, such as Spring MVC (In the case of web applications) as explained in article

As for the finally, I believe the new versions of hibernate handle session closing, without you having to worry about it.

But again, if you have a specific interest in addressing the possibility of not immediately closing the session, then YES, you will have to do this "dirty" code to solve your problem.

    
15.01.2016 / 14:31