2012-04-02 9 views
2

... zumindest, wenn das Konzept eines "eleganten Workarounds" tatsächlich einen Sinn hat!Weld (CDI) und Datanucleus (JPA) spielen nicht gut, gibt es einen eleganten Workaround?

Hier einige Details:

Wenn CDI und JPA verwenden, werden Sie wollen oft eine Ihrer JPA-Managed Beans für den Zugriff auf in einem EL-Expression auf einer Ihrer JSF-Seiten, etwa so:

<html xmlns="http://www.w3.org/1999/xhtml" 
     xmlns:h="http://java.sun.com/jsf/html" 
     xmlns:f="http://java.sun.com/jsf/core"> 
    <h:head> 
    <title>An Everyday Facelet</title> 
    </h:head> 
    <h:body> 
    <h1>Behold, a jpa-managed bean property!</h1> 
    <h:outputText value="#{exampleEntity.name}" /> 
    </h:body> 
</html> 

Der Einfachheit halber nehmen wir an, dass diese Seite das Ergebnis eines HTTP-Posts ist und dass der Client einen Wert für die Eigenschaft name auf der vorherigen Seite eingegeben hat. Die Bohne selbst kann eine einfache JPA-Entität von einer Producer-Methode abgerufen wird, etwa so:

@Entity 
public class ExampleEntity implements Serializable { 
    @Id 
    @Column 
    private Long entityId; 
    @Column 
    private String name; 

    public ExampleEntity() { 
    } 

    public void setEntityId(Long entityId) { 
     this.entityId = entityId; 
    } 
    public Long getEntityId() { 
     return entityId; 
    } 
    public void setName(String name) { 
     this.name = name; 
    } 
    public String getName() { 
     return name; 
    } 
} 

// Some method in another class 
public class AnotherClass { 
    @Produces 
    @RequestScoped 
    @Named("exampleEntity") 
    public ExampleEntity getExampleEntity() { 
     return new ExampleEntity(); 
    } 
} 

In Ordnung, lassen Sie sich kompilieren. Ein wichtiger Aspekt der Datanucleus-JPA-Implementierung ist, dass es einen Post-Compile-Schritt gibt. Sie müssen einen Bytecode-Enhancer für Ihre kompilierten jpa-Klassen ausführen. Nehmen wir an, wir haben das gemacht. Wenn Sie versuchen, Ihren Servlet-Container zu starten (zB Tomcat, Jetty), erhalten Sie beim Starten von Weld eine Ausnahme (mehrere, wenn Sie tatsächlich versuchen, diese Bean irgendwo zu injizieren). Unter der Annahme, meine Klassen sind in der 'testapp' -Paket, ist die Ausnahme so etwas wie:

org.jboss.weld.exceptions.UnproxyableResolutionException: WELD-001437 Normal scoped bean class testapp.ExampleEntity is not proxyable because the type is final or it contains a final method public final java.lang.Object testapp.ExampleEntity.jdoGetObjectId() - Managed Bean [class testapp.ExampleEntity] with qualifiers [@Any @Default @Named]. 

public final java.lang.Object jdoGetObjectId()? Ich habe weder einen noch einen guten Webserver definiert oder deklariert. Sie müssen sich irren!

Stellt sich heraus, dass der Datanucleus Enhancer diese letzte Methode zu meiner kompilierten Klasse hinzufügen muss. Der Webserver startet gut, wenn ich ihn nicht ausführe, obwohl ich annehme, dass meine Daten nicht bestehen bleiben. Mit ein wenig Nachforschung konnte ich eine Erklärung des Problems in der Weld-Dokumentation finden: http://docs.jboss.org/weld/reference/1.1.5.Final/en-US/html/injection.html#d0e1429

