Frage zu EJB3, Session Beans und Vererbung

thinkpad

Grünschnabel
Hallo Leute,
bin gerad am einarbeiten in EJB3 und habe gleich eine Frage: Wie läuft mit der Vererbung? Soweit ich weiss, gibt es in einer JEE Anwendung auf der Logik-Seite (EJB's) eine Fassade. Der Client kann diese Objekte (oder auch Services) erreichen, indem er über den Namensdienst (JNDI) die Services aufsucht.

Wie sieht es nun aus, wenn sich innerhalb der EJB's Logik abspielt und auf verschiedene Session Beans zugegriffen werden soll. Muss dann jede Session Bean (auch wenn sie nicht vom Client aufgerufen wird) über den Namensdienst (lookup) aufgerufen werden? Ich frage mich dies, da ich momentan Probleme mit der Vererbung von Session Beans habe.

Folgendes Beispiel habe ich im Netz gefunden:

Code:
import java.io.Serializable;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;

public abstract class PersistentBean  implements Serializable, PersistentLocal {
    
    @PersistenceContext(unitName="hrsystem",
    type=PersistenceContextType.EXTENDED)
    private EntityManager entityManager;
    
    private Object object;
    
    public abstract void setObject(Object object);
    
    public abstract Object getObject();
    
    public String update() {
        entityManager.merge(object);
        return "Success";
    }
    
    public String create() {
        entityManager.persist(object);
        return "Success";
    }
    
    public String delete() {
        entityManager.remove(object);
        return "Success";
    }
}

Code:
import javax.ejb.Stateful;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.Scope;

/**
 *
 * @author Administrator
 */
@Stateful
@Scope(value=ScopeType.SESSION)
@Name(value="employeeBean")
public class EmployeeBean extends PersistentBean {
    
    /** Creates a new instance of EmployeeBean */
    public EmployeeBean() {
    }
    
    private Object object;
    
    public void setObject(Object object) {
        this.object = object;
    }
    
    @In(value="employee") @Out(value="employee")
    public Object getObject() {
        return object;
    }
    
}

Ist solch eine Vererbung mit Session Beans wirklich möglich? Falls ja, muss ich wirklich jede Session Bean über JNDI aufrufen?
Vielen Dank im Voraus
 
Hm, dieses Design finde ich ußerst kritisch. Soweit ist das auch nicht das, was EJB als grundsätzliches Vorgehensmodell propagiert. Großer achpunkt ist halt vor allem, dass der Karm nicht threadsafe ist und obendrein Stateful, was nicht sein müsste.

Enterprise Beans sollten soweit wie möglich stateless sein. Meistens arbeiten die direkt mit einerm EntityManager und persistieren über diesen Datenobjekte.

Java:
@Entity
public User implements Serializable {

  // jede Menge properties und getter und setter
}
Java:
@Statless
public UserManagermentImpl implements UserManagement {

  @PersistencContext
  private EntityManager em;

  public User findUser(Long id) {
    return em.find(User.class, id);
  }
}

D.h. du hast zum einen Klassen die reine Datencontainer sind (früher EntityBeans genannt) und Klassen, die technische und geschäftliche Logik abbilden und Referenzen zu anderen Serverkomponenten (in dem Fall dem EntityManager) haben. Bei den letzteren spricht man auch von Services.

Der Client braucht jetzt unr eine Referenz auf einen Service. Dieser bleibt aber auf dem Server und wird logischerweise auch dort ausgeführt. Er erhält dann aber ein Objekt User mit dem er lokal arbeiten kann. Hinter der Referenz des Services steht eigentlich nichts anderes als ein RMI Aufruf.

Alle Klarheiten beseitigt?

Gruß
Ollie
 
Hallo Oliver,

das Session Beans meist Stateless sein sollten (bis auf ein Warenkorb zum Beispiel) ist mir schon klar. Nur Frage ich mich, ob Vererbung überhaupt bei Session Beans genutzt wird bzw. ob das üblich ist?
Falls nicht, würde für jeden Service eine einzige Klasse/Session Bean entstehen, die viele Zeilen Code mit sich bringt. Ebenso entsteht duplicated code.
Ich habe es nun folgendermaßen geschafft mein Problem zu lösen und mit Vererbung zu arbeiten:

Code:
public interface SessionFacade{}

Code:
public class SessionFacadeBean implements SessionFacade{
private abstractSession = null; // Achtung: Ist das Interface!

getService(){
// Hier erfolgt das lookup:
InitialContext ic = new InitialContext();
if(...)
abstractSession = ic.lookup(".../ServiceABean/local");
else if(...)
abstractSession = ic.lookup(".../ServiceBBean/local");
}
}

Code:
public interface AbstractSession {}

Code:
public interface ServiceA extends AbstractSession {}

Code:
public interface ServiceB extends AbstractSession {}

Code:
public class AbstractSessionBean implements AbstractSession{
...
EntityManager em;
...
}

Code:
@Stateless
public class ServiceABean extends AbstractSessionBean implements ServiceA {
...}

Code:
@Stateless
public class ServiceBBean extends AbstractSessionBean implements ServiceB {
...}

Wie du siehst eine Menge Interfaces und eine Menge Vererbung. Ich versuchs mal an einem Beispiel zu erklären: Sagen wir, wir haben einen Service KontoTransaktionen als SessionBean. Dann gibt es KreditkartenKontoTransaktionen und BankkartenKontoTransaktionen als Services bzw. Session Beans. Die Entitäten seien hiervon unberührt, da dort keine Vererbung notwendig ist. Die Transaktionen von Kreditkarten und Bankkarten sind bis auf wenige Unterschiede identisch. Man könnte in meinen oberen Beispiel als AbstractSession Konotransaktionen als Service implementieren. KreditkartenkontenTransaktionen als Service A und BankkartenTransaktionen als Service B.

In meiner Implementierung oben haben wir in der Facade ein Object vom Interface, welches wir dann je nach Wunsch über das Lookup die KreditkartenTransaktionenBean oder die BankkartenTransaktionenBean übergeben könnten. Gemeinsame Methoden können wir in der Abstrakten Klasse implementieren und verschiedene Methoden in der abstrakten Klasse als abstrakte Methoden (was erzwingt, dass man diese Methoden in den Unterklassen implementieren muss).

Hoffe du hast es so verstanden? Falls nicht, beschäftige ich mich nochmal mit UML =)

