Discussion:
ORM (Hibernate) auch ohne DAO?
(zu alt für eine Antwort)
Cybernd
2003-07-14 23:18:19 UTC
Permalink
Hallo Ihr,
ich bin noch recht neu in der Persistenzwelt und habe daher ein etwas
grundlegenderes Problem. Ich habe ein nettes OO-Modell, auf dass ich über
eine Singleton-Instanz zugreifen will. Nun möchte ich gerne dieses Modell
via Hibernate persistieren. Beim stöbern im Internet bin ich aber immer nur
auf Lösungen durch DAOs gestossen. Ich würde aber eigentlich gerne weiterhin
transparent auf mein OO-Modell zugreifen können, ohne explizit ein
dazugehöriges DAO aufzurufen. Ich stelle mir das so vor, dass jede Änderung
an einem Objekt via PropertyChangeEvent dem Singleton mitgeteilt wird,
welches dann das Objekt speichert. Genauso möchte ich auch nur ungerne
explizit löschen. List.remove() oder object = null sollte reichen. Ziel der
Sache soll sein, dass das Modell nicht requestzentriert ist sondern im
Speicher (und in der DB) existiert und konsistent mit der DB ist. (also auch
keine Queries)
Nun meine Frage: Spricht etwas dagegen, ist das schlechter Stil oder warum
findet man zu diesem Ansatz sonst nix? Lässt sich das mit gegenwärtigen
OR-Mappern überhaupt realisieren?
hmm eigentlich hoffte ich ja das jemand deinen beitrag beantwortet,
damit ich eine chance habe daraus zu lernen

ich selbst habe momentan das problem das ich bei der anwendung von
hibernate selbst eine art dao pattern herausgearbeitet habe ohne zu
wissen das es ein dao überhaupt gibt

der unterschied zum echten dao paper von sun:

ich benutze physische implementierungen und erspar mir die möglichkeit
die daos in abhängigkeit der datenbank über den broker zu holen (zu was
auch .. wenn ich die db ändere, muß ich das hibernate property ändern,
kann aber mit der selben dao implementierung dieses problem realisieren)

ich selbst hab diese dao objekte immer manager genannt, also eine klasse
Member hat somit einen MemberManager, der mir die db zugriffe kapselt

kommt aber dem ansatz des daos erstaunlicherweise nahe

somit hab ich mir dann auch selbst irgendwie die antwort auf die
notwendigkeit der daos gegeben ..

immerhin hat sich dieses dao bei mir herauskristallisiert ohne jemals
etwas vom dao gehört zu haben, wodurch ich der meinung bin das es einen
sinn macht diese art von zugriffsobjekte zu erstellen

ursprünglich waren meine objekte auch noch zusätzlich ein wenig anders,
denn sie waren nur statische methodensammlungen

hierbei hat sich dann aber herauskristalliseriert, das es einfacher ist
diese daos zu instanzieren, weil man sich somit erspart jedes mal von
neuem die hibernate session zu übergeben ...

diese hibernate session muß ja mitgeschliffen werden, da diese nicht
threadsafe sind und somit immer wieder von neuem aus dem pool geholt
werden müssen

ich selbst muß aber sagen das ich diese methodik noch immer als
unprikelnd empfinde denn eine echte transparente persistent, die ja
immer wieder von den mappern angestrebt wird existiert einfach nicht

in den funktionen des daos muß halt immer wieder die transaktion
geöffnet werden, worauf hin ein typisches session.save(object); oder ein
delete folgt um dann wieder die tx zu committen

hierbei müsste doch tx.start() bis zu tx.commit(); ausreichen und ein
echter transparenter persistierer somit irgendwie "by magic"? in der
lage sein die änderungen an den objekten zu reflektieren, also auch dein
angesprochenes löschen der objekte irgendwie automatisch mitkriegen können

hast du mittlerweile irgendwie eine idee herausgearbeitet die dich bei
diesem problem weitergebracht hat? ich muß hierbei passen ..

ich weiß lediglich das diese mapper zum teil sehr unausgereift sind ..

man betrachte mal hibernate ..
ist doch ein realistisches szenario, das man bei einem objekt ein
änderungsflag mitführen möchte

hierzu gibt es die möglichkeit eine klasse an der hibSession zu
registrieren, die dann die properties durchackert und beim finden eines
schlüsselwortes wie z.b. lastUpdated automatisch den neuen timestamp bei
änderung des objektes einfügt ...

