行为附加到注释的Java设计问题

行为附加到注释的Java设计问题,java,dependency-injection,transactions,annotations,jpa-2.0,Java,Dependency Injection,Transactions,Annotations,Jpa 2.0,假设我通过使用@transactions注释来使用JPA 因此,为了让任何方法在事务下运行,我添加了一个@transaction注释,并在事务下运行BINGO我的方法 为了实现上述目标,我们需要为类提供一个接口,实例由某个容器管理。 此外,我应该始终从接口引用调用该方法,以便代理对象可以启动事务。 因此,我的代码如下所示: class Bar { @Inject private FooI foo; ... void doWork() { foo.methodT

假设我通过使用
@transactions
注释来使用
JPA

因此,为了让任何方法在事务下运行,我添加了一个
@transaction
注释,并在事务下运行BINGO我的方法

为了实现上述目标,我们需要为类提供一个
接口
,实例由某个容器管理。
此外,我应该始终从接口引用调用该方法,以便代理对象可以启动事务。 因此,我的代码如下所示:

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

现在让我们假设在Tx下运行的
方法执行两个逻辑操作

[1] 调用一些服务(长的请求/响应周期,比如说5秒)并获取结果

[2] 执行一些jpa实体修改

现在,由于这个方法调用很长,我们不想让事务长时间处于打开状态,所以我们更改了代码,使[2]在单独的tx中发生,而在tx下运行的
方法不在事务中运行

因此,我们将从Tx下运行的
方法中删除
@Transaction
,并在类中添加另一个
@Transaction
方法,假设新方法是Tx2下运行的
方法,现在,要从运行在Tx下的
方法调用此方法,我们必须将其注入自身,并向接口添加一个方法,以便通过代理对象进行调用

因此,现在我们的代码如下所示:

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();
}
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();
}
现在问题解决了

我们通过
接口
将在TX2()下运行的
方法
公开

但这并不是我们想要公开为
FooI
的api,也不是为了从外部调用它


有没有解决问题的建议?

正如您所说,如果您在同一个bean上调用一个方法,它将不会被代理,因此不会发生事务管理,要解决这个问题,您可以在手动启动和停止事务的情况下使用bean管理的事务:

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
   }
}

这样,您就不会向公共api公开任何额外的内容,但您将有一点额外的代码来管理事务。

遵循接口分离原则,将两个逻辑操作分离为两个接口:一个获取程序和一个修改器。将两者都注入类
。这允许两个逻辑实现相互独立地更改,例如,允许一个是事务性的,而另一个不是事务性的。第二个接口不必是公共类。

关于处理事务部分,这个问题非常有效。但是,如果您试图隐藏一个功能,则需要考虑以下事项:

选项1:

  • 考虑-您需要公开完成调用者所需全部功能的方法

  • 在这种情况下的交易处理,我会建议您保持交易开放的时间,直到它完成

  • 选项2:

  • 考虑-您需要高效地管理事务
  • 基于功能性
    IModifyFoo
    ISelectFoo
    拆分接口方法,分别修改和选择并实现方法,并在所需方法上使用
    @Transactional
    进行注释
  • 接口设计为公共的,这意味着您需要知道需要向外部世界公开什么。在这个场景中,您需要选择原则而不是技术挑战
    我可以想到这些选项,我们正试图解决您在java基础上面临的技术挑战。考虑一下很好。

    如果您希望在TX2下运行的方法不会成为公共方法,请将其设置为私有方法并删除@Override注释,然后将其从接口中删除

    这就是为什么现代容器不需要实现任何接口——然后通过动态子类化创建代理,或者使用字节码插装


    因此,您的设计问题的解决方案很简单:实现一个包含事务方法的helper类,并将其注入实现接口的类(以及可以从中受益的任何其他类)。

    您必须接受基于事务的注释在私有方法上不起作用。因此,您不能简单地隐藏(使私有)一个应该是这种注释主题的方法


    您可以去掉接口(即EJB世界中的@LocalBean),但仍然不能使用私有方法…

    确保此问题的解决方案是acpect。它们将允许从
    public void methodthattrunundertx()
    的主体中除去
    self.methodthattrunundertx2()
    方法调用。这个问题的答案很可能会帮助您:


    然而,我不确定方面对于这个问题来说是否太重要,因为它们增加了代码的复杂性和可读性。我宁愿考虑以这样一种方式更改代码的体系结构,这样您的问题就不重要了。

    我们开始使用基于注释的事务,而不是自己管理用户事务。。。这违背了使用基于注释的事务处理的目的是的,我知道,通常当你面对这个问题时,你可以通过改变类的设计来解决,但有时你必须按照你发布的方式工作,在这种情况下,你的选项就是你做过的或者我发布过的