Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java design issue where behavior is attached to annotations

Let say I use JPA by using @transactions annotations.

So to have any method run under a transaction I add a @transaction annotations and BINGO my method run under a transaction.

To achieve the above we need have a interface for the class and the instance is managed by some container.
Also I should always call the method from interface reference so that the proxy object can start the transaction. So My code will look like:

class Bar {
   @Inject
   private FooI foo;
   ...
   void doWork() {
      foo.methodThatRunUnderTx();
   }
}
class FooImpl implements FooI {
   @Override
   @Transaction
   public void methodThatRunUnderTx() {
       // code run with jpa context and transaction open
   }
}
interface FooI {
    void methodThatRunUnderTx();
}

Well and Good

Now let say methodThatRunUnderTx does two logic operations

[1] call some service(long request/response cycle let say 5 sec) and fetch the results

[2] perform some jpa entity modifications

Now since this method call is long and we don't want to hold the transaction open for long time, so we change the code so that [2] happens in separate tx and methodThatRunUnderTx doesnt run in transaction

So we will remove the @Transaction from the methodThatRunUnderTx and add another method in class with @transaction let say new methods is methodThatRunUnderTx2, now to call this method from methodThatRunUnderTx we have to inject it into itself and add a method to interface so that the call happen through proxy object.

So now our code will look like:

class Bar {
   @Inject
   private FooI foo;
   ...
   void doWork() {
      foo.methodThatRunUnderTx();
   }
}
class FooImpl implements FooI {
   @Inject
   private FooI self;
   @Override
   //@Transaction -- remove transaction from here
   public void methodThatRunUnderTx() {
      ...
     self.methodThatRunUnderTx2();// call through proxy object
   }
   @Override
   @Transaction //add transaction from here
   public void methodThatRunUnderTx2() {
       // code run with jpa context and transaction open
   }
}
interface FooI {
    void methodThatRunUnderTx();
    void methodThatRunUnderTx2();
}

NOW The Problem

We have made methodThatRunUnderTx2() to be public through interface.

But it is not what we want to expose as our api of FooI and not meant to be called from outside..

Any suggestion to solve it ?

like image 458
Bhuvan Avatar asked Jul 10 '15 13:07

Bhuvan


1 Answers

That's why modern containers don't require any interface to be implemented - proxies are then created by dynamic subclassing or bytecode instrumentation is used.

So, the solution to your design issue is simple: Implement a helper class containing the transactional method and inject it to the class implementing the interface (and to any other class that can benefit from it).

like image 168
Dragan Bozanovic Avatar answered Nov 14 '22 23:11

Dragan Bozanovic