das dumme ist nur das man diese klasse nicht in das mapping einer klasse
hinzufügen kann, sondern im java code an die session hängen muß, wodurch
es zu zwei problemen kommt: a) jede persistierte klasse in dieser
session durchläuft diese klasse und b) man hat die möglichkeit das
anhängen der klasse an eine session versehentlich zu vergessen, weil es
eben nicht hardverdrahtet am mapping der klasse hängt ...

bin ich denn wirklich der erste der etwas derartiges umzusetzen
versuchte? kann es denn wirklich sein das ein derartig lange gereiftes
produkt wie hibernate noch immer nicht weiter ist in diesem einem
speziellen punkt?

ich weiß eine blumige metapher, aber ich hoffe du verstehst worauf ich
dabei hinaus wollte

die ansätze wie torque sind ja auch nciht gerade besser ..
bei diesen mappern wird die persistierende klasse verändert, wodurch es
ein objekt.save(); gibt um dieses objekt zu persistieren ..

gerade dieses verändern der zu persistierende klasse kann meines
erachtens auch nicht der richtige weg sein einen derartigen mapper
umzusetzen - sogesehen ist hibernate immerhin schon auf der besseren
seite (meine subjektive meinung)

deine idee mit den PropertyChangeEvent empfinde ich auf jedenfall als
einen sehr interessanten ansatz, aber könnte es nicht sein das dadurch
sehr viele probleme entstehen? wie steuert man dann den anfang bzw. das
ende der transaktion? also was passiert wenn ein property außerhalb
einer transaktion geändert werden würde

könnte es nicht sein, das diese variante nie von derartigen mappern in
betracht gezogen wurde, da es möglicherweise ein unperformantes mapping
ergeben würde? schließlich würde jede geänderte property ein event
feuern. bei manuellem auslösen von save() wird jedoch nur nach dem
letzten auftretenden event in dieser transaktion etwas ausgelöst

vor allem auch die frage: könnte man es wirklich schaffen ein
?singleton? zu schreiben, welches in der lage ist mit beliebigen dort
registrierten objekten deine idee eines automatischen mappings mit
hibernate umzusetzen, oder hätte man dann das problem das man mit der
public api von hibernate nicht auskommt, also interne änderungen nötig
wären (singleton unter fragezeichen, da ich es besser finden würde wenn
es eine applikationsweite instanz wäre also knapp kein singleton)

dann noch die problematik mit den einzelnen hibernate sessions
schließlich kommt in einer realen applikation einfach das multithreaded
environment vor in dem wieder ein connection pool mit konkurrierenden
sessions notwendig wäre, wobei man vermutlich von der applikationsweiten
instanz sogar auf gepoolte instanzen pro thread herunterbrechen müsste

schon mal danke im vorhinein, falls du dir die mühe machst auf meine
mail zu antworten

cybi alias Neuhauser Bernhard
Christian Bauer
2003-07-14 23:48:43 UTC
Permalink
Post by Cybernd
ich benutze physische implementierungen und erspar mir die möglichkeit
die daos in abhängigkeit der datenbank über den broker zu holen (zu was
auch .. wenn ich die db ändere, muß ich das hibernate property ändern,
kann aber mit der selben dao implementierung dieses problem realisieren)
Ja, die DAO Factory wird hier etwas ueberfluessig. Applikationen die
nicht nur zwischen SQL DBMS wechseln sondern sogar zwischen XML oder
relationalaer Persistenz halte ich fuer recht selten...
Post by Cybernd
hierbei hat sich dann aber herauskristalliseriert, das es einfacher ist
diese daos zu instanzieren, weil man sich somit erspart jedes mal von
neuem die hibernate session zu übergeben ...
Ja, neues DAO erzeugen ist OK, aber Du kannst evtl. innerhalb eines
Thread die gleiche Hibernate Session verwenden. Das ThreadLocal-Beispiel
im Hibernate wiki ist dafuer gut. Ich finde diese Loesung sehr elegant,
besonders beim Thema lazy loading.
Post by Cybernd
diese hibernate session muß ja mitgeschliffen werden, da diese nicht
threadsafe sind und somit immer wieder von neuem aus dem pool geholt
werden müssen
Oder ThreadLocal, s.o.
Post by Cybernd
ich selbst muß aber sagen das ich diese methodik noch immer als
unprikelnd empfinde denn eine echte transparente persistent, die ja
immer wieder von den mappern angestrebt wird existiert einfach nicht
Es wird sich auch nicht komplett geben (erstmal muesste man sich auf
eine Definition des Begriffs einigen), solange man im Java Bytecode
frickeln muss um state change/dirty checking zu implementieren. Hooks
dafuer (wie von Carl oefter mal vorgeschlagen) wird es wohl auch auf
absehbare Zeit nicht geben...
Post by Cybernd
hierbei müsste doch tx.start() bis zu tx.commit(); ausreichen und ein
echter transparenter persistierer somit irgendwie "by magic"? in der
lage sein die änderungen an den objekten zu reflektieren, also auch dein
angesprochenes löschen der objekte irgendwie automatisch mitkriegen können
Das geht doch ohne Probleme, gerade mit einer ThreadLocal Sessinon. Ich
verwende DAOs in etwa so:

