0

I want to know whether a username is already in use, and if that's not the case, whether an emailadress is already in use. I don't want to begin and commit a new transaction for each entitymanager find method because I think using just one transaction for multiple operations is more efficient then using several. Can I replace commit() with flush() in my method?

    public void createUser(User_ newUser){
            UserDao userDao = new UserDao();
    Alert alert;
    EntityTransaction transaction = entityManager.getTransaction();

        try{
            transaction.begin();
            userDao.find("userName",newUser.getUserName());
            transaction.commit();
            alert = new Alert(Alert.AlertType.ERROR,"Username already in use", ButtonType.OK);
            alert.showAndWait();
            if (alert.getResult() == ButtonType.OK)alert.close();
        }        
        catch(NoResultException e){
            try{
                transaction.begin();
                userDao.find("emailAdress",newUser.getEmailAdress());
                transaction.commit();
                alert = new Alert(Alert.AlertType.ERROR,"E-mail adress already in use", ButtonType.OK);
                alert.showAndWait();
                if (alert.getResult() == ButtonType.OK) alert.close();             
                }
            catch(NoResultException x){
                    transaction.begin();
                    userDao.save(newUser);
                    transaction.commit();
                }
        }
        catch(RuntimeException e){
            transaction.rollback();    
        }
        finally{entityManager.close();}
} 

thank you.

Maurice
  • 6,698
  • 9
  • 47
  • 104

2 Answers2

1

There are three issues with your code:

  1. Why would you even wrap query methods in separate transactions? First of all, executing queries does not require one. Secondly, you are right in trying to wrap both your query methods with a single transaction, but for the wrong reason. This has little to do with performance and everything to do with isolation. A transaction is required here simply to prevent another call to createUser from creating a user with the same username in between those two queries (obviously, for the same reason, persisting the user should be part of the same transaction).

  2. You should avoid using exceptions to handle normal states of your application. Not only would that be the proper way of using exceptions, but you'll also run into far fewer issues as far as transaction state is concerned. Judging by the fact that you're expecting userDao.find to throw NoResultException, I am assuming it is using query.getSingleResult() internally. getSingleResult should only be used when you expect a query to always have a result. You should use getResultList() instead, together with and emptiness check or, better still, query for the count of User entities with the given username

  3. Your method is mixing business logic and presentation concerns. Saving data to the db and presenting alerts should most certainly not be done in a single method. If you absolutely need to communicate the error to the user in the same method that persists data, make sure you do it after the transaction is commited, to prevent it from timing out.

crizzis
  • 9,978
  • 2
  • 28
  • 47
  • **"Why would you even wrap query methods in separate transactions? First of all, executing queries does not require one. Secondly, you are right in trying to wrap both your query methods with a single transaction, but for the wrong reason. This has little to do with performance and everything to do with isolation."** On the one hand you say you don't have to use it for the find method, but on the other hand you say, use it for isolation reasons. So what should I do then? Does the use of a transaction make the query thread safe? Or the database thread safe? – Maurice May 20 '17 at 18:18
  • 1
    You don't need a transaction if *all you want to do* is execute a finder method. However, your method executes a query *and then* persists an entity, the creation of which is only allowed when a certain condition about the result of that query is met. You need to wrap both the query and the creation of the entity in a transaction to make sure *the result of that query does not change* in the meantime – crizzis May 20 '17 at 18:23
0

There is no way you can rollback your transaction once the commit() succeeds. Flushing is the process of synchronizing the underlying persistent store with persistable state held in memory.it will update or insert into your tables in the running transaction, but it may not commit those changes.

flush() will synchronize your database with the current state of object/objects held in the memory but it does not commit the transaction. So, if you get any exception after flush() is called, then the transaction will be rolled back. You can synchronize your database with small chunks of data using flush() instead of committing a large data at once using commit() and face the risk of getting an Out Of Memory Exception.

commit() will make data stored in the database permanent. There is no way you can rollback your transaction once the commit() succeeds.

Now its upto your business logic which one to use

A Paul
  • 8,113
  • 3
  • 31
  • 61
  • I am not storing anything with the find() method... So using commit() method seems so strange. Can the find method throw a NoResultException when I use flush() instead of commit()? – Maurice May 20 '17 at 16:55
  • Transaction is always needed in hibernate. Every query happens under a transaction. Keep the commit there. Dont think like transaction of JDbc. Hibernate transactions have broader view. I think you got confused with my last msg. Sorry about that – A Paul May 20 '17 at 19:57
  • so @crizzis is wrong then? He says for find queries no transaction is needed. – Maurice May 20 '17 at 20:14
  • Hibernate transaction is always required – A Paul May 20 '17 at 21:16
  • 1
    http://stackoverflow.com/questions/13539213/why-do-i-need-transaction-in-hibernate-for-read-only-operation – A Paul May 20 '17 at 21:26