# ibatis +  "generated p-key" nach insert?



## tuxedo (24. Sep 2007)

Hallo,
hier müsste der eine oder andere sein der mit iBatis arbeitet....
Ich versuche mich gerade an dem Beispiel aus dem Tutorial das man der iBatis-Webseite findet.

Kann mir jemand sagen woran es liegen könnte dass ich bei einem 


```
System.out.println((Integer)sqlMap.insert("insertPerson", person_neu));
```

immer "null" auf der Console bekomme? Ich hab schon versucht das ganze zu debuggen. Bin hier stecken geblieben:

Klasse SqlMapExecutorDelegate.java

```
MappedStatement ms = getMappedStatement(id);
...
SelectKeyStatement selectKeyStatement = null;
      if (ms instanceof InsertStatement) {
        selectKeyStatement = ((InsertStatement) ms).getSelectKeyStatement();
      }
```

Mit getSekectKeyStatement() bekomme ich erst dann etwas anderes als "null", wenn vorher ein setSelectKeyStatement(...) ausgeführt wurde. Aber im gesamten Prozess der "insertPerson" Aktion kommt das offensichtlich nicht vor.

In der API-Doc steht nur:



> insert
> 
> public java.lang.Object insert(java.lang.String id,
> java.lang.Object parameterObject)
> ...



Ist die Funktion noch nicht weiter implementiert oder woran liegt das?

- Alex


----------



## maki (24. Sep 2007)

Kannst du noch das MappedStatement posten?


----------



## tuxedo (24. Sep 2007)

Du meinst das in der XML-Config?


```
<insert id="insertPerson" parameterClass="de.root1.cm.objects.Person">
INSERT INTO person (
	id, 
	firstName, 
	lastName, 
	birthDate)
VALUES (
	#id#, 
	#firstName#, 
	#lastName#, 
	#birthDate#)
</insert>
```

Ich glaub ich hab gerade was entdeckt:

ich füge auch ID mit ein... Wobei die ID-Spalte in der Db auf auto-increment steht. Muss ich nochmal schauen, oder kanns daran nicht liegen?

- Alex


[update]

okay, XML angepasst:


```
<insert id="insertPerson" parameterClass="de.root1.cm.objects.Person">
INSERT INTO person (
	firstName, 
	lastName, 
	birthDate)
VALUES (
	#firstName#, 
	#lastName#, 
	#birthDate#)
</insert>
```

Meine Testklasse;:


```
SqlMapClient sqlMap = MyAppSqlMapConfig.getSqlMapInstance(); // as coded above
				
		Person person_neu = new Person();
		person_neu.setFirstName("Max");
		person_neu.setLastName("Mustermann");
		
		System.out.println((Integer)sqlMap.insert("insertPerson", person_neu));
```

Eine Person anhand der ID abfragen geht übrigens prima...Aber beim einfügen krieg ich auch mit jetzt angepasster XML keinen p-key...


----------



## maki (24. Sep 2007)

> ch füge auch ID mit ein... Wobei die ID-Spalte in der Db auf auto-increment steht. Muss ich nochmal schauen, oder kanns daran nicht liegen?


Ja, sehr wahscheinlich.


Die Doku zu iBatis (seite 20, auto generated keys) enthält die entsprechenden Hinweise 


```
<!— Microsoft SQL Server IDENTITY Column Example -->
<insert id="insertProduct-MS-SQL" parameterClass="com.domain.Product">
    insert into PRODUCT (PRD_DESCRIPTION)
    values (#description#)

    <selectKey resultClass="int" >
        SELECT @@IDENTITY AS ID
    </selectKey>

</insert>
```
Das wichtige dabei ist die selectKey Anweisung, da die ID erst nach dem einfügen verfügbar ist, bei Oracle würde dass natürlich anders laufen.


----------



## tuxedo (24. Sep 2007)