MyDAO dao = MyDAOFactory.getInstance();
dao.beginTransaction();

...

dao.endTransaction();

Dazwischen mache ich mit den Objekten was ich will, typischerweise Laden
oder re-connect zur Transaktion mit update() (version check) und sonst
nix. Sobald ein Object im scope der Transaktion ist, sollte ein
"automatischer" Persistenzmechanismus den state zur Datenbank committen.
Post by Cybernd
bin ich denn wirklich der erste der etwas derartiges umzusetzen
versuchte? kann es denn wirklich sein das ein derartig lange gereiftes
produkt wie hibernate noch immer nicht weiter ist in diesem einem
speziellen punkt?
Ehm, so ganz verstanden habe ich nicht, was Du da vorhast :)
--
Christian Bauer
Cybernd
2003-07-19 14:02:11 UTC
Permalink
Hi

Ich wollte mich nur bei Christian für das liefern eines brauchbaren
Ansatzes bedanken :o)
Post by Christian Bauer
MyDAO dao = MyDAOFactory.getInstance();
dao.beginTransaction();
...
dao.endTransaction();
Das ganze ist nach genauerer Betrachtung eine sehr gute Möglichkeit der
Transaktionsverwaltung, da ja die eigentliche Dao Instanz nur für den
einen Thread gültigkeit hat. Ich versuchte bisher immer das ganze so zu
realisieren das eine DAO Methode gleichzeitig start und ende der
Transaktion handhabt, was allerdings nur dazu führte, das die Zustände
nur mehr schwer zu kontrollieren sind.

Also wenn z.b. eine Dao Methode eine andere Dao Methode benutzt war für
mein Vorgehen eine Sonderbehandlung ohne Transaktionsstart /
Transaktionsende in dieser einen Methode notwendig.

An dieser Stelle möchte ich noch anmerken, das Ich nicht weiß, wieso ich
nie in Erwägung gezogen habe das Transaktionshandling von außen
steuerbar zu gestalten ...

(mein bisheriger ansatz war: in pseudocode

Dao.methode()
{
tx.start();
... do something
tx.commit();
}
)
Post by Christian Bauer
Post by Cybernd
hierbei hat sich dann aber herauskristalliseriert, das es einfacher ist
diese daos zu instanzieren, weil man sich somit erspart jedes mal von
neuem die hibernate session zu übergeben ...
Ja, neues DAO erzeugen ist OK, aber Du kannst evtl. innerhalb eines
Thread die gleiche Hibernate Session verwenden. Das ThreadLocal-Beispiel
im Hibernate wiki ist dafuer gut. Ich finde diese Loesung sehr elegant,
besonders beim Thema lazy loading.
*anguck*
Danke für den Tip. Bin leider relativ neu auf dem Gebiet und merke
gerade das ich viele gute Ansätze einfach nicht kenne ;o)

Wenngleich ich zugeben muß, das ich die Idee die Session an den Thread
zu binden bereits einmal hatte und wieder verworfen habe. Die Begründung
für das Verwerfen der Session war, das ich mir einfach dachte, das so
etwas sicher nur mit viel Overhead verbunden verwirklichbar sein wird ..

(Also was den Lookup zum Threadkontext betrifft)

Wenngleich es jetzt interessant wäre zu wissen, wie ein Webcontainer die
Threads handhabt. Sind es immer wieder die selben Threads aus einer Art
Threadpool? Oder wird für jeden neuen Request immer ein neuer unique
Thread gestartet? ;o) Bei ersterem müsste die Threadlocale
Implementierung sogar von Vorteil sein. Bei zweiterem sehe ich keinen
direkten Vorteil, da ich ja im ControllerServlet einen zentralen Punkt
zum verwalten der Hibernate Sessions habe. Der einzige Vorteil wäre
dann, das die Dao Factory die hibSessions selbstständig beziehen kann
und ich mir somit die übergabe der hibSession im Konstruktor des Daos
erspare.
Post by Christian Bauer
Post by Cybernd
bin ich denn wirklich der erste der etwas derartiges umzusetzen
versuchte? kann es denn wirklich sein das ein derartig lange gereiftes
produkt wie hibernate noch immer nicht weiter ist in diesem einem
speziellen punkt?
Ehm, so ganz verstanden habe ich nicht, was Du da vorhast :)
Okay nochmal etwas einleuchtender:

