Java design issue in which behavior is tied to annotations

Let's say I use JPA with @transactions annotations.

So, for a method to execute under a transaction, I add @transaction annotations and my BINGO method executed in the transaction.

To achieve the above, we need to have an interface for the class, and the instance is controlled by some kind of container.
Also, I should always call the method from the interface link so that the proxy object can start the transaction. So my code will look like this:

 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(); } 

Good and good

Now let's say methodThatRunUnderTx performs two logical operations

[1] call some service (long request / response cycle, say, 5 seconds) and select the results

[2] make some modifications to the jpa object

Now, since this method call is long, and we don’t want to hold the transaction for a long time, so we change the code so that [2] occurs in separate tx and methodThatRunUnderTx does not execute in the transaction

So, we will remove @transaction from methodThatRunUnderTx and add another method to the class with @transaction , suppose that the new methods are methodThatRunUnderTx2 , now to call this method from methodThatRunUnderTx we need to insert it into and add a method for the interface so that the call is made through a proxy an object.

So, now our code will look like this:

 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 Problem

We made methodThatRunUnderTx2() public through the interface .

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

Any suggestions for resolving it?

+6
source share
7 answers

So that modern containers do not require the implementation of any interface, proxies are used using a dynamic subclass or bytecode toolkit.

So, the solution to the design problem is simple: Deploy a helper class containing the transactional method, and add it to the class that implements the interface (and any other class that can benefit from it).

+2
source

Following the principle of interface separability, separate two logical operations into two interfaces: a collector and a modifier. Add both values ​​to the Bar class. This allows two logical implementations to change independently of each other, for example, allowing a transaction to be carried out, and the other not. The second interface does not have to be public.

+1
source

The question is very important for transaction processing. However, if you are trying to hide one functionality over another, you need to consider the following issues:

OPTION 1:

  • Given - you will need to set a method that performs all the functionality required by the caller

  • In this case, transaction processing, I suggest you keep the transaction open until it completes

OPTION 2:

  • Given - you will need to effectively manage transactions
  • Separate interface methods based on the functionality of IModifyFoo and ISelectFoo , which modify and select accordingly and implement methods and comment using @Transactional on the required methods
  • Interfaces are designed to be publicly accessible, which means you need to know what you need to expose to the outside world. In this case, you are invited to choose the Principle for a technical problem.

I can only think of these options, and we are trying to solve your technical problem here, which is based on the basics of Java. Think about it well.

+1
source

As you said, if you call the method in the same bean, it will not be proxied, so transaction management will not happen, to solve it you can bean a managed transaction, where you manually start and stop the transaction

 class FooImpl implements FooI { @Resource private UserTransaction userTransaction; @Override //@Transaction -- remove transaction from here public void methodThatRunUnderTx() { ... self.methodThatRunUnderTx2();// call through proxy object } @Override //@Transaction -- remove transaction from here too, because now you'll manage the transaction public void methodThatRunUnderTx2() { userTransaction.start(); // code run with jpa context and transaction open userTransaction.commit(); // Commit or rollback do all the handling, i'm not writing it because its just an example } } 

This way you are not posting anything superfluous for the public api, but you will have some extra code to manage the transaction.

0
source

if you want thisThatRunUnderTx2 method not to become public, make it a private method and remove the @Override annotation and remove it from the interface.

0
source

You must agree that transaction-based annotations will not work with private methods. Thus, you simply cannot hide (make private) the method that should be the object of such an annotation.

You can get rid of interfaces (i.e. @LocalBean in the EJB world), but nonetheless you cannot use a private method ...

0
source

Of course, the solution to this problem is the problem. They will allow you to get rid of the call to the self.methodThatRunUnderTx2() method from the body of the public void methodThatRunUnderTx() . Most likely, the answer to this question can help you: Aspectj and catch private or internal methods

I am not sure, however, if the aspects are not too large for this problem, since they increase the complexity and readability of the code. I would rather think about changing the architecture of your code so that your problem does not matter.

0
source

All Articles