Kurz gesagt, es sieht so aus, als ob benannte Bohnen keine endgültigen Methoden haben können. Nach einigen Ratschlägen habe ich eine funktionierende Lösung gefunden. Ich deklariere eine Schnittstelle für meine jpa-Bean und erstelle eine Producer-Methode, um eine konkrete Klasse zu erhalten (was eigentlich die Datanucleus-Klasse ist, aber Weld weiß es nicht). Hier ist der Zusatzcode:

public interface ExampleEntity { 
    public Long getEntityId(); 
    public void setEntityId(Long entityId); 
    public String getName(); 
    public void setName(String name); 
} 

Nun, da ExampleEntity ist lediglich eine Schnittstelle, erstelle ich eine JPA-verwaltete Implementierung ohne CDI Anmerkungen

@Entity 
public class ExampleEntityImpl implements ExampleEntity, Serializable { 
    // Same as the ExampleEntity class above 
} 

Und schließlich, vielleicht in einem Controller irgendwo definiere ich einen Produzenten Methode zum Erhalten von ExampleEntities

@Named("exampleEntityController") 
@RequestScoped 
public class ExampleEntityController { 

    private ExampleEntity newExampleEntity; 

    public ExampleEntityController() { 
     newExampleEntity = new ExampleEntityImpl(); 
    } 

    // The producer method 
    @Produces 
    @RequestScoped 
    @Named("exampleEntity") 
    public ExampleEntity getExampleEntity() { 
     return newExampleEntity; 
    } 

    // Other stuff, because surely a controller does more than just this, right? 
} 

Und ... das funktioniert. Der Webserver startet, keine Ausnahmen. Das name-Attribut wird in diesem Beispiel-Facelet ordnungsgemäß abgerufen (vorausgesetzt, Daten wurden bereits auf einer vorherigen Seite eingegeben). Das Erstellen von einmaligen Schnittstellen und Producer-Methoden für jede jpa-gesteuerte Bean ist jedoch ziemlich überflüssig und hässlich. Gibt es eine bessere Lösung? Datanucleus ist eine Voraussetzung für dieses Projekt, daher kann ich nicht einfach etwas anderes verwenden. Wenn es einen "eleganteren Workaround" gibt, dann stelle ich mir vor, dass es eine bessere Methode ist, um Weld dazu zu bringen, mir eine finale Methode zu geben, die ich nie in einer meiner benannten Beans verwenden werde.

Edit: Danke für die Antworten/Korrekturen, aber ich bin auf der Suche nach einer Möglichkeit zu vermeiden, eine Schnittstelle für jede jpa Bohne zu erstellen. Ich habe das Mischen von JPA/CDI-Anmerkungen aus den Beispielen entfernt, aber der Webserver wirft immer noch die gleiche Ausnahme ab. Ich kann sehen, dass ich auf jeden Fall eine Produzentenmethode brauche.

+0

leider verarbeiten kann, wie bist du gekommen, um '@Named "@ RequestScoped'' @ Entity'? – Osw

+0

Nun, der Bereich ist in diesem Beispiel irgendwie willkürlich, aber es ist nur eine benannte Bean, die ich auch mit der JPA-API persistieren kann. Das ist sowieso das Ziel. Es funktioniert für mich in einem vollständigen Anwendungsserver. – Shaun

+1

DataNucleus Bytecode-Erweiterung fügt viele Methoden, zu einer standardisierten API, gemäß http://db.apache.org/jdo/enhancement.html – DataNucleus

Antwort

1

Sie sollten CDI nicht für Ihre Entitäten verwenden.

  • Einheiten werden von Ihnen erstellt, mit dem new Operator
  • CDI Bohnen vom Container erstellt werden, so dass es Injektionen und Initialisierung
1

CDI-Anmerkungen nicht mit JPA mischen. Was Sie wirklich tun müssen, ist einen Produzenten für jede Entität zu erstellen, die Sie mit CDI verwenden möchten. JPA muss die Instanz erstellen, dann würde ein Produzent zulassen, dass sie in einer CDI-Umgebung verwendet oder für die EL-Nutzung benannt wird.