Gegeben sei:
Dings.lastUpdated als Zeit

Jetzt sollte dieses Attribut so akkurat wie möglich gehandhabt werden,
also sollte idealerweise Hibernate das updaten dieses Flags durchführen.

Meine Idee war, das man hierfür einen Interceptor schreibt, der einfach
bei jedem Objekt vor dem Persistieren die update(); ausführt

Das Problem hierbei sehe ich aber darin, das man den Interceptor per
Session session = sf.openSession( new Interceptor() ); an die Session bindet

Das ganze ist nun an einer Stelle die nichts mit der Klasse selbst zu
tun hat, wodurch man das registrieren des Interceptors vergessen kann.

Vergisst man dieses hinzufügen, wirkt sich das lediglich in
inkonsistenten Daten aus, aber man erhält keine Fehlermessage das man es
vergessen hat.

Weiters denke ich das es nicht gerade ideal ist, das dieser Interceptor
auf alle Objekte dieser hibernate Session angewandt wird. D.h. wieso muß
ich alle persistierte Objekte durch den Interceptor laufen lasse, wenn
nur das Dings objekt wirklich ein update flag enthält?

Wäre es somit nicht besser, wenn man das binden des Interceptors im
Klassenmapping durchführt?

also etwas in der art
<class ... ><interceptor class="full.qualified.Interceptor"/></class>

Somit würde der Interceptor nur mehr bei den Objekten verwendet werden,
die wirklich diesen Benötigen und zudem könnte man nicht vergessen, das
man Ihn in der Session aktiviert.

Vermutlich liege ich aber jetzt total daneben und es existiert
vermutlich auch ein Ansatz der für mein Vorhaben gedacht ist ;o) Nur
kenn ich Ihn leider nicht ..

thx nochmal für deinen Beitrag
cybi
Christian Bauer
2003-07-19 14:29:13 UTC
Permalink
Post by Cybernd
Vermutlich liege ich aber jetzt total daneben und es existiert
vermutlich auch ein Ansatz der für mein Vorhaben gedacht ist ;o) Nur
kenn ich Ihn leider nicht ..
Du bist auf dem besten Weg Dir ein eigenes Inversion Of Control (IOC)
framework zu bauen. Wirf doch mal einen Blick auf Spring, die
Hibernate-Seite dazu im Wiki und die Diskussion im Forum um das Thema:

http://sourceforge.net/forum/forum.php?thread_id=898425&forum_id=128638
--
Christian Bauer
Cybernd
2003-07-20 17:15:52 UTC
Permalink
Post by Christian Bauer
Post by Cybernd
Vermutlich liege ich aber jetzt total daneben und es existiert
vermutlich auch ein Ansatz der für mein Vorhaben gedacht ist ;o) Nur
kenn ich Ihn leider nicht ..
Du bist auf dem besten Weg Dir ein eigenes Inversion Of Control (IOC)
framework zu bauen. Wirf doch mal einen Blick auf Spring, die
http://sourceforge.net/forum/forum.php?thread_id=898425&forum_id=128638
Danke, Spring kannte ich noch nicht. Wenngleich ich jetzt auf Anhieb
nicht erkennen konnte, was genau es darstellt ;o) Hunderte Seiten
Blabla, aber die signifikante Erklärung fehlt .. (Guck ich mir später
genauer an)

Btw. eigentlich wollte ich wegen etwas anderem Posten.
Hab mir gerade den Tomcat im Debugmodus genauer angesehen (Wegen der
Threadlocalen)

Sieht wirklich so aus, als würden nur 10 Threads laufen, die immer
wieder verwendet werden. D.h. die Requests werden immer wieder an die
selben Threads gefüttert, wobei halt max. 10 parallele Threads laufen
können.

(Zugegeben mit provokativem, schnellen Reloaden im Browser wurden meine
Requests von max. 3 Threads bearbeitet, wenn ich mir aber die
Namensgebung im Debugger ansehe, würde ich auf 10 Threads schließen)