Was mich an der ganzen Sache stört: Die Interfaces der Unterklassen (Service A und Service B) brauche ich eigentlich gar nicht. Problem ist bei mir nur das ich sonst eine Fehlermeldung beim deployen kriegen, dass das businiess interface für den Service fehlt. Mir fehlt zudem das Verständnis, wieso ich hier überhaupt mit einem "lookup" arbeiten muss, wenn die Klassen innerhalb desselben EJB Containers sind. Ich dachte solche Namensdienstaufrufe bräuchte man nur für entfernte Aufrufe (RMI).

Vielen Dank im Voraus =)
 
Hallo Oliver,

das Session Beans meist Stateless sein sollten (bis auf ein Warenkorb zum Beispiel) ist mir schon klar. Nur Frage ich mich, ob Vererbung überhaupt bei Session Beans genutzt wird bzw. ob das üblich ist?
Falls nicht, würde für jeden Service eine einzige Klasse/Session Bean entstehen, die viele Zeilen Code mit sich bringt. Ebenso entsteht duplicated code.
Ich habe es nun folgendermaßen geschafft mein Problem zu lösen und mit Vererbung zu arbeiten:
Hm, ich glaube fährst einen etwas komischen Designansatz. SessionBeans sind eigentlich schon Fassaden für ein Subsystem. D.h. in einer Geschäftsanwendung hättest du sowas wie ein UserManagement, AccountManagement usw. Da diese fachlich relativ unterschiedlich sind, macht es wenig Sinn da Vererbung einzusetzen. D.h. ich glaube dein Problem, zu viele KLassen und Interfaces zu haben ist eher ein Designproblem. Vielleicht wirfst du mal einen Blick in Eric Evans' "Domain Driven Design" - darin ist der Unterschied zwischen Entitäten und Services recht gut erklärt.

