Transactions 将应用程序迁移到使用Guice-如何将事务注入现有对象?

Transactions 将应用程序迁移到使用Guice-如何将事务注入现有对象?,transactions,migration,guice,Transactions,Migration,Guice,我是Guice的新手,我正在使用大量遗留代码将其应用到一个应用程序中。它有几个类,如下所示: public final class DataAccessClass { private Transaction txn; @Inject //This was just added public DataAccessClass(/* injectable parameters */, Transaction txn) { this.txn = txn;

我是Guice的新手,我正在使用大量遗留代码将其应用到一个应用程序中。它有几个类,如下所示:

public final class DataAccessClass {

    private Transaction txn;

    @Inject //This was just added
    public DataAccessClass(/* injectable parameters */, Transaction txn) {

        this.txn = txn;
    }

    //Maybe add @Inject here to set a new transaction when it changes?
    public void setTransaction(Transaction txn) {

        this.txn = txn;
    }

    public void writeData(/* parameters for data to be written */) {

        //Write data using the current instance of 'txn'
    }
}
public final class Application {

    private final Provider<Transaction> transactionProvider;
    private final DataAccessClass dao; //Instance provided by Guice

    public void scopedOperation() {

        //Use a fresh transaction for this operation
        final Transaction txn = ...;

        //Make transaction available to Guice (for new objects) and to legacy (already-instantiated) DAO classes
        this.transactionProvider.setTransaction(txn);
        this.dao.setTransaction(txn); //Legacy approach - can this be updated?

        //Do some kind of data operation under the scope of the current transaction
        try {
            this.dao.writeData(...);
        } catch (Exception e) {
            txn.abort();
            throw new RuntimeException("Operation failed", e);
        }

        //The operation is over now
        this.txn.commit();
    }
如何使用Guice绑定从不更改的实例是非常清楚的,但是对于确实更改的实例(即事务)又如何呢?有没有一种方法可以让我在事务发生变化时使用Guice注入不同的事务实例?请注意,事务实例不是受良好支持的JPA/Hibernate/Spring事务之一

我能想到的入侵性最小的方法(避免同时迁移每个使用事务的类)是仅在实例化对象时使用Guice注入事务,并且我会保留现有的应用程序代码,在必要时更新事务。例如,此提供程序可用于向当前事务实例注入新对象:

public final class TransactionProvider implements Provider<Transaction> {

    /** Nullable transaction that should be used for all operations at the moment */
    private Transaction txn;

    public TransactionProvider(Transaction txn) {

        this.txn = txn;
    }

    /**
     * @param txn Nullable transaction to use for all operations at the moment
     */
    public void setTransaction(Transaction txn) {

        this.txn = txn;
    }

    /* Provider methods */

    public Transaction get() {

        return this.txn;
    }
}
公共最终类TransactionProvider实现提供程序{
/**此时应用于所有操作的可为空的事务*/
私人交易txn;
公共事务提供程序(事务txn){
这个.txn=txn;
}
/**
*@param txn可为空的事务,用于当前的所有操作
*/
公共作废setTransaction(事务txn){
这个.txn=txn;
}
/*提供者方法*/
公共事务get(){
返回此.txn;
}
}
应用程序逻辑如下所示:

public final class DataAccessClass {

    private Transaction txn;

    @Inject //This was just added
    public DataAccessClass(/* injectable parameters */, Transaction txn) {

        this.txn = txn;
    }

    //Maybe add @Inject here to set a new transaction when it changes?
    public void setTransaction(Transaction txn) {

        this.txn = txn;
    }

    public void writeData(/* parameters for data to be written */) {

        //Write data using the current instance of 'txn'
    }
}
public final class Application {

    private final Provider<Transaction> transactionProvider;
    private final DataAccessClass dao; //Instance provided by Guice

    public void scopedOperation() {

        //Use a fresh transaction for this operation
        final Transaction txn = ...;

        //Make transaction available to Guice (for new objects) and to legacy (already-instantiated) DAO classes
        this.transactionProvider.setTransaction(txn);
        this.dao.setTransaction(txn); //Legacy approach - can this be updated?

        //Do some kind of data operation under the scope of the current transaction
        try {
            this.dao.writeData(...);
        } catch (Exception e) {
            txn.abort();
            throw new RuntimeException("Operation failed", e);
        }

        //The operation is over now
        this.txn.commit();
    }
公共最终课程申请{
私人最终提供者transactionProvider;
private final DataAccessClass dao;//Guice提供的实例
公共无效范围操作(){
//为此操作使用新事务
最终交易txn=。。。;
//使事务可用于GUI(用于新对象)和遗留(已实例化)DAO类
this.transactionProvider.setTransaction(txn);
this.dao.setTransaction(txn);//旧方法-可以更新吗?
//在当前事务的范围内执行某种数据操作
试一试{
这个.dao.writeData(…);
}捕获(例外e){
txn.abort();
抛出新的运行时异常(“操作失败”,e);
}
//手术现在结束了
this.txn.commit();
}
是否有其他方法可以更新现有类使用的事务实例,而无需立即迁移每个类?

请查看。要使用它,请定义一个三行接口:

public interface DataAccessClassFactory {
  DataAccessClass create(Transaction transaction);
}
…然后使用
FactoryProvider
绑定来绑定工厂:

bind(DataAccessClassFactory.class).toProvider(
    FactoryProvider.newFactory(DataAccessClassFactory.class, DataAccessClass.class));

然后,您可以在需要构建
DataAccessClass
的任何位置注入
DataAccessClassFactory
。尽管它需要一个单独的.jar文件,但Guice 2中包含了AssistedInject。

如果我理解正确,您的问题有两个独立部分:

  • 在规范化类上使用正确的事务实例
  • 在遗留类上使用正确的事务实例
  • 对于“1”,您的自定义提供程序可以工作,但我将为事务创建一个自定义作用域,并在该作用域绑定事务类。有关自定义作用域的说明,请参阅


    关于“2”,一旦有了自定义作用域,这是使用Guice向遗留类提供实例的常见问题。您没有提到当前向遗留类提供事务实例的代码,但您可以更改该特定代码段以从Guice请求实例。因为您在“交易范围",Guice负责提供正确的实例。

    这看起来可能会有所帮助,但我不知道如何使用它将事务绑定更新到表示当前打开的事务的实例。换句话说,在任何给定时间只使用一个事务实例,但每次都会替换该实例新事务已启动。是否有更详细的使用AssistedInject的示例?在wiki页面上,我很难确定日期和金钱的实例在何处实例化。谢谢-这看起来会起作用。重构遗留类以从Guice请求事务很有意义-它看起来会起作用这可能是对每个遗留类的一个小更改,并且可能比使用两个系统来管理事务要容易得多。