# Persistenz-Entscheidung (Entity Beans, Hibernate, JDBC)



## Gast2 (2. Dez 2005)

Hallo Zusammen!

Ich habe mittlerweile viel über Peristenz im J2EE-Bereich gelesen, bin aber letztenendes nur noch verwirrt. Deshalb wende ich mich an Euch in der Hoffnung, dass Ihr mir sagen könnt, welches Verfahren gar nicht oder besonders gut geeignet ist für meine Anforderungen.

Meine Anwendung (eine Kunden- und Auftragsverwaltung) soll folgende Anforderungen erfüllen:

1. Der Client ist eine Java-Applikation, der Server ist ein JBOSS 4.0.1

2. Die Datenbank steht und darf ich nicht verändern. Einige Daten, die für den Client logisch zusammen gehören und ihm als Ganzes präsentiert werden, sind dabei auf 2 Tabellen verteilt.

3. Der Nutzer kann Daten für die DB nicht nur erzeugen und ansehen, sondern ändert diese auch häufig.

4. Zu einem Auftrag gehören bestimmte Produkte, die zu verwalten sind. Nicht selten sind das 1000 und mehr, die dem Nutzer in einer Tabelle angezeigt werden. Innerhalb dieser 1000Zeilen-Tabelle kann er jede einzelne Zelle bearbeiten. Der geänderte Wert in der Zelle muss natürlich in der DB entsprechend geupdated werden.

5. Zu einem Auftrag werden den beteiligten Kunden bestimmte Rollen zugeordnet. Eine Kunde wiederum kann mehrere Aufträge geben. Es exisitert hier also eine n:m-Relation (In der DB: Auftrag, Kunde, AuftragKunde)

6. Es soll eine komplexe Such-Funktionalität realisiert werden, bei der die DB-Abfrage dynamisch zur Laufzeit zusammen gebaut wird.

7. Es ist eine MultiUser-Anwendung (etwa 10 User). Es dürfte zwar äußerst selten vorkommen, dass mehrere Nutzer an demselben Datensatz arbeiten, aber auszuschließen ist es auch nicht.

8. Der Datenbank-Typ (SAP DB) wird wohl in nächster Zukunft nicht gewechselt.


Mit diesen 8 Anforderungen im Hinterkopf habe ich mich die letzten 2 Wochen mit EntityBeans, Hibernate und JDBC mit SessionBeans befasst und bin bisher zu folgendem Schluß gekommen.

Hibernate ist zu langsam, vor allem in Hinblick darauf, dass ich oft Daten ändern möchte (vgl. Anforderung 3), evtl. einen Eintrag in einer 1000zeiligen Tabelle (vgl. 4). Außerdem wird der größte Vorteil von Hibernate - die DB-Typ-Unabhängigkeit - wohl gar nicht ausgeschöpft (vgl. 8.).

