[SQL] Wie ID (Auto Inc) des zuletzt hinzugefügten Datensatzes erfahren?

Kamikatze

Captain
Registriert
Okt. 2004
Beiträge
3.708
Hey!

Folgendes Problem:

Ich erzeuge Objekte (Java), die ich dann in der Datenbank speichere.
Beim Speichern erzeugt MySQL 5 dann automatisch eine neue ID (Primärschlüssel).

Wenn ich dann aber mit dem Objekt weiterarbeiten will, muss die ID, anhand der das Objekt eindeutig identifiziert wird (anders nicht möglich!), aber im Objekt gespeichert sein, ich diese also in das Objekt übernehmen.

Bisher hab ich die gespeicherten Datensätze gleich nach dem Speichern einfach wieder aus der DB ausgelesen und die erzeugte ID ins Objekt übernommen (war bei den bisherigen Objekten möglich, da auch anderwertig identifizierbar). Jetzt hab ich aber das Problem, dass bei anderen Objekten der Datensatz nicht mehr eindeutig identifizierbar ist und ich jetzt irgendwie anstehe...

Bitte um euren Rat!

Danke!

EDIT:
Gerade noch einen Tipp bekommen, der mich evtl. weiterbringen könnte:

In der Spalte "AUTO_INCREMENT" der Tabelle "TABLES" im Schema "INFORMATION_SCHEMA" ist der Wert gespeichert, den der nächste Datensatz erhalten wird.
Wenn man gleich nach dem Speichern diesen Wert ausliest und 1 subtrahiert (dann muss Schrittweise aber unbedingt 1 sein), könnte man diese in Erfahrung bringen, ganz sauber kommt mir das aber immer noch nicht vor.

Meinungen/Tipps?
 
Zuletzt bearbeitet:
So, jetzt muss ich aber noch dazusagen, dass das ganze eine Webapplikation ist und daher nur eine einzige Verbindung zur Datenbank für alle User besteht - und zwar die des Webservers zur DB.

Kann es bei Last_insert_id() nicht sein, dass zwischenzeitlich ein anderer Benutzer einen anderen Datensatz in eine andere Tabelle eingefügt hat? Auch wenn die Zeitspanne zwischen Speichern und ID-Abfrage sehr gering ist...

Kann man das zumindest noch auf die aktuelle Tabelle beschränken?

So in etwa wie:
Select Last_insert_id() from usertabelle;

EDIT:

Es funktioniert nicht und ich versteh nicht warum.

Code:
Statement statement = connection.createStatement();
statement.executeUpdate(sqlstring);

ResultSet rs = statement.executeQuery("SELECT LAST_INSERT_ID();");
rs.next();
a.setID(rs.getInt(1));

Nach Ausführung von rs.next() wird folgende Exception geworfen:

java.sql.SQLException: Operation not allowed after ResultSet closed
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1056)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:957)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:927)
at com.mysql.jdbc.ResultSetImpl.checkClosed(ResultSetImpl.java:770)
at com.mysql.jdbc.ResultSetImpl.next(ResultSetImpl.java:7001)

EDIT:
Einfach unerklärlich... ersetze ich im obigen Code den SQL-Befehl, funktioniert es. Also funktioniert anscheinend LAST_INSERT_ID() nicht. :(
Code:
ResultSet rs = statement.executeQuery("SELECT COUNT(*) FROM TABELLE;");

Hab es im MySQL Query Browser versucht - zuerst einen Datensatz eingefügt und dann "SELECT LAST_INSERT_ID();" ausgeführt: Rückgabe 0 (statt 4).
 
Zuletzt bearbeitet:
Es gibt auch noch
http://dev.mysql.com/doc/refman/5.1/de/mysql-insert-id.html
aber das ist ja nicht das Problem.
Ansonsten wäre das noch die Doku zu dem last_insert_id :http://dev.mysql.com/doc/refman/5.1/de/information-functions.html#id3259953

Aber mal was generelles auf Grund meiner Erfahrungen mit Datenbankverbindungen.

Nur eine (statische) Verbindung zur Datenbank zu haben bzw. kein Transaktionsmanagement wird dich, je nach Anwendung, den Kopf kosten.

Beispiel:

Person A lädt Datensatz 5
Person B lädt Datensatz 5
Person A verändert Datensatz 5
Person A schreibt Datensatz 5 zurück in die Datenbank

Person B schreibt Datensatz 5 zurück in die Datenbank

Ergebnis? Genau, alle Änderungen von Person A sind weg.

Ähnliches trifft auf deine Frage zum autoincrement Fall zu.
Entweder du programmiert nun einen Connection Pool, einen Cache, ein Transaktionsmanagement und dazu das ganze noch objektorientiert, oder du verwendest einfach das, was schon da ist. z.B. Hibernate.

Um aber nochmal auf eine "Edit's" zurück zu kommen.

Ich glaube das dein ResultSet "leer" ist. Und wenn du next() machst, gibt es kein nächstes weil es noch nie was gegeben hat.

Leider sind meine Erfahrungen damit milde gesagt, beschränkt. Ich hab vor 4 Jahren zuletzt Datenbank abfragen auf diese Art und Weise gemacht. Seit dem nur noch mit OJB, iBATIS und Hibernate.

Ich glaube sogar für dich wäre für den Anfang iBATIS einfach als Hibernate, denn iBATIS ist Datenbank näher.

Und zu deinem edit 2:
Ich hab das gerade gestest auf "Server version: 5.0.32-Debian_7etch3-log".
Funktioniert prima.

Ich müsste mal deine Tabellen Struktur sehen, damit ich dir weiterhelfen kann.

Gruß
Nox
 
Tja, das witzige ist, ich habe beim letzten Projekt bereits mit Hibernate gearbeitet. ;)
Dachte mir aber, das hier ist eh recht klein... zahlt sich nicht aus und außerdem hatten wir auch mit Hibernate so unsere Probleme... machst es halt ohne. Inzwischen bereue ich die Entscheidung schon... -.-

