2008-12-16 2 views
3

Я ищу предложения о том, как внедрять зависимости времени выполнения в объекты JPA, полученные из Hibernate. Моя проблема в основном такова:Необходимые шаблоны (Hibernate + Guice)

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

Чтобы обойти эту проблему, я принял подход, что это несколько сродни картине посетителей, следующим образом:

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 & реализовано множество несвязанных crud glopped together. В идеале у меня просто был бы метод execute() для самого объекта транзакции, но я не хочу, чтобы вызывающий код должен был знать о типе транзакции или требуемых зависимостях. Кроме того, это может сделать тестирование сложнее, потому что я не мог легко издеваться над методом execute(), если это был конкретный метод для объекта Transaction. Использование интерфейса Transactor означает, что я могу легко высмеять его по мере необходимости.

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

ответ

5

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

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

Отлично, спасибо! Я подозревал, что может возникнуть подход AOP к этой проблеме, но не был точно уверен, как это сделать. –

2

Проверил: 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() вы можете вызвать инжектор.injectMembers().