JDBC mit SessioBeans: Sehr schnell (bei einer recht simplen Abfrage war's etwa um den Faktor 10 schneller als Hibernate).
Dafür muss ich mich um Transaktionen, konkurrierenden Zugriff etc. selber kümmern (vgl. 7). Wollte das evtl. so umsetzen, dass eine SessionFacade auf ein DAO-Objekt zugreift, welches dann die DB-Abfrage durchführt und das Ergebnis an die SessionFacade zurückliefert, die wiederum dann den Client mit dem Ergebnis bedient.

Entity Beans: Da bin ich völlig verwirrt.
Hab über das CompositeEntity-Pattern für BMP gelesen. Das gilt wohl aber nur für EJB 1.1, weil Lazy loading und Store Optimazation wohl ab Version 2.0 anders bzw. vom Container gehandelt werden soll. Ab EJB 2.0 solle man also CMP-Beans mit Container Managed Relationships (CMR) verwenden, aber da sollen angeblich die abhängigen Objekte nicht vom Client veränderbar sein können.
Ein einfaches DB/Entity-mapping wird nicht gehen, da ich wie gesagt evtl. mehrere tausend Produkt-Einträge aus der DB zu verwalten habe (vgl. 4). Da würde sich ja evtl. so ein CompositeEntity-Pattern mit Lazyloading anbieten. Außerdem hab ich eine n:m-Relation in meinem DB-Schema (vgl. 5), die wohl auch nich so leicht auf EntityBeans gemappt werden kann.
Und dann sind auch noch zusammengehörige Daten teilweise auch noch über 2 Tabellen verstreut (vgl. 2).

Wäre schön, wenn jemand in Hinblick auf die oben genannten Anforderungen Licht in den Persistenz-Jungle für mich bringen könnte. Insbesondere ein wenig Aufklärung über den Gebrauch von EntityBeans, wenn man n:mRelationen sowie viele DB-Einträge gleichzeitig verwalten muss und zusammengehörige Daten über 2 Tabellen verstreut sind.
Sind meine bisherigen Schlüsse bezüglich Hibernate und JDBC korrekt?
Welches ist Eurer Meinung nach eine geeignete oder besonders ungeeignete Lösung? Evtl. eine Misch-Lösung?

Bin für jeden Tipp dankbar.
Gruß,
egon


----------



## RicoSoft (2. Dez 2005)

Kannst Du mal das Beispiel posten, wo Du mit JDBC 10x schneller bist als mit Hibernate? Irgendwie geht das bei mir nicht so auf. EntityBeans würde ich eher meiden, da sie meiner Meinung nach zwar mächtig, aber zu komplex sind. Hibernate ist meine Wahl Nr. 1, manchmal geht's aber nicht anders als über JDBC. Aber Faktor 10 an Geschwindigkeitserhöhung habe ich bisher noch nicht geschafft...


----------



## Gast2 (2. Dez 2005)

*Anforderung:* Finde alle Kunden, deren Filialen im Namen den String "Filiale" haben.
Zugriff erfolgt beides mal von einem einfachen Client aus auf dieselbe SessionBean im JBoss. Diese Bean hat zwei Methoden. Eine greift über Hibernate auf die DB zu (executeCriteria), die andere über JDBC (executeCriteriaJDBC).

Die Messung erfolgt im Client wie folgt:

```
long startTime = System.currentTimeMillis();
List customerList = bean.executeCriteria(dc);
System.out.println("Hibernate: "+((System.currentTimeMillis()-startTime)/1000.0f)+" sek");
		    
startTime = System.currentTimeMillis();
List customerListJDBC = bean.executeCriteriaJDBC(query);
System.out.println("JDBC: "+((System.currentTimeMillis()-startTime)/1000.0f)+" sek");
```



*HIBERNATE:*

_ABFRAGE:_

```
Filiale filiale = new Filiale();
filiale.setName("%Filiale%");

Example example = Example.create(filiale);	   
 
DetachedCriteria dc = DetachedCriteria.forClass(Kunde.class)
	.createCriteria("filialen").add(example.enableLike()).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
```

_ERGEBNIS:_
Kundenname: Kunde_A
Kundenname: Kunde_F
Kundenname: Kunde_D
Kundenname: Kunde_R
Kundenname: Kunde_S

_ZEIT:_
Hibernate: 1.343 sek


*JDBC:*

_ABFRAGE:_

```
Select distinct k.* from Kunde k, Filiale F where k.id=f.kunde_id and f.name like '%Filiale%'
//Nach Ausführung des Queries: Erzeuge eine Liste mit Kunden-Objekten. Setze dabei jedes Attribut im Kunden-Objekt
```

_ERGEBNIS:_
Kundenname: Kunde_R
Kundenname: Kunde_A
Kundenname: Kunde_S
Kundenname: Kunde_D
Kundenname: Kunde_F

_ZEIT:_
JDBC: 0.125 sek


So weit mein Ergebnis. Ich hoffe, ich hab bei meinem Test alles richtig gemacht. Wenn nicht, korrigiert mich bitte.
Jedenfalls hat mich das Ergebnis dann doch eher von Hibernate abgeschreckt, vor allem wenn man bedenkt, dass ich in meinem Programm eine umfangreiche, dynamische Suche realisieren will. Oder wenn ich manchmal eine Tabelle von mehreren tausend Einträgen habe und ich will eine einzige Zelle in dieser Tabelle bearbeiten.

Gruß,
Egon


----------



## RicoSoft (2. Dez 2005)

also mal ganz ehrlich: faktor 2 hätte ich vielleicht noch erwartet, solange nicht auf hibernate optimiert wurde (indizes nicht gesetzt und so weiter). aber faktor 10 ist mir neu, ich würde sagen, da macht er irgendetwas in dem beispiel falsch. hast du mal geschaut, was für ausgaben (v.a. die sql-anweisung) dir hibernate aus deinem beispiel generiert? du kannst ja showsql einschalten und mich würde da schon interessieren, was er daraus macht.


----------



## KSG9|sebastian (2. Dez 2005)

versuch mal nen Hibernate-Query..ob das schneller geht


----------



## Bleiglanz (2. Dez 2005)

na ja, so gut wie handoptimiertes SQL wird hibernate nie sein

in der Regel ist man bei flachen Queries und Batch-Updates mit native SQL immer besser dran

für die "1 Record per Primary-Key" holen, bearbeiten und dann updaten spielt das aber keine Rolle, da ist Hibernate schon sehr viel komfortabler als SQL

würde als Faustregel sagen:

- komplexe Queries und Batch-Updates mit SQL

- Hibernate für das Manipulieren einzelner Records

- und solche Abfragen, bei denen es nicht so sehr auf Speed ankommt

EJB kannst du abhaken: wenn du in der Materie noch nicht fit bist, dann dauert die Einarbeitung wahrscheinlich länger als das ganze Projekt. Ist auch nur dann sinnvoll, wenn viel mit Transaktionen gearbetet werden muss (und sie bieten auch für das Multiuser/Concurrentproblem gewisse Unterstützung..).


----------



## Gast2 (2. Dez 2005)

Hibernate-Ausgabe:


```
select
        this_.ID as ID1_1_,
        this_.NAME as NAME1_1_,
        this_.KUNDENO as KUNDENO1_1_,
        this_.KURZBESCHREIBUNG as KURZBESC1_1_,
        this_.BESCHREIBUNG as BESCHREI1_1_,
        this_.STEUERNO as STEUERNO1_1_,
        this_.UMSSTEUER as UMSSTEUER 1_1_,
        this_.KONTOINHABER as KONTOINH8_1_1_,
        this_.KONTONO as KONTONO1_1_,
        this_.BLZ as BLZ1_1_,
        this_.BANKNAME as BANKNAME1_1_,
        filiale1_.ID as ID0_0_,
        filiale1_.NAME as NAME0_0_,
        filiale1_.ILN as ILN0_0_,
        filiale1_.KURZBESCHREIBUNG as KURZBESC0_0_,
        filiale1_.BESCHREIBUNG as BESCHREI0_0_,
        filiale1_.TELEFON as TELEFON0_0_,
        filiale1_.KUNDE_ID as KUNDE8_0_0_
    from
        KUNDE this_,
        FILIALE filiale1_
    where
        this_.ID=filiale1_.KUNDE_ID
        and (
            filiale1_.NAME like ?
        )
```


Mmmh... sieht so aus, als ob Hibernate auch die Filialdaten aus der Filial-Tabelle zu den Kunden ausliest. Liegt's vielleicht daran? Hab im Kunden-Mapping-File jedoch angegeben, dass er die Filialen lazy loaden soll.
Welche Tricks gibts denn wohl, um Hibernate zu optimieren? Wie würd ich z.B. die obige Abfrage optimieren? Auf die DB hab ich wie gesagt keinen Einfluss, deshalb werd ich da auch keine Indizes einfach setzen dürfen. Andere Tipps?

Bin ansonsten noch Hibernate-Newbie. Hab eigentlich wie gesagt nur diesen Test gemacht und war mit dem Ergebnis unzufrieden. Wie mappe ich z.B. die n:m Relation? Ansonsten: Hat noch jemand gut Links oder Bücher zum Thema Hibernate?

Gruß,
Egon


----------



## kama (2. Dez 2005)

Hi,

createSQLQuery () schon probiert? oder das schon vorgeschlagene createQuery ().


BTW: Wie oft hast Du die abfragen laufen lassen? einmal ? zweimal?

Getrennt in unterschiedlichen Programmen oder in einem gemeinsamen?

Wie schon erwähnt würde ich die Indizes prüfen bzw. setzen. 
Warum sollst Du keine Indizes setzen dürfen? Komisch..

Auch schon den Ausführungsbaum der DB angschaut?

Ich bearbeite mit Hiberanate Tabellen mit Einträgen (von ca. 100.000 Datensätzen!) kein Problem. Indizes und Relationen (lazy/non lazy) können zu erheblichen Problemen führen.

MfG
Karl Heinz Marbaise


----------



## Guest (2. Dez 2005)

Hallo.



> createSQLQuery () schon probiert? oder das schon vorgeschlagene createQuery ().


Werd ich Montag mal testen. Jetzt schaff ichs nicht mehr. Ich geb dann bescheid.




> BTW: Wie oft hast Du die abfragen laufen lassen? einmal ? zweimal?


Hab die Abfragen mehrere male laufen lassen. Mit der Zeit wurde Hibernate auch immer effizienter, allerdings will ich mit Hibernate u.a. ja auch Such-Abfragen starten und die führt man ja üblicherweise nur einmal aus. Also ist eine gute Geschwindigkeit nach einigen Anläufen kein wirklicher Pluspunkt für Hibernate (zumindest in diesem Fall). 




> Getrennt in unterschiedlichen Programmen oder in einem gemeinsamen?


Beide Methoden wurden innerhalb von ein und derselben Main-Methode nacheinander (erst Hibernate, dann JDBC) ausgeführt. War nur ein Test-Client, der nichts anderes macht.



> Wie schon erwähnt würde ich die Indizes prüfen bzw. setzen.
> Warum sollst Du keine Indizes setzen dürfen? Komisch..


Die Sache ist, dass ich nicht der DB-Admin bin. Allerdings vertraue ich da sehr auf den Zuständigen, dass er Indizes etc. vernünftig setzt. 



> Auch schon den Ausführungsbaum der DB angschaut?


Was ist das? Wie geht das?



> Ich bearbeite mit Hiberanate Tabellen mit Einträgen (von ca. 100.000 Datensätzen!) kein Problem. Indizes und Relationen (lazy/non lazy) können zu erheblichen Problemen führen.


Könnntest du zu den erheblichen Problemen etwas mehr sagen. Wann treten die Probleme auf?
Gruß,
egon


----------



## Guest (3. Dez 2005)

Für alle Fälle, wo Daten tabellarisch angezeigt werden, kannst du CachedRowSet verwenden.
Entsprechende TableModel etc. implementieren. Dadurch entfällt die Konvertiererei der Daten
und ist entsprechend um einiges schneller.
Werden einzelne Datensätze editiert (die in Tabellen angezeigt werden), holst du anhand der 
ID den entsprechenden Datensatz vom Server und präsentierst ihn im Updateformular.
Updates über EJB mit Transaktionen sind nicht langsam, wenn es nicht gerade um Batchverarbeitung
geht.


----------



## kama (4. Dez 2005)

Hallo,



			
				anonymous hat gesagt.:
			
		

> > BTW: Wie oft hast Du die abfragen laufen lassen? einmal ? zweimal?
> 
> 
> Hab die Abfragen mehrere male laufen lassen. Mit der Zeit wurde Hibernate auch immer effizienter, allerdings will ich mit Hibernate u.a. ja auch Such-Abfragen starten und die führt man ja üblicherweise nur einmal aus. Also ist eine gute Geschwindigkeit nach einigen Anläufen kein wirklicher Pluspunkt für Hibernate (zumindest in diesem Fall).


Es ist doch wohl klar, dass einen Datenbank (Oracle, MySQL, PostgreSQL etc.) Abfragen cachen, falls immer die gleiche Abfrage kommt. Somit ist das Verhalten, dass zum Ende hin immer schneller wird.
Den Punkt, den Du hier festgestellt hast, hat nichts mit Hibernate zu tuen. Das ist ein Grundsätzliches Thema im Zusammenhang mit einer Datenbank.




			
				anonymous hat gesagt.:
			
		

> Beide Methoden wurden innerhalb von ein und derselben Main-Methode nacheinander (erst Hibernate, dann JDBC) ausgeführt. War nur ein Test-Client, der nichts anderes macht.


Das bedeutet, dass der JDBC Teil den bereits von Hibernate abgefragten Teile zu "Fressen" bekommen hat. Hier hat die Datenbank dann mit großer Sicherheit die Abgfrage (eventuell sogar den Result-Set) gecached. Somit ist klar, dass die Ausführung von JDBC schneller zu sein scheint.




			
				anonymous hat gesagt.:
			
		

> Die Sache ist, dass ich nicht der DB-Admin bin. Allerdings vertraue ich da sehr auf den Zuständigen, dass er Indizes etc. vernünftig setzt.


Kennen die Zuständigen auch deine SQL Abfragen? Wenn nicht, ist es unmöglich die richtigen Indizes zu setzen.




			
				anonymous hat gesagt.:
			
		

> Auch schon den Ausführungsbaum der DB angschaut?


Auf MySQL EXPLAIN, unter Orcale Explain Plan und auf PostgreSQL Explain.
Mit denen kannst Du prüfen, wie die Datenbank einzelne SQL Statements bearbeitet und welche Indizes tatsächlich verwendet werden oder ob die Datenbank einen Full-Scan einer oder mehrere Tabellen durchführt, der natürlich tierisch viel Zeit benötigt etc.
Mithilfe dieser Informationen kann man dann genau die Indizes setzen, die auch benötigt werden.



			
				anonymous hat gesagt.:
			
		

> Könnntest du zu den erheblichen Problemen etwas mehr sagen. Wann treten die Probleme auf?


Ein ganz wichtiger Teile ist der, wenn ich falsche Indizes gesetzt habe oder eventuelle gar keine. Das führt dann zu Ausführungszeiten von 20-30 Sekunden. Mit richtig gesetzten Indizes bekommt man die gleiche Abfrage dann auf 100-200 ms runter geschraubt (Je nach Rechner etc.)
Man sollte zum Testen immer eine Abfrage machen, die garantiert viele Datensätze liefert (ca. 20-30 Tausend) und danach die tatsächliche, die man testen möchte. Damit schaltet man bei den meisten Datenbanken die Caches aus.
Somit muss die Datenbank die Abfrage tatsächlich immer wieder bearbeiten.
Dann sollte man sich Ausführungspläne der Datenbanken anschauen und sehen was da passiert.
Mit Hibernate ist es mir bisher erst einmal passiert, dass ich die Abfrage in createSQLQuery() erstellen musste, da die automatisch generierte nicht so ganz Optimal war (Lag aber an den Relations, konnten leider nicht geändert werden!).

Weiterhin muss man genau aufpassen wie die Verknüpfungen der einzelnen Tabellen ist und wie dort "lazy" gehandhabt wird. Sonst kann es Dir passieren, dass Du eine Abfrage produziert, die dann mal 20 Datensätze als Ergebnis hat aber durch "lazy" und Relations dann hunderte oder gar Tausende Objekte mit lädt. Was Zeit kostet. 

MfG
Karl Heinz Marbaise


----------



## Guest (4. Dez 2005)

kama hat gesagt.:
			
		

> anonymous hat gesagt.:
> 
> 
> 
> ...


Nur zur Info. EXPLAIN gibt es auch in SQL Studio von SAPDB.


----------



## Guest (5. Dez 2005)

Hallo!

Also erstmal Dankeschön, vor allem kama, für die Hilfe und Hinweise.
Trotzdem hab ich jetz noch ein paar Fragen.



> Es ist doch wohl klar, dass einen Datenbank (Oracle, MySQL, PostgreSQL etc.) Abfragen cachen, falls immer die gleiche Abfrage kommt. Somit ist das Verhalten, dass zum Ende hin immer schneller wird.
> Den Punkt, den Du hier festgestellt hast, hat nichts mit Hibernate zu tuen. Das ist ein Grundsätzliches Thema im Zusammenhang mit einer Datenbank.
> ...
> Das bedeutet, dass der JDBC Teil den bereits von Hibernate abgefragten Teile zu "Fressen" bekommen hat. Hier hat die Datenbank dann mit großer Sicherheit die Abgfrage (eventuell sogar den Result-Set) gecached. Somit ist klar, dass die Ausführung von JDBC schneller zu sein scheint.


Hab die Abfragen jetzt mal vertauscht. Der Faktor 10 in der Abfrage bleibt aber bestehen.

Hab das auch mal mit dem explain getestet, sowohl für meinen sql-query als auch für den, den Hibernate ausgiebt. Hab auch mal den DB-Admin gefragt, welche Indizes gesetzt sind. Es sind neben ein paar speziellen wohl alle Referenzen indiziert. Also bei Tabelle Filiale ist die KundenID (also Referenz auf Tabelle Kunde) indiziert.
Die Geschwindigkeit bei der Ausführung ist bei beiden Queries (selfmade und Hibernate) die gleiche. Hibernate ist etwas langsamer, aber nur aus dem Grund, dass Hibernate auch die Filialdaten mit ausliest (siehe Query im post weiter oben). Kann ich das irgendwie ausstellen, dass Hibernate auch die Filialdaten aus der DB ausliest? Mich interessiert ja an dieser Stelle nur der Kundenname. Also bräuchte ich sogar eigentlich nur den und noch nicht einmal alle Kundendaten. Wie erreiche ich das in Hibernate?
Dadurch, dass Hibernate auch die Filial-Daten ausliest, könnte es auch sein, dass Hibernate daraus auch Filial-Objekte erzeugt (obwohl ich die Assoziation wie gesagt im Kunden-Mapping-File auf lazy gesetzt hab)? 



> Weiterhin muss man genau aufpassen wie die Verknüpfungen der einzelnen Tabellen ist und wie dort "lazy" gehandhabt wird. Sonst kann es Dir passieren, dass Du eine Abfrage produziert, die dann mal 20 Datensätze als Ergebnis hat aber durch "lazy" und Relations dann hunderte oder gar Tausende Objekte mit lädt. Was Zeit kostet.


Ich dachte, gerade durch das Setzen von lazy=true werden assoziierte Objekte zunächst nicht geladen, sondern nur, wenn man sie anfordert. Also ist "lazy" doch eigentlich immer ne gute Wahl, wenn ich nicht möchte, dass abhängige Objekte mitgeladen werden, oder? In meinem Fall also, wo mich nur der Kundenname interessiert, möchte ich die Filialen zu jedem Kunden gar nicht haben. Also ist in dem Kunden-Mapping-File auch lazy auf true. Ist das falsch? Muss ich sonst noch wo was einstellen?

Gruß,
egon


----------

