1

Is there any way of better writing database methods, without always having to get the entity amanager, begin the trasnaction, commit, close etc?

private updateDB() {
        EntityManager em = EntityManagerFactory.get();
        em.getTransaction().begin();

        //do some DB stuff        

        em.getTransaction().commit();
        em.close();
}

It has to work without any application server, just a local client JavaSE application.

membersound
  • 81,582
  • 193
  • 585
  • 1,120
  • I think it is better to keep your data under the protection of transaction, and it will keep your data with integration and rollback security! – Rugal Jan 10 '14 at 09:50
  • so it you just want to play with DB, have fun! – Rugal Jan 10 '14 at 09:51

1 Answers1

2

You can use a template method pattern and an interface callback for the template method's implementation.

A quick and short example of such a template method pattern could look like this:

public class TransactionTemplate {

    public <T> T execute(Callable<T> doInTransaction) throws Exception {
        EntityManager em = EntityManagerFactory.get();
        EntityTransaction transaction = em.getTransaction();
        transaction.begin();

        try {
            T result = doInTransaction.call();
            transaction.commit();
            return result;
        } catch(/* Some exception that causes a rollback */ e) {
           transaction.rollback();
           throw e;
        } finally {
           em.close();
        }
    }
}

In your code you can then use it this way

private updateDB() {
   TransactionTemplate transaction = ... // get it somehow;
   transaction.execute(new Callable<Void>(){

        // will be executed in a transaction
        public Void call() throws Exception {
            //do some DB stuff  
        }
   });
}  

Note: This is exactly the same pattern that spring's TransactionTemplate uses. So if you can use spring you will get it for free.

Edit according to user3111525 comment

There is no reference to it passed. If for example the operation is to persist(), how can it do it? ... Guess there is a need to use ThreadLocal`?

There are several ways you can make the EntityManager reference available to the Callable

  1. Use a ThreadLocal as user3111525 proposed in the comment
  2. Instead of using a standard Callable<T> introduce an own interface, e.g. EMAwareCallable.call(EntityManager).
  3. If the Callable is a anonymous class provide it by a final variable of the enclosing class.
  4. Create a class that implements Callable and add a constructor to pass the reference to it.

Each way has it's pros and cons in implementation effort, testability, extensibility (standard java interface or not), and so on. It's up to you to make a choice.

René Link
  • 48,224
  • 13
  • 108
  • 140
  • Can I use Spring in a client app that does not run on any server? If yes, then Spring might be my choice here?= – membersound Jan 10 '14 at 10:18
  • 1
    @membersound Yes you can. Take a look at http://stackoverflow.com/questions/8105217/using-spring-in-a-standalone-application – René Link Jan 10 '14 at 10:19
  • 1
    @membersound you can also use a lot of spring classes just as normal classes without the need of an application context to be started. – René Link Jan 10 '14 at 10:21
  • So I could as well annotate my DB methods with `@Transactional` an am fine with it? – membersound Jan 10 '14 at 10:23
  • 1
    @membersound Yes, you can use `@Transactional`. Since spring uses java proxies per default you must create an interface that the db class implements. Then you can define your db impl in the spring context xml and enable springs annotation based transaction management. Spring will then create a transaction aware proxy if you request the db access object from the application context. – René Link Jan 10 '14 at 10:25
  • @RenéLink how would ``doInTransaction`` access the entity manager. There is no reference to it passed. If for example the operation is to persist(), how can it do it? – user3111525 Jan 20 '14 at 22:22