Wie du siehst eine Menge Interfaces und eine Menge Vererbung. Ich versuchs mal an einem Beispiel zu erklären: Sagen wir, wir haben einen Service KontoTransaktionen als SessionBean. Dann gibt es KreditkartenKontoTransaktionen und BankkartenKontoTransaktionen als Services bzw. Session Beans. Die Entitäten seien hiervon unberührt, da dort keine Vererbung notwendig ist. Die Transaktionen von Kreditkarten und Bankkarten sind bis auf wenige Unterschiede identisch. Man könnte in meinen oberen Beispiel als AbstractSession Konotransaktionen als Service implementieren. KreditkartenkontenTransaktionen als Service A und BankkartenTransaktionen als Service B.
Das ist genau das was ich meine. Wieso willst du unterschiedliche Logik unbedingt in verschiedenen Services implementieren? Wenn die Logik sich kaum unterscheidet, warum dann zwei Klassen? Ansonste würde ich eher anfangen über Vererbung bei den Entitäten nachzudenken.

Für mich klingt das nach so etwas:
Java:
@Stateless
public class AccountManagerImpl implements AccountManager {

  public void process(CreditCardTransaction transaction) {
  }

  public void process(BankCardTransaction transaction) {
  }
}

In meiner Implementierung oben haben wir in der Facade ein Object vom Interface, welches wir dann je nach Wunsch über das Lookup die KreditkartenTransaktionenBean oder die BankkartenTransaktionenBean übergeben könnten. Gemeinsame Methoden können wir in der Abstrakten Klasse implementieren und verschiedene Methoden in der abstrakten Klasse als abstrakte Methoden (was erzwingt, dass man diese Methoden in den Unterklassen implementieren muss).

Immernoch die Frage, wieso zwei verschiedene Komponenten? Es handelt sich um eine Komponente die Geldtransaktionen verarbeitet. Unterschiedliche Logik und Daten kannst du über verschiedene Methoden und Entitäten abdecken.

Was mich an der ganzen Sache stört: Die Interfaces der Unterklassen (Service A und Service B) brauche ich eigentlich gar nicht. Problem ist bei mir nur das ich sonst eine Fehlermeldung beim deployen kriegen, dass das businiess interface für den Service fehlt.

Du hast EJB gewählt, d.h. du musst auch mit den Nachteilen leben. Die (unbedingt benötigten) Interfaces sind da halt Teil davon. Grundsätzlich ist es aber durchaus sinnvoll Interfaces für Servicefassaden zu haben. Wie willst du die Clients sonst unittesten?

Mir fehlt zudem das Verständnis, wieso ich hier überhaupt mit einem "lookup" arbeiten muss, wenn die Klassen innerhalb desselben EJB Containers sind. Ich dachte solche Namensdienstaufrufe bräuchte man nur für entfernte Aufrufe (RMI). Vielen Dank im Voraus =)

Richtig, wenn der Client im gleichen COntainer läuft eicht auch ein @EJB an der property die deine Fassade referenziert.

Java:
public class Client implements HttpServlet {

  @EJB
  private AccountManager accountManager;

  // foo bar
}

REINHAUN!
 
Hm, ich glaube fährst einen etwas komischen Designansatz. SessionBeans sind eigentlich schon Fassaden für ein Subsystem. D.h. in einer Geschäftsanwendung hättest du sowas wie ein UserManagement, AccountManagement usw. Da diese fachlich relativ unterschiedlich sind, macht es wenig Sinn da Vererbung einzusetzen. D.h. ich glaube dein Problem, zu viele KLassen und Interfaces zu haben ist eher ein Designproblem. Vielleicht wirfst du mal einen Blick in Eric Evans' "Domain Driven Design" - darin ist der Unterschied zwischen Entitäten und Services recht gut erklärt.

Danke für den Literaturhinweis. Bisher habe ich folgendes Verständis: Entitäten sind Abbildungen der Datenbanktabellen. In diesen Klassen gehören lediglich die Attribute sowie Setter und Getter. Services implementieren die gesamte Geschäftslogik. Da liege ich wohl richtig oder?