Coole sache, es funzt, Danke für den Tipp...
Dachte Anfangs iBatis ist selbst so schlau und holt sich die ID .. Aber damit wäre man dann ja nicht ganz so flexibel ;-)


```
<insert id="insertPerson" parameterClass="de.root1.cm.objects.Person">
INSERT INTO person (
	firstName, 
	lastName, 
	birthDate)
VALUES (
	#firstName#, 
	#lastName#, 
	#birthDate#)
	<selectKey resultClass="int" >
		SELECT id FROM person WHERE firstName = #firstName# AND lastName = #lastName#
	</selectKey> 
</insert>
```

Jetzt muss ich nur noch sicherstellen dass es keine zwei identischen Personen gibt ;-)

- Alex


----------



## maki (24. Sep 2007)

> Dachte Anfangs iBatis ist selbst so schlau und holt sich die ID .. Aber damit wäre man dann ja nicht ganz so flexibel


Richtig.

iBatis ist nur eine ganz dünne Schicht, iBatis weiss gar nix über den SQL Dialekt, stellt so gut wie keine Anforderungen aber bietet dafür viele Möglichkeiten.

Das ist auch ganz gut so, sonst könnte ich es bestimmt nicht mit einem Uralt JDBC Treiber (1.*) verwenden


----------



## maki (24. Sep 2007)

Moment!

Mir ist da gerade etwas aufgefallen:


```
<selectKey resultClass="int" >
      SELECT id FROM person WHERE firstName = #firstName# AND lastName = #lastName#
   </selectKey>
```
Das ist sehr unsicher 

Welche DB verwendest du denn?

Die meisten haben eine spezielle Syntax, um auf die ID des gerade angelegten Datensatzes zuzugreifen.


----------



## tuxedo (24. Sep 2007)

Verwende zur Zeit MySQL... Und ja, ich weiß dass das unsicher ist.
Aber wenn mehrere auf die DB zugreifen oder Einträge machen ist auch das abfragen der letzten Inser-ID ebenfalls unsicher (könnte ja schon wieder neue geben). Ist also doch irgendwie geschickter  ich frage den Datensatz ab, den ich eben eingetragen habe (mit möglichst der gleichen Anzahl Spalten in der Where-Bedingung... 

- Alex


----------



## maki (24. Sep 2007)

> Aber wenn mehrere auf die DB zugreifen oder Einträge machen ist auch das abfragen der letzten Inser-ID ebenfalls unsicher (könnte ja schon wieder neue geben). Ist also doch irgendwie geschickter ich frage den Datensatz ab, den ich eben eingetragen habe (mit möglichst der gleichen Anzahl Spalten in der Where-Bedingung...


Nicht wirklich, da es sich um ein anderes Statement handeln würde.

http://lists.mysql.com/internals/5
http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_last-insert-id
Was du brauchst ist folgendes:

SELECT LAST_INSERT_ID();

Damit hast du dann keine Probleme mehr


----------



## tuxedo (24. Sep 2007)

danke...ich schau's mir mal an....


----------



## tuxedo (24. Sep 2007)

Naja, ich kriegs nicht gebacken... Verwende meinen jPMdbc JDBC-Treiber. Der arbeitet ohne durchgehend offene Verbindung zur DB (genau wie phpMyAdmin auch).

Der Witz an der Sache:

1) Führe ich "SELECT LAST_INSERT_ID()" über meinen Treiber aus krieg ich immer eine 0 zurück (Connector/J kann ich getrade nicht testen).
2) Gehe ich über phpMyAdmin auf die DB und hab keine Tabelle gewählt, einfach nur die DB, klicke auf SQL und führe "SELECT LAST_INSERT_ID()" aus krieg ich ebenfalls 0 zurück.
3) Klicke ich in phpMyAdmin die entsprechende Tabelle an und gehe dann erst auf SQL und führe "SELECT LAST_INSERT_ID()" aus, dann krieg ich den richtigen Wert (aktuell 24). 