Somit macht nun die Idee mit den Threadlokalen absolut Sinn, denn somit
werden die Sessions nicht ständig neu aufgebaut und es können somit auch
die Cachemechanismen der Hibernate Sessions Wirkung erlangen.

Zudem noch der Vorteil das ich mir das übergeben dieser lästigen
Variable erspare .. :o)

In der Tomcat Doku konnte ich keinerlei Hinweis auf das
Threadmangagement finden, jedoch macht die beobachte Vorgehensweise im
Debugger für mich durchaus Sinn, da ja das Erzeugen eines neuen Threads
die Ressourcen belasten würde.

cybi
Stefan Matthias Aust
2003-07-20 18:31:25 UTC
Permalink
Post by Cybernd
Sieht wirklich so aus, als würden nur 10 Threads laufen, die immer
wieder verwendet werden. D.h. die Requests werden immer wieder an die
selben Threads gefüttert, wobei halt max. 10 parallele Threads laufen
können.
Die minimale und maximale Anzahl von Thread kann in der server.xml
konfiguriert werden.


bye
--
Stefan Matthias Aust // "Ist es normal, nur weil alle es tun?" -F4
Patrick Roemer
2003-07-19 16:32:39 UTC
Permalink
Hallo,
Post by Cybernd
Post by Christian Bauer
MyDAO dao = MyDAOFactory.getInstance();
dao.beginTransaction();
...
dao.endTransaction();
Das ganze ist nach genauerer Betrachtung eine sehr gute Möglichkeit der
Transaktionsverwaltung, da ja die eigentliche Dao Instanz nur für den
einen Thread gültigkeit hat.
Wobei IMO ein ungutes Gefuehl bleibt, wenn man sich darauf verlassen
muss, dass der Client das Transaktionshandling auch vernuenftig
betreibt, insbesondere, dass Transaktionen auf jeden Fall explizit
beendet werden.
Post by Cybernd
Ich versuchte bisher immer das ganze so zu
realisieren das eine DAO Methode gleichzeitig start und ende der
Transaktion handhabt, was allerdings nur dazu führte, das die Zustände
nur mehr schwer zu kontrollieren sind.
Also wenn z.b. eine Dao Methode eine andere Dao Methode benutzt war für
mein Vorgehen eine Sonderbehandlung ohne Transaktionsstart /
Transaktionsende in dieser einen Methode notwendig.
Ich habe schon auf x verschiedene Arten versucht, z.B. den in Ruby oft
verwendeten Ansatz mit Blockuebergabe irgendwie auf Java zu uebertragen.
Da koennte man z.B. sowas machen:

dao.transaction {|context| doSomethingWith(context)}

Die transaction-Methode wuerde dann implizit eine Transaktion starten,
dabei irgendeine Art von Kontext erzeugen, den uebergebenen Block mit
diesem Kontext als Parameter aufrufen und die Transaktion danach auf
jeden Fall beenden. Wenn man das in Java nachempfinden will, wird es
leider schnell sehr umrandig.

Eine spontane Schnapsidee, die mir gerade eben kam, waere die Verwendung
eines Proxy:

public interface TransactionEnabled {
void startTransaction();
void endTransaction();
}

public class TransactionHandler implements InvocationHandler {
TransactionEnabled delegate;
Method startTransaction;
Method endTransaction;

public TransactionHandler(TransactionEnabled delegate) {
this.delegate = delegate;
try {
startTransaction = delegate.getClass().getMethod(
"startTransaction", new Class[]{}
);
endTransaction = delegate.getClass().getMethod(
"endTransaction", new Class[]{}
);
}
catch (NoSuchMethodException e) {
throw new RuntimeException("Shouldn't have happened: "+e,e);
}
}

public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object retval = null;
startTransaction.invoke(delegate, new Object[]{});
try {
retval = method.invoke(delegate, args);
}
catch(InvocationTargetException itexc) {
throw itexc.getTargetException();
}
finally {
endTransaction.invoke(delegate, new Object[]{});
}
return retval;
}
}

Wahrscheinlich nicht besonders praktikabel, schon aus
Performancegruenden, aber das oben beschriebene Problem wuerde es
loesen: Die Methoden wuerden so geschrieben, dass sie stets davon
ausgehen, sich in einer Transaktion zu befinden, und dem Client wird ein
Proxy mit diesem Handler zur Verfuegung gestellt, der um jeden
Methodenaufruf die Transaktionen wickelt.

Viele Gruesse,
Patrick
Lesen Sie weiter auf narkive:
Loading...