2008-12-16 9 views
3

Je cherche des suggestions sur la façon d'injecter des dépendances d'exécution dans les entités JPA récupérées depuis Hibernate. Mon problème est essentiellement ceci:Suggestions de motifs nécessaires (Hibernate + Guice)

J'ai un certain nombre de sous-classes différentes d'un objet de transaction. Chaque sous-classe Transaction a un comportement différent lorsqu'elle est exécutée et nécessite un ensemble différent de dépendances de l'environnement. Ces objets Transaction sont gérés en tant qu'entités JPA par Hibernate, donc je ne peux pas utiliser efficacement Guice pour l'injection de dépendance pour remplir les instances avec leurs dépendances environnementales comme je le fais dans le reste de mon application.

Pour contourner ce problème, j'ai pris une approche qui est un peu semblable au modèle des visiteurs, comme suit:

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(...)  
    } 
} 

J'ai différentes implémentations de Transactor pour différentes parties du cycle de vie des transactions. Ceux-ci peuvent être injectés en utilisant la dépendance-Guice dans le contexte dans lequel je veux traiter les transactions, où j'appelle simplement:

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

Ce que je n'aime pas cette approche est (1) Non tous les types de transaction devrait être exécutable dans chaque phase du cycle de vie, mais chaque Transactor doit implémenter une méthode execute() pour chaque sous-classe de transaction et (2) Essentiellement, aucune des dépendances injectées n'est utilisée pour traiter plus d'un type de transaction. Essentiellement, mes implémentations de l'interface Transactor & ont beaucoup de crud non reliées ensemble. Idéalement, je voudrais juste avoir la méthode execute() sur l'objet de transaction lui-même, mais je ne veux pas que le code appelant sache le type de la transaction ou les dépendances dont elle a besoin. En outre, cela pourrait rendre les tests plus difficiles car je ne pourrais pas facilement simuler la méthode execute() s'il s'agissait d'une méthode concrète sur l'objet Transaction. L'utilisation de l'interface Transactor signifie que je peux facilement le simuler au besoin. Quelqu'un peut-il suggérer comment résoudre ce problème d'une manière sécurisée qui n'entraîne pas le regroupement de plusieurs comportements non apparentés dans le Transactor, mais qui conserve la testabilité et permet l'injection de dépendances?

Répondre

5

J'utilise guice pour les transactions, mais j'utilise l'AOP pour les réaliser. Je n'ai presque pas de passe-partout, au détriment d'un peu de "magie". Tant que votre classe interceptée est «dans le club», cela fonctionne très bien.

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

C'est excellent, merci! J'avais soupçonné qu'il pourrait y avoir une approche AOP à ce problème, mais je ne savais pas exactement comment s'y prendre. –

2

Vérifier: http://www.hibernate.org/hib_docs/v3/api/org/hibernate/Interceptor.html

Configurez votre mise en veille prolongée d'utiliser un Intercetor. La méthode

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

sera appelé à

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. 

Vous pouvez appeler votre injector.getInstance() à partir de là. Peut-être que vous pourriez utiliser les méthodes getEntity() ou onLoad(). À partir de la méthode onLoad(), vous pouvez appeler injector.injectMembers().

Questions connexes