Any ideas why? Laut MySQL-Doku:



> Die erzeugte Kennung wird auf dem Server verbindungsspezifisch gehandhabt: Der von der Funktion an einen bestimmten Client zurückgegebene Wert ist der erste AUTO_INCREMENT-Wert, der für die zuletzt abgesetzte Anweisung, die eine AUTO_INCREMENT-Spalte betraf, von diesem Client erzeugt wurde. Dieser Wert darf nicht von anderen Clients bearbeitet werden, auch wenn diese selbst AUTO_INCREMENT-Werte erzeugen. Dieses Verhalten gewährleistet, dass jeder Client seine eigene Kennung abrufen kann, ohne die Aktivitäten anderer Clients berücksichtigen oder Sperren setzen bzw. Transaktionen verwenden zu müssen.



D.h. eigtl dürfte es auch mit phpMyAdmin nicht funktionieren da mit PHP die Verbindung zur DB ja nicht dauerhaft offen gehalten wird (zumindest ist da nix konfiguriert).

Irgendwie ist das für mich ein Rätsel warum ich in phpMyAdmin zuvor die Tabelle anklicken muss damit ich einen richtigen Wert bekomme, und das ganze mit dem jdbc bei mir gar nicht tut ...

- Alex

[update]

Mich macht das ganz kirre.... Hab eben nochmals gelesen dass der Kontext des LAST_INSERT_ID verloren geht, wenn man die Verbindung trennt.

Hab eben mit 2 verschiedenen Browsern, einen via HTTP und den anderen via HTTPS, phpMyAdmin aufgerufen. Mittels JDBC hab ich einen neuen Eintrag erzeugt. In beiden Browsern hat es "LAST-INSERT-ID" inkrementiert, jedoch nur wenn ich vorher die Datenbank UND die Tabelle angewählt habe....

Versteht das einer? Wie macht phpMyAdmin das?


----------



## tuxedo (24. Sep 2007)

Da die Sache vielleicht auch irgendwie mit PHP zusammenhängt, hab ich auch mal in einem PHP-Forum gefragt:

http://phpforum.de/forum/showthread.php?t=209447

Bis jetzt gibts dort noch keine Antwort.


----------



## maki (24. Sep 2007)

Zu MySQL und PHPAdmin kann ich nicht viel sagen.




> Mich macht das ganz kirre.... Hab eben nochmals gelesen dass der Kontext des LAST_INSERT_ID verloren geht, wenn man die Verbindung trennt.


Ich weiss nicht ob ich mir darüber Sorgen machen würde, zumindest nicht wenn der Insert und Select(key) im selben prepared statement ausgeführt werden:

```
INSERT INTO ... ;
SELECT  LAST_INSERT_ID();
```
Sieh dir doch mal die iBatis log ausgaben an, dazu musst du in der Loggingkonfiguration (log4j) folgendes eintragen:

```
log4j.logger.com.ibatis=DEBUG
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG
  log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
```

Wenn es wirklich NICHT* in einem "Rutsch" rübergeht, ist das dein Problem, ansonsten ist es etwas anderes.

*EDIT


----------



## tuxedo (25. Sep 2007)

Ich glaub mit JDBC ist es nicht wirklich möglich, und auch nicht sinnvoll zwei Statements auf einmal auszuführen. Weil man bekommt ja auch nicht zwei Resultsets auf einmal zurück (AFAIK). 

In der Tat führt iBatis die Statements getrennt aus. 
Hab aber schon eine Lösung gefunden: PHP kann die LAST-INSERT-ID ja selbst abholen und temporär zusammen mit der session-id der PHP-Sitzung speichern. Muss dann nur einen eigenen SQL-Befehl formen den PHP erkennt und daraufhin die zwischengespeicherte ID zurückgibt. Ist zwar nicht ganz so flexibel (ein "SELECT LAST_INSER_ID() AS MeineLetzteInsertId" welches eine Spalte mit dem Namen "MeineLetzteInsertId" zurückgibt wäre also nicht möglich), aber besser als gar nicht auf die letzte ID zugreifen zu können.

