我现在很难不在我正在研究的Java程序中重复自己。
说,我需要声明许多基本上按以下方式构建的方法:
public SomeEntity doSomething (String someAttribute, String anotherAttribute) {
EntityManager em = this.createEntityManager();
EntityTransaction tx = null;
try {
/*
* ... independent logic ...
*/
tx = em.getTransaction();
} catch (RuntimeException e) {
if (tx != null && tx.isActive()) {
tx.rollback();
}
throw e;
} finally {
em.close();
}
return something;
}
所有方法的方法体都需要包含用于资源管理的元素。
“独立逻辑”本身也会相当复杂,因此将try / catch语句放在一个单独的方法中将无法正常工作。
我想避免重复此代码。在这些情况下应用哪些最佳实践?
答案 0 :(得分:35)
创建一个界面:
public interface EntityManagerAction {
public void execute(EntityManager em);
}
实用类:
public class EntityUtil {
public static void executeWithEntityManager(EntityManagerAction action) {
EntityManager em = someHowCreateEntityManager();
EntityTransaction tx = null;
try {
action.execute(em);
tx = em.getTransaction();
} catch (RuntimeException e) {
if (tx != null && tx.isActive()) {
tx.rollback();
}
throw e;
} finally {
em.close();
}
}
}
现在您可以在EntityUtil类中重复使用样板,并且代码变为:
public SomeEntity doSomething (String someAttribute, String anotherAttribute) {
Something something;
EntityUtil.executeWithEntityManager(new EntityManagerAction() {
public void execute(EntityManager em ) {
/*
* ... independent logic ...
*/
//use the passed in 'em' here.
}
});
return something;
}
答案 1 :(得分:8)
如果您的所有finally
条款都用于关闭Stream
等(实施AutoCloseable
的任何内容),则可以使用try-with-resources
(如其中一条所述)评论)摆脱finally
条款。
但是,如果您需要更通用的解决方案,并且在Exception
子句中捕获了相同类型的finally
和相同类型的处理,则可以创建一个抽象类,例如:
abstract class SensitiveBlockHandler {
public void handle() {
try {
doHandling();
} catch (SomeException | AnotherException e) {
// TODO: handle exceptions here ...
} finally {
// TODO: cleanup here ...
}
}
protected abstract void doHandling();
}
然后,您可以创建内部类来处理不同的情况,或者作为匿名类。代码应该类似于:
public SomeEntity doSomething (String someAttribute, String anotherAttribute) {
new SensitiveBlockHandler() {
protected void doHandling() {
/*
* ... independent logic ...
*/
}
}.handle();
return something;
}
答案 2 :(得分:3)
我会创建一个独立逻辑的抽象,比如说Job
,而doSomething()
将成为processJob()
类中的Service
。您将针对每个处理调用processJob()
以及示例中的所有代码,但independent logic
只写一次。
修改: nos 在评论中建议的内容:What is the "Execute Around" idiom?
答案 3 :(得分:3)
我们最近遇到了这样的问题,并决定采用callback设计模式。
因此,如果所有方法都具有相似的代码并且它只是不同的独立逻辑,那么您可以创建一个实现处理独立代码的接口。所以像这样:
public SomeEntity doSomething (String someAttribute, String anotherAttribute) {
return (SomeEntity)callbackMethod(someAttribute, anotherAttribute, new IndependentCodeInterfaceImpl1());
}
public SomeOtherEntity doSomethingElse (String someAttribute, String anotherAttribute) {
return (SomeOtherEntity)callbackMethod(someAttribute, anotherAttribute, new IndependentCodeInterfaceImpl2());
}
private Object callbackMethod(String someAttribute, String anotherAttribute, IndependentCodeInterface independent) {
EntityManager em = this.createEntityManager();
EntityTransaction tx = null;
Object response = null;
try {
response = independent.execute(someAttribute, anotherAttribute, em);
tx = em.getTransaction();
} catch (RuntimeException e) {
if (tx != null && tx.isActive()) {
tx.rollback();
}
throw e;
} finally {
em.close();
}
return response;
}
答案 4 :(得分:3)
如果您使用的是JavaEE应用程序服务器,那么使用无状态会话bean可以大大简化您的代码:
@Stateless
public class SomethingFactory {
@PersistenceContext
private EntityManager em;
public SomeEntity doSomething (String someAttribute, String anotherAttribute) {
/*
* ... independent logic ...
*/
return something;
}
}
容器将为您管理所有事务管理语义。
答案 5 :(得分:2)
您可以使方法签名返回异常
public SomeEntity doSomething (String someAttribute, String anotherAttribute) throws RuntimeException {
// your independent logic
}
public SomeEntity doSomethingElse (String someAttribute, String anotherAttribute) throws RuntimeException {
// your independent logic
}
public SomeEntity doSomethingDifferent (String someAttribute, String anotherAttribute) throws RuntimeException {
// your independent logic
}
然后你可以用一个单独的方法处理它:
public String yourHandleMethod(){
String something = "";
EntityManager em = this.createEntityManager();
EntityTransaction tx = null;
try{
doSomething();
doSomethingElse();
doSomethingDifferent();
tx = em.getTransaction();
} catch (RuntimeException e) {
if (tx != null && tx.isActive()) {
tx.rollback();
}
throw e;
} finally {
em.close();
}
return something;
答案 6 :(得分:2)
您可以实现类似于Spring Transaction Template的机制。
首先,实现一个将为每个业务操作实现的回调,同时提供实体管理器和事务:
public static interface TransactionCallback<R> {
R doInTransaction(EntityManager em, EntityTransaction tx);
}
然后,使用样板代码创建一个通用方法:
public <T> T execute(TransactionCallback<T> callback) {
EntityManager em = this.createEntityManager();
EntityTransaction tx = null;
try {
tx = em.getTransaction();
return callback.doInTransaction(em, tx);
} catch (RuntimeException e) {
if (tx != null && tx.isActive()) {
tx.rollback();
}
throw e;
} finally {
em.close();
}
}
最后,您可以创建类似于此的业务逻辑:
public SomeEntity doSomething(String someAttribute, String anotherAttribute) {
return execute(new TransactionCallback<SomeEntity>() {
@Override
public SomeEntity doInTransaction(EntityManager em, EntityTransaction tx) {
// do something here
}
});
}
这种方法有几个优点。它很干净,您可以在一个地方拦截所有操作 - 例如:添加日志记录等。
我很抱歉任何语法错误,我在没有IDE的情况下编写它。但是你明白了。
编辑:也许在JDK 8中使用lambdas会更短,因为TransactionCallback可能是功能接口:)。
答案 7 :(得分:2)
为了完整性 - Template Method Pattern。它允许您从过程中抽象出所有管理代码,并只实现核心功能。当您有多个元需求(例如在正确处理异常时关闭内容)时,此模式特别有用。
我使用此模式运行数据库查询,因为所有规则都要确保关闭所有ResultSet
和Connection
,同时仍能正确管理PreparedStatement
第
abstract class DoSomethingWithEntity {
public SomeEntity doSomething(String someAttribute, String anotherAttribute) {
SomeEntity something;
EntityManager em = createEntityManager();
EntityTransaction tx = null;
try {
tx = em.getTransaction();
// Call the abstract stuff that can be different.
something = doIt(em, tx);
} catch (RuntimeException e) {
if (tx != null && tx.isActive()) {
tx.rollback();
}
throw e;
} finally {
em.close();
}
return something;
}
// The bit you want to write yourself. Make it abstract so you have to write it.
abstract SomeEntity doIt(EntityManager em, EntityTransaction tx) throws Exception;
}
public void test() {
SomeEntity e = new DoSomethingWithEntity() {
@Override
SomeEntity doIt(EntityManager em, EntityTransaction tx) {
// Do your stuff here.
return new SomeEntity();
}
}.doSomething("someAttribute", "anotherAttribute");
}
请注意,您不必将所有例外都设置为RuntimeException
- 您可以明确定义允许抛出的异常。
答案 8 :(得分:1)
仅供参考 - JDK 1.8中新的lambda表达式功能现在是解决此问题的最佳方式。你的DoSomething方法就是这样的:
DataExecutor.execute(() -> {
// let's say your "independent logic" is the following three statements
statementA;
statementB;
statementC;
});
你的DataExecutor.execute方法看起来像这样(你可能想要一个返回类型):
public static void execute(work)
{
EntityManager em = this.createEntityManager();
EntityTransaction tx = null;
try {
// this is the "independent logic" you passed in via the "work" lambda
work.doWork();
tx = em.getTransaction();
} catch (RuntimeException e) {
if (tx != null && tx.isActive()) {
tx.rollback();
}
throw e;
} finally {
em.close();
}
最后,您需要定义一个(或使用现有的)功能界面:
@FunctionalInterface
public interface DataFuncVoid {
public abstract void doWork();
}