Требуются шаблоны (Hibernate + Guice) - PullRequest
3 голосов
/ 16 декабря 2008

Я ищу предложения о том, как внедрить зависимости времени выполнения в сущности JPA, полученные из Hibernate. Моя проблема, по сути, такова:

У меня есть несколько различных подклассов объекта транзакции. Каждый подкласс Transaction по-разному ведет себя при выполнении и требует различного набора зависимостей от среды. Эти объекты транзакций управляются как объекты JPA с помощью Hibernate, поэтому я не могу эффективно использовать Guice для внедрения зависимостей, чтобы заполнить экземпляры их зависимостями среды, как я делаю в остальной части моего приложения.

Чтобы обойти эту проблему, я выбрал подход, несколько схожий с шаблоном Visitor, а именно:

public abstract class Transaction {
    // ...snip...
    public abstract void apply(Transactor transactor);
}

public class TransactionA extends Transaction {
    public void apply(Transactor transactor) {
        transactor.execute(this);
    }
}

public class TransactionB extends Transaction {
    public void apply(Transactor transactor) {
        transactor.execute(this);
    }
}
// other Transaction subclasses with the same boilerplate

public interface Transactor {
    public void execute(TransactionA trans);
    public void execute(TransactionB trans);
    // corresponding methods for other transaction types.
}

public class BeginTransactor {
     @Inject
     private Foo execAdep;
     public void execute(TransactionA trans) {
         execAdep.doSomething(...)    
     }

     @Inject
     private Bar execBdep;
     public void execute(TransactionB trans) {
         execBdep.doOther(...)    
     }
 }

У меня есть различные реализации Transactor для разных частей жизненного цикла транзакции. Они могут быть внедрены в зависимости с помощью Guice в контекст, в котором я хочу обрабатывать транзакции, где я просто вызываю:

 Transactor transactor = injector.getInstance(BeginTransactor.class); //Guice injection
 Transaction t = ... //get a transaction instance
 t.apply(transactor);

Что мне не нравится в этом подходе, так это (1) Не каждый тип транзакции должен быть исполняемым на каждой фазе жизненного цикла, но каждый Transactor должен реализовывать метод execute () для каждого подкласса транзакции и (2) по сути, ни один из внедренные зависимости используются для обработки нескольких типов транзакций.

По сути, мой интерфейс и реализации Transactor содержат много несвязанных друг с другом слов. В идеале я бы просто использовал метод execute () для самого объекта транзакции, но я не хочу, чтобы вызывающий код должен был знать о типе транзакции или зависимостях, которые ему требуются. Кроме того, это может усложнить тестирование, потому что я не мог легко смоделировать метод execute (), если бы это был конкретный метод объекта Transaction. Использование интерфейса Transactor означает, что я могу легко смоделировать его при необходимости.

Кто-нибудь может подсказать, как решить эту проблему безопасным для типов способом, который не приводит к тому, что в Transactor смешивается куча в основном несвязанного поведения, но сохраняет тестируемость и позволяет внедрять зависимости?

Ответы [ 2 ]

5 голосов
/ 17 декабря 2008

Я использую подсказки для транзакций, но использую АОП для их выполнения. У меня почти нет шаблонов, за счет немного "магии". Пока ваш перехваченный класс находится «в клубе», он работает очень хорошо.

class BusinessLogic {
    @Inject public EntityManager em;

    @Transactional
    publc void doSomething() {
       //...
       em.persist(myObj);
    }

    @Transactional
    public void doSomethingElse() {
       //...
       em.delete(myObj);
    }
}

class TransactionalInterceptor implements MethodInterceptor {
    @Inject static Injector injector;
    public Object intercept(MethodInvocation invocation) {
        EntityManager em = injector.getInstance(EntityManager.class);
        em.getTransaction().begin();
        Object result = invocation.proceed();
        em.getTransaction().commit();
        return result;
    }
}
class TransactionalModule extends AbstractModule {
    public void configure() {
        requestStaticInjection(TransactionalInterceptor.class);
        bindInterceptor(Matchers.any(), Matchers.annotatedWith(Transactional.class),
                 new TransactionalInterceptor());
    }
}
2 голосов
/ 17 декабря 2008

Проверка: http://www.hibernate.org/hib_docs/v3/api/org/hibernate/Interceptor.html

Настройте свой спящий режим для использования Intercetor. Метод

public Object instantiate(String entityName,
                      EntityMode entityMode,
                      Serializable id)

будет вызван на

Instantiate the entity class. Return null to indicate that Hibernate
should use the default constructor of the class. The identifier property
of the returned instance should be initialized with the given identifier. 

Оттуда вы можете позвонить по адресу injector.getInstance ().

Возможно, вы могли бы использовать методы getEntity () или onLoad (). Из метода onLoad () вы можете вызвать injector.injectMembers ().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...