Danke mal soweit für die Hilfe... 

BTW: Gibts Grenzen von iBatis die man vorher wissen/kennen sollte? Ich meine zu glauben Hibernate wäre mächtiger als iBatis. Dafür ist iBatis halt schon einfach und unkompliziert.

- Alex


----------



## maki (25. Sep 2007)

> Ich glaub mit JDBC ist es nicht wirklich möglich, und auch nicht sinnvoll zwei Statements auf einmal auszuführen. Weil man bekommt ja auch nicht zwei Resultsets auf einmal zurück (AFAIK).


Doch, man kann mit JDBC mehrere Resultsets zurückbekommen, wenn es die DB/der JDBC Treiber unterstützt: http://java.sun.com/j2se/1.3/docs/api/java/sql/DatabaseMetaData.html#supportsMultipleResultSets()

Es gibt Situationen in denen das Sinn ergibt.

Das man mehrere Statement auf einmal schicken kann, ist übrigens sehr üblich, allein schon aus Performance Gründen.



> In der Tat führt iBatis die Statements getrennt aus.


Das wundert mich ehrlich gesagt sehr, bin davon ausgegangen das ein Mapped Statement zu einem statement führt, wie es auch sein sollte.



> BTW: Gibts Grenzen von iBatis die man vorher wissen/kennen sollte? Ich meine zu glauben Hibernate wäre mächtiger als iBatis. Dafür ist iBatis halt schon einfach und unkompliziert.


Hibernate ist ein ORM Framework, um einiges komplexer und abstrakter als iBatis, dafür braucht man sich um tabellenstruktur etc. keine Sorgen mehr zu machen.
iBatis sorgt dafür, dass SQL nicht mehr in Java Quellcode vorkommt und erspart einem (Gott sei Dank!) jeglichen JDBC Code.
Man sollte halt bedenken, das die mapped statements in XML dateien liegen, also Dinge wie < und > sollten maskiert sein


----------



## tuxedo (25. Sep 2007)

maki hat gesagt.:
			
		

> > Ich glaub mit JDBC ist es nicht wirklich möglich, und auch nicht sinnvoll zwei Statements auf einmal auszuführen. Weil man bekommt ja auch nicht zwei Resultsets auf einmal zurück (AFAIK).
> 
> 
> Doch, man kann mit JDBC mehrere Resultsets zurückbekommen, wenn es die DB/der JDBC Treiber unterstützt: http://java.sun.com/j2se/1.3/docs/api/java/sql/DatabaseMetaData.html#supportsMultipleResultSets()
> ...



Wieder was dazugelernt... Aber die Performance-Gründe versteh ich nicht. Die DB bekommt die Statements ja nicht parallel (bzw. evaluiert sich nicht zeitgleich), sondern nacheinander. Und kann sie demnach auch nur nacheinander auf die DB los lassen. 

Zur Sache mit iBatis selbst: Hab mich mit Hibernate noch nicht intensiv beschäftigt. Zum einen weil ich's nicht binnen 15min zum l aufeb bekommen hab und zum anderen weil's mir irgendwie zu abstrakt ist.

iBatis ist da ziemlich schick und einfach. Wenn dir also keinen wirklich Nennenswerten Grenzen einfallen, dann nehm ich das mal so hin. Prinzipiell sollte es, soweit ich das gesehen hab, keine großen Einschränkungen geben. 

- Alex


----------



## maki (25. Sep 2007)

> Die DB bekommt die Statements ja nicht parallel (bzw. evaluiert sich nicht zeitgleich), sondern nacheinander. Und kann sie demnach auch nur nacheinander auf die DB los lassen.