Das ist genau das was ich meine. Wieso willst du unterschiedliche Logik unbedingt in verschiedenen Services implementieren? Wenn die Logik sich kaum unterscheidet, warum dann zwei Klassen? Ansonste würde ich eher anfangen über Vererbung bei den Entitäten nachzudenken.
Das ganze hat eigentlich nur einen Grund: duplicated Code vermeiden. Der Service den ich implementiert habe läuft schon Einwandfrei, jedoch sind es nun bis zu 1000 Zeilen Code. Viel zu viel meiner Meinung nach. Ich dachte durch Kapselung des Codes auf verschiedene Klassen (--> EJB's --> also Services) würde alles gerad übersichtlicher machen und wartbarer. Vererbung bei Entitäten kenne ich leider nicht, werde ich aber drüber nachdenken.


Für mich klingt das nach so etwas:
Java:
@Stateless
public class AccountManagerImpl implements AccountManager {

  public void process(CreditCardTransaction transaction) {
  }

  public void process(BankCardTransaction transaction) {
  }
}
Das würde so zwar funktionieren, jedoch hat man dann 2 Methoden, die eigentlich dasselbe tun bis auf wenige Zeilen. Natürlich könnte man auch nur diese 2 Zeilen auslagern. Jeder Java SE Programmierer würder sich aber Fragen: Wozu gibts denn sonst Vererbung? =)

Immernoch die Frage, wieso zwei verschiedene Komponenten? Es handelt sich um eine Komponente die Geldtransaktionen verarbeitet. Unterschiedliche Logik und Daten kannst du über verschiedene Methoden und Entitäten abdecken.

Du hast EJB gewählt, d.h. du musst auch mit den Nachteilen leben. Die (unbedingt benötigten) Interfaces sind da halt Teil davon. Grundsätzlich ist es aber durchaus sinnvoll Interfaces für Servicefassaden zu haben. Wie willst du die Clients sonst unittesten?
Ein Interface für die Fassade ist pflicht, sonst ist der Service für den Client nicht aufrufbar. Die Interfaces in den Unterklassen sind jedoch unnötig. Muss wohl mit den Nachteilen leben...


Zuletzt noch eine Frage:
Habe ich das dann richtig verstanden, dass man die Logik bzw. einen Anwendungsfall normalerweise nicht auf mehrere Services verteilt? Das heisst in einem Service wird nur Bezug zu den Entitäten genommen (und dies auch meist nur mit lokalen Attributen innerhalb von Methoden) und kein Bezug zu anderen Services?
--> Landen wir dann innerhalb eines Services (einer EJB) zur prozeduralen Programmierung, wenn wir die Entitäten wegdenken?
 
Danke für den Literaturhinweis. Bisher habe ich folgendes Verständis: Entitäten sind Abbildungen der Datenbanktabellen. In diesen Klassen gehören lediglich die Attribute sowie Setter und Getter. Services implementieren die gesamte Geschäftslogik. Da liege ich wohl richtig oder?
Grundsätzlich ist das richtig. Allerdings versucht man, die Entitäten nich reine Datencontainer sein zu lassen. D.h. Logik in Entitäten ist eigentlich auch explizit erwünscht. Bei vielen Anwendungen handelt es sich bei der "Logik" allerdings um reine Persistierungsoperationen. Diese werden dann aber eher von einem DAO abgehandelt bzw. bei EJB3 vom EntityManager. D.h. man kommt fast immer zu Entitäten mit vielen Gettern und Settern und ein bis zwei zusätzlichen Methoden.

Anm. das ist seeeehr stark vereinfacht und nicht 100% korrekt bzw erstrebenswert. Ich will die Sache lediglich nicht zu kompliziert darstellen.

Das ganze hat eigentlich nur einen Grund: duplicated Code vermeiden. Der Service den ich implementiert habe läuft schon Einwandfrei, jedoch sind es nun bis zu 1000 Zeilen Code. Viel zu viel meiner Meinung nach. Ich dachte durch Kapselung des Codes auf verschiedene Klassen (--> EJB's --> also Services) würde alles gerad übersichtlicher machen und wartbarer. Vererbung bei Entitäten kenne ich leider nicht, werde ich aber drüber nachdenken.
Wenn du Logik in einer Klasse hast, reicht, um duplizierten Code zu vermeiden, eine private Methode, die gemeinsame Logik übernimmt und vielleicht zwei öffentliche Methoden, die als Einsprungspunkt für unterschiedliche Anwendungsfälle dienen. Damit delegierst du die Fallentscheidung allerdings an den Client. Im Idealfall nutzt du die Vererbung zwischen den Entitäten um unterschiedliche FUnktionalität zu implementieren und lässt den Service nur auf der Superklasse arbeiten.

Das würde so zwar funktionieren, jedoch hat man dann 2 Methoden, die eigentlich dasselbe tun bis auf wenige Zeilen. Natürlich könnte man auch nur diese 2 Zeilen auslagern. Jeder Java SE Programmierer würder sich aber Fragen: Wozu gibts denn sonst Vererbung? =)
Huiuiui, ich glaube, da hat jemand Objektorientierung nicht verstanden. Vererbung ist nicht DAS Mittel um Codeduplikation zu vermeiden. Vererbung wird im allgemeinen auf völlig überbewertet. Nicht umsonst gibt es das Prinzip "Favour delegation over inheritance". Vererbung bildet eine "is-a" Beziehung ab. Nur wenn diese Bedingung zutrifft, sollte man darüber nachdenken, Vererbung einzusetzen. Nicht nach dem Motto: "Hey in der Klasse gibt es eine Methode, die ich hier brauchen könnte. Leite ich einfach davon ab."