An das Problem mit der Mehrfachbenutzung habe ich auch bereits gedacht...

Ich habe es soeben mit einer komplett neuen Tabelle versucht, nur 1 Auto-Inc-Wert + 1 Varchar2:

Code:
insert into tab(val) values('tst');

Ausführen.

Code:
SELECT LAST_INSERT_ID();

Ausführen:
Ergebnis ist wieder 0. :rolleyes:


Bei Serverinformation steht bei mir folgendes:
MySQL 5.0.45-community-nt via TCP/IP
 

Anhänge

  • 2008-01-02_171830.png
    2008-01-02_171830.png
    34,3 KB · Aufrufe: 211
  • 2008-01-02_171850.png
    2008-01-02_171850.png
    34,6 KB · Aufrufe: 216
Also iBATIS wäre durch aus einen Blick wert. Nicht so umfangreich und kompliziert wie Hibernate aber strukturiert und gekapselt. Man packt seinen Query in eine XML File und ruft über Java den Alias auf. Kann dann auch auf HashMap, List oder Objekte mappen. Alles ohne Cache oder Transaktionen. Somit sehr Datenbank nah.

last_insert_id() bezieht sich immer auf die aktuelle Verbindung. Ich verwenden MySQL Browser nicht. (Arbeite unter Linux/Unix)
Kann sein das dein MySQL Browser immer eine neue Verbindung aufbaut?!

Was kommt den bei
Code:
DESC <Tabellenname>
raus?

Oder kann ich mal dein Create Statement sehen?
 
5 Tabellen:
  1. Applicants
  2. Files (Applicants 1:n Files)
  3. Listboxentry (Listboxes 1:n Listboxes)
  4. Listboxes
  5. User (für Anmeldung am System)

Ich bin jetzt nur bereits schon fast am Ende des Projekts, das alles jetzt noch umzustellen wäre viel Aufwand, ich würde das gerne so lösen.

EDIT:
Hoppla, die Bedeutung der Spalten:
Spaltenname, Datentyp, Not Null, Auto Inc, Schalter, Vorgabewert, Kommentar
 

Anhänge

  • 1_applicants.png
    1_applicants.png
    54,2 KB · Aufrufe: 222
  • 2_files.png
    2_files.png
    11,6 KB · Aufrufe: 218
  • 3_listboxentry.png
    3_listboxentry.png
    12,5 KB · Aufrufe: 201
  • 4_listboxes.png
    4_listboxes.png
    7 KB · Aufrufe: 198
  • 5_user.png
    5_user.png
    20,4 KB · Aufrufe: 207
Zuletzt bearbeitet:
Hab die Spaltenbedeutungen hinzugefügt, aber hier noch mal in der Console (in der war ich bisher noch gar nie...).