Da geht darum, keine unnötigen (und leider sehr langsamen) Netzwerkverbindungen zu öffnen, wenn man alles in einer Verbindung rüberschiebt könnte.
Die Latenzzeiten des Netzwerkes liegen im milli Sekundenbereich, das hört zwar nicht nach viel an, im Vergleich zu zugriffen auf den Speicher etc. (nano/micro Seunden), sind das aber Größen unterschiede um den Faktor 1000-1000000.

Hibernate zu lernen lohnt sich auf jedenfall, allerdings dauert es bis sich das Wissen auch umsetzen lässt.
iBatis benutze ich mittlerweile nur noch, wenn ich auf eine bestehende DB zugreife oder einfach SQL brauche.
JDBC ist zwar nicht schwer, allerdings bläht es den Quellcode ganz schön auf, und eine DAO Schicht gehörtmittlerwele zum Standard, wieso also selber schreiben?


----------



## tuxedo (25. Sep 2007)

Okay, das mit der Dauer des immer wieder öffnens der Netzwerkverbindung stimmt ... hatte ich nicht berücksichtigt. 

Was macht denn Hibernate so viel anders als iBatis? Beides läuft ja unter denm Überbegriff Persistenz-Framework das das Programm prinzipiell von der DB unabhängig macht...
Die kurze Zusammenfassung von Hibernate bei Wikipedia liefert mir zumindest keinen Anhalt was Hibernate anders macht oder warum es besser ist...

-> http://de.wikipedia.org/wiki/Hibernate_(Framework)

Gruß Alex

[update]

okay, hab ein wenig gegoogelt (hätt ich auch gleich tun können ich dummerchen...):

http://www.javalobby.org/java/forums/t16496.html

Hab noch viel mehr Info gefunde. Quintessenz für mich: iBatis lässt sich prima einsetzen und man kommt schnell zum Ziel. Hibernate kann (irgend) etwas mehr und wird oftmals in sehr großen Projekten eingesetzt. Für mich reicht für's erste mal iBatis. Wenn ich da an meine Grenzen stoße seh ich mir Hibernate nochmal genauer an...


----------



## maki (25. Sep 2007)

Als Buch für den Einstig in Hibernate 3 gibt es eigentlich nur eine Wahl: http://www.amazon.de/Java-Persisten...ie=UTF8&s=books-intl-de&qid=1190711071&sr=1-1

Man kann natürlich auch das ältere Buch nehmen, für Hibernate 2: http://www.amazon.de/Hibernate-Action-Practical-Relational-Mapping/dp/193239415X
Ist natürlich nicht mehr ganz aktuell 

In dem Buch geht man erstmal auf ORM ein, und das ist der Hauptunterschied zwischen iBatis und Hibernate.

Die Online doku (http://www.hibernate.org/hib_docs/v3/reference/en/html/) spart sich einige Erklärungen die vor allem für Ein- und Umsteiger sehr wertvoll sind.


----------



## Gast (27. Okt 2007)

Hello! Good Site! Thanks you! hfmattvmcun


----------



## tuxedo (27. Okt 2007)

Gast hat gesagt.:
			
		

> Hello! Good Site! Thanks you! hfmattvmcun



Was will uns dieser Post sagen?


----------



## merlin2 (27. Okt 2007)

Dass der Spamschutz des Forums besser sein könnte?


----------



## wtezsjmnx mruvo (9. Nov 2007)

msgl fpycnexkz bmalv dqeit mpckfrjie wdnjghu nqvjtswdo


----------



## wtezsjmnx mruvo (9. Nov 2007)

msgl fpycnexkz bmalv dqeit mpckfrjie wdnjghu nqvjtswdo


----------



## tuxedo (9. Nov 2007)

Aber echt. Is nur seltsam, dass scheinbar nur in diesem Thread gespammed wird ?!

Ne Idee wäre auch, dass die Gäste, die hier posten wollen, ne kleine Rechnung machen müssen um den Post abzusenden. Oder die Sache mit dem Captchas ...

- Alex


----------