Ein Interface für die Fassade ist pflicht, sonst ist der Service für den Client nicht aufrufbar. Die Interfaces in den Unterklassen sind jedoch unnötig. Muss wohl mit den Nachteilen leben...
Unterklassen? Interfaces sind in den seltensten Fällen unnötig, da sie erst Unittestbarkeit ermöglichen.

Zuletzt noch eine Frage: Habe ich das dann richtig verstanden, dass man die Logik bzw. einen Anwendungsfall normalerweise nicht auf mehrere Services verteilt? Das heisst in einem Service wird nur Bezug zu den Entitäten genommen (und dies auch meist nur mit lokalen Attributen innerhalb von Methoden) und kein Bezug zu anderen Services?
--> Landen wir dann innerhalb eines Services (einer EJB) zur prozeduralen Programmierung, wenn wir die Entitäten wegdenken?
Jein. Ich weiß nicht, wie ihr zu einer Klasse mit 1000 Zeilen kommt aber normalerweise gibt es sowas wie Design by Responsibility. Und das sieht man am besten zu allererst fachlich. D.h. man teilt sein System in fachliche Teile auf: Benutzermanagement, Kundenmanagement, Transaktionsmanagement (Banktransaktionen wohlgemerkt) usw. Diese bestehen dann zu allererst mal aus einer Reihe von Entitäten (oft mit einem Aggregate Root (siehe DDD vom Evans)) und einer ServiceFassade. Wie jetzt der Service implementiert ist, ist (fast) völlig wurscht. Der kann gut und gern Referenzen z anderen Services haben, mit denen kommunizieren - aber, wohlgemerkt durch Interfaces definierte. So erreichst du lose Kopplung zuwischen Komponenten.

Allerdings hast du nicht ganz unrecht. Durch die (in der EJB Welt übliche) Trennung von Daten (Entitäten) und Logik (Services) ist man recht schnell bei einem sehr prozeduralen Programmierstil. Dem treten so Ansätze wie das DDD vom Evans entgegen. Das Problem ist, dass die Technologie diese Designform unterstützt - es also sehr einfach ist so zu programmieren. Schließlich hat sich dieser Stil ja auch bewährt.

Es gibt mit Spring z.B. allerdings auch die Möglichkeit Entitäten Referenzen auf Services zu verschaffen und somit wesentlich objektorientierter zu arbeiten. Da steckt noch viel Potential ;).

Gruß
Ollie
 
Hallo Ollie,

vielen dank für deine Hilfe. Mir ist schon klar, dass Vererbung nicht immer ideal ist und Interfaces auch benötigt werden. In meinen oberen Beispiel machen aber die Interfaces der Unterklassen wenig Sinn, da man Methodenvorschreibungen bereits durch die abstrakte Oberklasse festlegen kann oder im Interface der abstrakten Oberklasse (und diese dann in den Unterklassen implementiert).
Bei meinem speziellen Fall geht es um die Erstellung von benutzerdefinierten Selektionen aus der Datenbank. Da ich mit Hibernate und Criterias arbeite, hat sich nun eine Menge Code in diesen einen Service angesammelt um die Selektionen nach verschiedenen Kriterien durchzuarbeiten. Da es Selektionen nach Kontakten oder Firmen gibt, wäre schon eine IsA Beziehung richtig, da sich letztendlich nur 3 oder 4 Methoden unterscheiden.

Ich werde aber zunächst dann wohl bei diesen einen Service bleiben und bei diesen Methoden abfragen einbauen.

Vielen Dank nochmal für deine Hilfe...
 

Neue Beiträge

Zurück