Code:
mysql> desc appdb.applicants;
+--------------------+------------------+------+-----+---------+----------------+
| Field              | Type             | Null | Key | Default | Extra          |
+--------------------+------------------+------+-----+---------+----------------+
| applid             | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| lastname           | varchar(45)      | NO   |     |         |                |
| firstname          | varchar(45)      | NO   |     |         |                |
| title              | varchar(20)      | YES  |     | NULL    |                |
| datebirth          | datetime         | YES  |     | NULL    |                |
| pdvid              | int(10) unsigned | YES  |     | NULL    |                |
| street             | varchar(45)      | YES  |     | NULL    |                |
| zipcode            | varchar(10)      | NO   |     |         |                |
| city               | varchar(45)      | NO   |     |         |                |
| email              | varchar(45)      | YES  |     | NULL    |                |
| phone              | varchar(30)      | YES  |     | NULL    |                |
| notice             | varchar(255)     | YES  |     | NULL    |                |
| source             | varchar(45)      | NO   |     |         |                |
| applicability_id   | int(10) unsigned | YES  |     | NULL    |                |
| processor_id       | int(10) unsigned | NO   |     |         |                |
| datearrived        | datetime         | YES  |     | NULL    |                |
| dateintrod         | datetime         | YES  |     | NULL    |                |
| introduction_at_id | int(10) unsigned | YES  |     | NULL    |                |
| datelastmod        | datetime         | NO   |     |         |                |
| notice2            | text             | YES  |     | NULL    |                |
| photo              | mediumblob       | YES  |     | NULL    |                |
| status_id          | int(10) unsigned | YES  |     | NULL    |                |
| convlog            | text             | YES  |     | NULL    |                |
+--------------------+------------------+------+-----+---------+----------------+
23 rows in set (0.00 sec)


mysql> desc appdb.files;
+----------+------------------+------+-----+---------+----------------+
| Field    | Type             | Null | Key | Default | Extra          |
+----------+------------------+------+-----+---------+----------------+
| fileid   | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| applid   | int(10) unsigned | NO   | MUL |         |                |
| name     | varchar(45)      | NO   |     |         |                |
| filedata | mediumblob       | NO   |     |         |                |
+----------+------------------+------+-----+---------+----------------+
4 rows in set (0.10 sec)


mysql> desc appdb.listboxentry;
+----------+------------------+------+-----+---------+----------------+
| Field    | Type             | Null | Key | Default | Extra          |
+----------+------------------+------+-----+---------+----------------+
| lbeid    | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| lbevalue | varchar(45)      | NO   |     |         |                |
| lbepos   | int(10) unsigned | NO   |     |         |                |
| lbid     | int(10) unsigned | NO   | MUL |         |                |
+----------+------------------+------+-----+---------+----------------+
4 rows in set (0.07 sec)


mysql> desc appdb.listboxes;
+--------+------------------+------+-----+---------+----------------+
| Field  | Type             | Null | Key | Default | Extra          |
+--------+------------------+------+-----+---------+----------------+
| lbid   | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| lbname | varchar(45)      | NO   |     |         |                |
+--------+------------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)


mysql> desc appdb.user;
+-------------+------------------+------+-----+---------+----------------+
| Field       | Type             | Null | Key | Default | Extra          |
+-------------+------------------+------+-----+---------+----------------+
| userid      | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| name        | varchar(45)      | NO   |     |         |                |
| password    | varchar(45)      | NO   |     |         |                |
| canread     | tinyint(1)       | NO   |     |         |                |
| canwrite    | tinyint(1)       | NO   |     |         |                |
| isadmin     | tinyint(1)       | NO   |     |         |                |
| canedituser | tinyint(1)       | NO   |     |         |                |
+-------------+------------------+------+-----+---------+----------------+
7 rows in set (0.07 sec)


Witzig, ja, hier funktioniert es:
Code:
mysql> insert into test.tab(val) values('versuch');
Query OK, 1 row affected (0.12 sec)

mysql> SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
|                2 |
+------------------+
1 row in set (0.00 sec)
 
Zuletzt bearbeitet:
Jo die Tabelle sehen recht gut aus. Fehlen vielleicht ein paar Indizes aber sonst alles ok.
Scheint ja auch zu gehen :)


Leider kann ich die mit ResultSet nicht weiterhelfen, hmm vielleicht mal mit PreparedStatement probieren.

Ansonsten iBATIS oder gaaaanz viel Testen :) - Oder jemand anderes hat noch einen Tipp.


Gruß
Nox
 
Danke, es geht, kA warum, aber es funktioniert! :)

Hab es jetzt so geändert:
Code:
Statement statement = connection.createStatement();
PreparedStatement prepared=connection.prepareStatement("SELECT LAST_INSERT_ID();");
statement.executeUpdate(sqlstring);

ResultSet rs = prepared.executeQuery();
rs.next();
a.setApplicantID(rs.getInt(1));
                
statement.close();
 
Zuletzt bearbeitet:
Zurück
Oben