Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
Hi!
Wie handhabt ihr eigentlich die Hibernate Transaction? Angenommen ich habe eine Methode in welcher über eine Liste iteriert wird und für jeden Eintrag in der Liste muss man bspw. 1-4 Statements absetzen, macht ihr das alles innerhalb einer Transaction oder macht ihr jeweils eine neue auf? Weiters setzt ihr die Transactions in der "DAO Klasse" oder macht ihr das auch im Model oder in der View?
Hi!
Wie handhabt ihr eigentlich die Hibernate Transaction? Angenommen ich habe eine Methode in welcher über eine Liste iteriert wird und für jeden Eintrag in der Liste muss man bspw. 1-4 Statements absetzen, macht ihr das alles innerhalb einer Transaction oder macht ihr jeweils eine neue auf?
Das ist eine Frage der Anforderung. Hat mit Hibernate nichts zu tun. Wenn es keine Anforderung gibt, dann würde ich den Transaktionskontext so klein wie möglich machen, also pro Methode bzw. den Treiber auf autocommit schalten, weil eine Transaktion Speicher für das Rollback benötigt.
y0dA hat gesagt.:
Weiters setzt ihr die Transactions in der "DAO Klasse" oder macht ihr das auch im Model oder in der View?
Wenn, dann im Controller bzw. dort, wo die Businesslogik implementiert ist, zb EJBs.
Im DAO ist es eher schlecht weil dann zwei DAO-Methoden nicht innerhalb einer Transaktion laufen können. Ausser du rufst innerhalb einer DAO-Methode eine weitere DAO-Methode auf.
Dann hast du aber die Businesslogik im DAO => :autsch: autsch!
Ich bevorzuge Spring um den Transaktionskontext für Servicebeans zu definieren.
Wie wär's mit einer Art PersistenceContext. Es kapselt das ganze Session- und Transaktionshandling.
Jedes Dao erbt von einem abstrakten Dao und kriegt PersistenceContext als ersten Parameter an den
Konstruktor.
Ich würde das jetzt nicht als großen Fehler bezeichnen, so wie du es gepostet hast, da es in diesem Fall nur eine einzige Datenbankoperation ist, und zwar eine lesende. Eine Transaktion macht nur dann Sinn wenn mehrere schreibende Datenbankoperationen durchgeführt werden sollen und diese eine gewisse Abhängigkeit untereinander haben. In deinem Fall ist es nur unnötig.
Also brauche ich nun keine Transaktion wenn ich aus der DB etwas lese? Geht aber nur mit HQL oder? Und wenn ich Proceduren der DB aufrufe ist das dann lesend oder scheibend?
Also brauche ich nun keine Transaktion wenn ich aus der DB etwas lese? Geht aber nur mit HQL oder? Und wenn ich Proceduren der DB aufrufe ist das dann lesend oder scheibend?
1) Nein, sollte immer gehen.
2) Ein SELECT ist immer lesend, INSERT, UPDATE und DELETE schreibend. Selbsterklärend, oder? Für Stored Procedures gilt, soweit ich das verstanden habe mit Einschränkungen dasselbe. Siehe hier: http://www.hibernate.org/hib_docs/v3/reference/en/html_single/#sp_query
Ich würde hier "Business"-Transaktionen und das Hibernate-Transaction-Objekt unterscheiden... letzteres gehört in das DAO und kapselt schreibende Operationen in einer Klammer, die dann in DB-Transaktionen mündet... letzten Endes hab ich in meinen DAOs eigentlich immer nur eine Operation gleichzeitig verpackt und mache für mehrere Operationen hintereinander halt immer wieder ne Transaction auf.
Für tatsächliches Transaction-Handling empfiehlt sich imho Spring - da kannst du dann z.B. via Annotations oder nameConventions bestimmte Methoden unter Transaktionsverwaltung stellen - das ist imho sauberer noch als diese blöden Transaktionsklammern rumhängen zu haben - die ja nicht wirklich was mit der business-logik aber auch nich wirklich was mit der dao-logik zu tun haben.
wenn du nur ein Select machst, brauchst du keine Transaction, da kann ja auch kein rollback passieren ... ansonsten ist die Operation aber genauso wie das Erstellen der Session nicht teuer.
Ich geh mal davon aus, dass du jetzt ein schreibendes Statement meinst. Zum schreiben brauchst du immer eine Transaktion (Wir reden von richtigen Datenbanken, nicht MySQL/MyIsam). Entweder im einfachsten Fall so wie du es geschrieben hast, oder autocommit = true setzen (dann ersparst du dir jedesmal den Code für die Transaktionen) oder an die Session koppeln oder mit Spring, ... es gibt einige Wege.
Wir sollten ein konkretes Beispiel hernehmen, dann wirds vielleicht verständlicher.
@SnooP
Was genau verstehst du unter Business-Transaktionen?
Wenn ich innerhalb einer Transaktion in die Datenbank schreibe und danach in der Businesslogik eine Exception auftritt sollte auch in der Datenbank ein Rollback stattfinden. Daher halte ich eine Trennung in diesem Fall für nicht sinnvoll.
letzten Endes hab ich in meinen DAOs eigentlich immer nur eine Operation gleichzeitig verpackt und mache für mehrere Operationen hintereinander halt immer wieder ne Transaction auf.
Also bin ich doch nicht allein mit dieser Umsetzung
Denke ich habs nun geschnallt und werde mal die Transaktionen bei Lesezugriffe entfernen. Spring muss ich mich leider erst einlesen, wie ich das mit Hibernate koppeln kann.
**EDIT**
ms hat gesagt.:
y0dA hat gesagt.:
Danke für die Erklärungen (und was schreibend und was lesend ist hatte ich schon zuvor begriffen^^).
Ich geh mal davon aus, dass du jetzt ein schreibendes Statement meinst. Zum schreiben brauchst du immer eine Transaktion (Wir reden von richtigen Datenbanken, nicht MySQL/MyIsam). Entweder im einfachsten Fall so wie du es geschrieben hast, oder autocommit = true setzen (dann ersparst du dir jedesmal den Code für die Transaktionen) oder an die Session koppeln oder mit Spring, ... es gibt einige Wege.
Wir sollten ein konkretes Beispiel hernehmen, dann wirds vielleicht verständlicher.
@SnooP
Was genau verstehst du unter Business-Transaktionen?
Wenn ich innerhalb einer Transaktion in die Datenbank schreibe und danach in der Businesslogik eine Exception auftritt sollte auch in der Datenbank ein Rollback stattfinden. Daher halte ich eine Trennung in diesem Fall für nicht sinnvoll.
Nun bin ich wieder ein wenig verwirrt, wenn ich autocommit = true setze brauche ich kein Transactionshandling mehr - hat das auch Nachteile, dürfte wohl langsamer sein als wenn ich Transactions benutze?
Es wurde der Ruf nach einem Bsp laut, hier nun mein aktueller Problemcode, in welchem über eine Liste iteriert wird und pro Listeneintrag werden min. 2 DB Statements abgesetzt. Hierbei wird pro Stmt eine eigene Transaction erstellt - wie könnte ich jenes verbessern (die StringBuffer-Lösung zum zusammensetzen des Stmts ist auch eine Notlösung..):
Businesslogik:
Code:
public void evaluateTrackSegmentList(final List<TrackSegment> list) {
/* object for doing a statistic of the whole track */
FeatureStatisticController statistic = new FeatureStatisticController();
for (TrackSegment trackSegment : list) {
/* match start point and create feature segment */
List<FeatureSegment> startMatch =
this.createFeatureSegment(this.doMatchPoint(trackSegment.getJGeom().getJGeometry().getFirstPoint(),
trackSegment.getJGeom().getJGeometry().getSRID()));
/* match end point and create feature segment */
List<FeatureSegment> endMatch =
this.createFeatureSegment(this.doMatchPoint(trackSegment.getJGeom().getJGeometry().getLastPoint(),
trackSegment.getJGeom().getJGeometry().getSRID()));
}
}
private List<FeatureModel> doMatchPoint(final double[] point, final int srid) {
/* first match - start point */
return FeatureDB.getNearestNeighbour4Point(point, 10, GeomOperationDB.UNIT_METER,
FeatureController.FEATURE_TYPE_STREET, FeatureController.WITHIN_DISTANCE, srid);
}
private List<FeatureSegment> createFeatureSegment(final List<FeatureModel> list) {
List<FeatureSegment> l = new LinkedList<FeatureSegment>();
for (FeatureModel model : list) {
/* add new feature segment - constructor also calculate distance */
l.add(new FeatureSegment(model.getId(), model.getGeom(), NMEAController.UNIT_METER));
}
return l;
}
Persistenzklasse:
Code:
public static List<FeatureModel> getNearestNeighbour4Point(final double[] point, final int nnNumRes,
final String unit, final int featureType, final int withinDistance, final int srid) {
StringBuffer point2 = new StringBuffer();
point2.append(" SDO_GEOMETRY( ");
point2.append(FeatureDB.POINT_GTYPE);
point2.append(", ");
point2.append(srid);
point2.append(", ");
point2.append("SDO_POINT_TYPE( ");
point2.append(point[0]);
point2.append(", ");
point2.append(point[1]);
point2.append(", null), null, null)");
StringBuffer sb = new StringBuffer();
sb.append(" SELECT {feature.*} ");
sb.append(" FROM feature {feature} ");
sb.append(" WHERE SDO_NN ( ");
sb.append(" feature.f_geom, ");
sb.append(point2.toString());
sb.append(", '");
sb.append(" SDO_NUM_RES = ");
sb.append(nnNumRes);
sb.append(" ");
sb.append(unit);
sb.append("', 1) = 'TRUE' AND SDO_WITHIN_DISTANCE( feature.f_geom, ");
sb.append(point2.toString());
sb.append(", ' DISTANCE = ");
sb.append(withinDistance);
sb.append(" ");
sb.append(unit);
sb.append("') = 'TRUE' AND f_ft_id = ");
sb.append(featureType);
Session session = HibernateUtil.getCurrentSession();
Transaction tx = null;
List<FeatureModel> list = null;
try {
tx = session.beginTransaction();
SQLQuery q = session.createSQLQuery(sb.toString());
q.addEntity("feature", FeatureModel.class);
list = q.list();
/* commit and close session */
tx.commit();
} catch (Exception e) {
e.printStackTrace();
if (tx != null) {
tx.rollback();
}
} finally {
session = null;
tx = null;
}
return list;
}
ich meinte mit Business-Transaktionen halt Transaktionen die noch etwas schlauer sind als die Hibernate-Transaktionen wie etwa per JTA... mit Spring wird dann darüber natürlich auch ein DB-seitiger rollback durchgeführt - aber via Spring kann man halt die Transaktionen so wie man es gerne möchte, nämlich quasi deskriptiv an die zuständigen zu überwachenden Methoden dranpappen.
Spring und Hibernate verhalten sich ganz wunderbar miteinander ... das Stichwort heißt dort HibernateTemplate und bietet unheimlich viel Abstraktion... sprich jedes DAO bekommt ein Template injiziert auf das dann zugegriffen wird.. die DAOs wiederum können in den zuständigen Controller injiziert werden und feddich is ... alles hübsch konfigurativ via xml-datei ohne dabei aber großartig die Semantik beim Coden verlieren zu müssen, weil die entsprechenden getter/felder sind ja da - nur spar ich mir dann die null-pointer exceptions weil ich mal wieder vergessen hab irgendwo nen DAO durchzureichen oder ähnliches gemisste
letzten Endes hab ich in meinen DAOs eigentlich immer nur eine Operation gleichzeitig verpackt und mache für mehrere Operationen hintereinander halt immer wieder ne Transaction auf.
Da kannst du gleich autocommit auf true setzen, dann ersparst du dir die Schreiberei.
Jedoch hast du dann keine Transaktion im fachlichen Sinn.
Zu deinem Nachtrag:
Du iterierst über die Liste und setzt pro Listeneintrag 2 Statements (insert, update oder delete) ab.
Jetzt stellt sich die Frage was passieren soll, wenn was schief geht.
1) Wenn sichergestellt werden soll, dass pro Listeneintrag entweder beide Statements oder gar keines ausgeführt werden soll, dann muss eine Transaktionsklammer über die beiden Statements vorhanden sein.
Dh, geht das erste Statement OK, das zweite Statement aber schief, soll auch das erste nicht commited werden. Die beiden Statements für alle anderen Listeneinträge werden unabhängig davon bearbeitet.
2) Wenn sichergestellt werden soll, dass entweder alle Statements für alle Listeneinträge ausgeführt werden sollen oder gar keines, dann muss eine Transaktionsklammer über die Abarbeitung der ganzen Liste vorhanden sein.
Dh, gehen die beiden Statements des Listeneintrags 1..n OK, aber zB das zweite Statement des letzten Listeneintrags aber schief, sollen auch alle anderen Statements nicht commited werden.
Jede dbMethode wird unabhängig von der anderen ausgeführt. Es spricht nichts dagegen, solange fachlich nichts anderes gefordert wird.
Zur StringBuffer-Notlösung.
1) Zur besseren Lesbarkeit KEINEN StringBuffer verwenden. Der Performanceverlust zur Laufzeit steht in keinem Verhältnis zum Performanceverlust beim Lesen. :wink:
2) Das ist kein HQL was du verwendest. Schreib doch einfach
Zur StringBuffer-Notlösung.
1) Zur besseren Lesbarkeit KEINEN StringBuffer verwenden. Der Performanceverlust zur Laufzeit steht in keinem Verhältnis zum Performanceverlust beim Lesen. icon_wink.gif
Hi!
Also bei meiner Iteration wird nur gelesen! Im Genaueren wird mit Oracle Spatial Funktionen gearbeitet.
Ich dachte immer wenn ich spezielle Funktionen einer DB ausführe, müsse ich das mit SQL und nicht mit HQL machen - blödsinn?
Weiters ist es sinnvoll solche Funktionen über eine View aufzurufen, sprich wäre das schneller als das Statement in Java zu erstellen?
Öhm ... was genau passiert eigentlich in SDO_GEOMETRY( ... ), SDO_NN( ... ) und SDO_WITHHIN_DISTANCE ( ... ) ?
Hier wird der nearest neighbour (eigentlich die 10 nähesten) zu einer Geokoordinate innerhalb einer gewissen Distanz gesucht
**EDIT**
Code:
public static List<FeatureModel> getNearestNeighbour4Point(final double[] point, final int nnNumRes,
final String unit, final int featureType, final int withinDistance, final int srid) {
StringBuffer point2 = new StringBuffer();
point2.append(" SDO_GEOMETRY( ");
point2.append(FeatureDB.POINT_GTYPE);
point2.append(", ");
point2.append(srid);
point2.append(", ");
point2.append("SDO_POINT_TYPE( ");
point2.append(point[0]);
point2.append(", ");
point2.append(point[1]);
point2.append(", null), null, null)");
String s = "SELECT feature FROM Feature feature WHERE " +
"(SDO_NN (feature.f_geom, :point2, 'SDO_NUM_RES = :nnNumRes :unit') 1) = 'TRUE' AND " +
"(SDO_WITHHIN_DISTANCE(feature.f_geom, :point2, ' DISTANCE = :withinDistance :unit') = 'TRUE' AND f_ft_id = :featureType";
Session session = HibernateUtil.getCurrentSession();
List<FeatureModel> list = null;
try {
Query query = session.createQuery(s);
query.setString("point", point2.toString());
query.setInteger("nnNumRes", nnNumRes);
query.setString("unit", unit);
query.setInteger("withinDistance", withinDistance);
query.setInteger("featureType", featureType);
query.list();
} catch (Exception e) {
e.printStackTrace();
} finally {
session = null;
}
return list;
}
Wenn ich das so ausführen möchte, ohne Transaction bekomme ich folgende Exception:
Code:
org.hibernate.HibernateException: createQuery is not valid without active transaction
at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:297)
at $Proxy0.createQuery(Unknown Source)
at at.pcd.wam.technologie.persistence.db.FeatureDB.getNearestNeighbour4Point(FeatureDB.java:74)
at at.pcd.wam.technologie.controller.FeatureController.doMatchPoint(FeatureController.java:217)
at at.pcd.wam.technologie.controller.FeatureController.evaluateTrackSegmentList(FeatureController.java:135)
at at.pcd.wam.technologie.batch.RunBatch.main(RunBatch.java:123)
vermutlich brauchst du bei Queries also grundsätzlich eine Transaction, weil er ja nicht weiß, ob du nen update oder nur nen select machen willst und imho kann man tatsächlich nicht auf jede Funktion von Oracle via HQL zugreifen, das wird im jeweiligen Dialect geregelt.. so sachen wie to_date etc. gehen imho - aber was nearest-neighbour angeht wäre ich kritisch
sprich... doch SQL direkt verwenden... und auf Prepared Statements verzichten... weil - die werden eh überbewertet
Also brauche ich nun doch immer eine Transaktion, egal was ich mache?
Naja das hat nicht wirklich was mit speziellen DB Funktionen zu tun, ein einfaches "select * from" schmeißt denselben Fehler
Code:
public static void main(String[] args) {
String stmt1 = "FROM feature where f_id = 232861";
Session session = HibernateUtil.getCurrentSession();
try {
Query q = session.createQuery(stmt1);
List<FeatureModel> l = q.list();
/* commit and close session */
System.out.println("passt");
} catch (Exception e) {
e.printStackTrace();
} finally {
session = null;
}
}
Weiters stelle ich die ganzen Fragen ja weil ich, so wie es momentan ist, ein erhebliches Performance Problem habe. Deshalb wurde mir gesagt, ich soll die Transactionen rausnehmen wo nicht benötigt - geht nun ja anscheinend nicht...
Dadurch dass ich Spring verwende, sind natürlich immer Transaktionsklammern vorhanden um die man sich im Code nicht kümmern muss. Dagegen sind bei schreibenden Statements fehlende Transaktionsklammern bei Verwendung von reinem JDBC eine häufige Stolperfalle.
Zu deinem Performanceproblem: die Transaktionen sind höchstwahrscheinlich nicht schuld daran.
Wie wirkt sich das Performanceproblem aus?
Wieviel Datensätze werden denn abgearbeitet?
Zu deinem Performanceproblem: die Transaktionen sind höchstwahrscheinlich nicht schuld daran.
Wie wirkt sich das Performanceproblem aus?
Wieviel Datensätze werden denn abgearbeitet?
In einem anderen Thread wurde mir aber mitgeteilt dass eben diese Transactions schuld daran seien, denn ich iteriere über eine Liste in meiner Businesslogik und für jeden Eintrag in der Liste muss ich min. 2 Stmts (max 3) gegen die DB absetzen (eben diese nearest neighbour Geschichte von oben). Die Liste hat 1354 Einträge und für jeden Eintrag werden min. 2mal Transactionen begonnen und beendet (siehe Code von oben).
Ahja für das iterieren mit den stmts absetzen braucht mein Programm im Moment 9 Minuten!!!
Nun weiß ich echt nicht mehr wie ich hier etwas an der Performance ändern kann. Folgende Ideen:
+) Das Stmt statt mit StringBuffer oder Stringverkettung in dem entsprechendem hbm file als <sql-query> reinschreiben?
+)Das Stmt mehr oder weniger in eine View schreiben und dann die View aufrufen (kA ob das einen Unterschied macht)
+)Für diese 1354 Sätze nur eine Transaction benutzen - obwohl ich noch nicht so ganz weiß wie ich das umsetzen soll, dass ich keinen Businesscode in der DAO habe.
Ok, dann versuche als erstes einmal nur eine Transaktion zu verwenden.
Auch wenn im ersten Schritt die Businesslogik im DAO ist, zum analysieren des Problems ist das sicher legitim.
Refactoring kannst du nachher auch noch machen.
Wie schon in dem Beispiel von mir weiter oben, die For-Schleife über alle Listeneinträge IN dem try-catch-Block mit der Transaktion.
Hi!
Kam erst heute wieder dazu auf meiner Spielwiese weiter zu arbeiten, folgendes Problem besteht:
Code:
org.hibernate.HibernateException: createQuery is not valid without active transaction
at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:297)
at $Proxy0.createQuery(Unknown Source)
at at.pcd.wam.technologie.persistence.db.FeatureDB.getNearestNeighbour4Point(FeatureDB.java:74)
at at.pcd.wam.technologie.controller.FeatureController.doMatchPoint(FeatureController.java:217)
at at.pcd.wam.technologie.controller.FeatureController.evaluateTrackSegmentList(FeatureController.java:135)
at at.pcd.wam.technologie.batch.RunBatch.main(RunBatch.java:123)
Sprich ich habe nun nur eine transaction jedoch wird nach dem ersten Stmt die transaction geschlossen obwohl ich weder die session noch die transaction schliesse oder commite.