MySQL Datensatz wird nicht richtig gelöscht

ahunzs

Mitglied
Hallo zusammen, ich bin's wieder mal! :D

Ich habe eine Datenbank aufgesetzt und arbeite jetzt mit JPA und DBUnit darauf.
Ich bin noch in der Lernphase und habe deshalb einige kleinere UnitTests erstellt.
einer davon will einfach nicht ordentlich laufen. Die Datenbank besteht aus 3 Tabellen
Member(Die Familienmitglieder), Family(die Familien), Member_has_family(die Join-Tabelle).
Bei dem Test sollte ein bestimmter User aus der Tabelle Member gelöscht werden,
dementsprechend sollten auch die Datensätze aus der Join-Tabelle gelöscht werden.
Bei der Join-Tabelle funktioniert es super, nur wird der Datensatz in der Member-Tabelle nicht gelöscht!

[JAVA=100]@Test
public void test_deleteMember() {
_em.getTransaction().begin();
Family f = _em.find(Family.class, 2);
List<Member> members = f.getMembers();
assertEquals(5, f.getMembers().size());
Member deletedMember = members.remove(0);
int id = deletedMember.getId();
assertEquals(4, f.getMembers().size());
f.setMembers(members);
_em.persist(f);
_em.flush();
_em.getTransaction().commit();

members = null;
f = null;

f = _em.find(Family.class, 2);
assertEquals(4, f.getMembers().size());
members = f.getMembers();
for (Member m : members) {
if (m.getId() == id) {
fail("Falsch gelöscht!");
}
}
if (_em.find(Member.class, 3) != null) {
_em.getTransaction().begin();
fail("Nur aus Join-Tabelle gelöscht!"); //<--- Hier ist das Problem!
_em.persist(f);
_em.flush();
_em.getTransaction().commit();
}

}[/code]

OrphanRemoval kann ich nicht verwenden, da die Tabellen in einer @ManyToMany-Relation sind.
Hat mir einer von euch vielleicht einen Tipp? Danke schonmal im voraus!
 
Zuletzt bearbeitet von einem Moderator:

ahunzs

Mitglied
Ok ich habe mir jetzt mal ausgedacht, dass ich es wohl oder übel von Hand löschen lassen muss.
:bahnhof: Habe mir da mal eine kleine Methode zusammengeschustert.
Nun stehe ich wieder an vor einer Wand und komme nicht weiter. Von der Tabelle member_has_family kann ich keine Instanzen machen,
da ich keine Klasse habe! Wie soll ich nun das Query aussehen lassen?
Wie gesagt, ich möchte alle Member, welche in der Join-Tabelle nicht mehr drin sind,
auch aus der Member-Tabelle löschen! Vielleicht kann mir ja jetzt einer helfen?

Methode:
[JAVA=15]
public static void delete(Member memberToDelete, EntityManager em) {
Query q = em.createQuery("SELECT m from Member WHERE :family NOT MEMBER OF /*Was kommt hier rein??*/ ");
q.setParameter("family", memberToDelete.getFamilies());
memberToDelete = (Member) q.getSingleResult();
em.remove(memberToDelete);
}
[/code]
 
Zuletzt bearbeitet:
N

nillehammer

Gast
OrphanRemoval kann ich nicht verwenden, da die Tabellen in einer @ManyToMany-Relation sind.
Das wäre auch für den Fall hier garnicht das Richtige. OrphanRemoval würde greifen, wenn Du eine Family löschst und würde sicher stellen, dass danach nicht Members ohne Family rumfliegen. Ob Du das mit JoinColumn oder JoinTable machst, ist dabei ziemlich egal.

Dein Testcode sieht soweit gut aus (man könnte meiner Meinung nach ein paar unnötige Befehle streichen, aber für den Test ist das erstmal egal). Nur eine Zeile macht mich etwas stuzig:
Java:
if (_em.find(Member.class, 3) != null) {
Hier suchst Du nach einer magischen Konstanten 3. Da die Ids ja höchstwahrscheinlich automatisch vergeben werden, wie kannst Du da sicher sein, dass das von Dir gelöschte Member wirklich die id 3 hat? Wäre folgendes nicht besser?
Java:
// Nach der weiter oben gemerkten id des deletedMember suchen
if (_em.find(Member.class, id) != null) {
Weiters kann man gerade am Anfang auch immer mal parallel direkt in die Datenbank schauen, um zu überprüfen, das der Test wirklich das macht, was man sich vorgestellt hat.
 

ahunzs

Mitglied
Hallo nillehammer. Zuerst mal danke für deinen Tipp mit der id. Ich werde dass noch ändern. Parallel schaue ich immer in der Datenbank nach und soweit funktioniert der Test auch teilweise. der Datensatz wird aus der Join-Tabelle gelöscht, aber der Datensatz in der Member-Tabelle bleibt enthalten.
Wie muss ich nun das JPQL in der Methode "Delete" machen, dass es mir diese Member ausgibt, welche nicht mehr an eine Familie gebunden sind? die Tabellen sehen so aus:

[tbl_member]{ID(PK), name, prename, age, gender}
[tbl_family]{id_family(PK), name}
[tbl_family_has_tbl_member]{family_id, member_id}

ich weiss, ich habe die Tabellen und Spalten schlecht benannt.. :autsch:

danke nochmals

ahunzs
 
N

nillehammer

Gast
Ich bin ja auch zu blöd. Das hätte mir eigentlich gleich auffallen könne. Mit dem
Java:
Member deletedMember = members.remove(0);
Löst Du auf Objektebene erstmal nur die Beziehung zwischen der einen Family und dem Member auf. Nachdem Du mit einer Join-Tabelle arbeitest, könnte es mit diesem Datenmodell, ja sein, dass das Member auch eine Beziehung zu einer anderen Family hat. Die Auflösung einer Beziehung kann also nicht als Auslöser für das Löschen des Members dienen. Darüber hinaus hast Du das Mapping wahrscheinlich so gebaut, dass es Members ohne Family geben darf. Auch hier kann Hibernate dann nicht einfach löschen. Lösch also einfach das memberToDelete mit em.delete
 

ahunzs

Mitglied
Cool, jetzt habe ich es geschafft! Der Test ist jetzt folgendermassen aufgebaut:
[JAVA=125]
@Test
public void test_deleteMember() {
_em.getTransaction().begin();
Family f = _em.find(Family.class, 2);
List<Member> members = f.getMembers();
assertEquals(5, f.getMembers().size());
Member memberToRemove = members.remove(0);
assertEquals(4, f.getMembers().size());
_em.persist(f);
_em.flush();
CodeCollector.delete(memberToRemove, _em);

_em.getTransaction().commit();

members = null;
f = null;

f = _em.find(Family.class, 2);
assertEquals(4, f.getMembers().size());
members = f.getMembers();
int id = memberToRemove.getId();
for (Member m : members) {
if (m.getId() == id) {
fail("Falsch gelöscht!");
}
}
if (_em.find(Member.class, id) != null) {
fail("Nicht gelöscht!");
}

}
[/code]

und die "Delete"-Methode so:

[JAVA=16]
public static void delete(Member memberToDelete, EntityManager em) {

Query q =
em
.createNativeQuery("SELECT ID FROM tbl_member m WHERE NOT EXISTS ( SELECT member_id FROM tbl_family_has_tbl_member WHERE member_id = m.ID ) and m.Id = :THEID");
q.setParameter("THEID", memberToDelete.getId());
try {
q.getSingleResult();
em.remove(memberToDelete);
} catch (NoResultException e) {
}

}
[/code]

Vielen Dank nochmals für die Hilfe! :D
 
N

nillehammer

Gast
Auch, wenn es jetzt geht, denk nochmal über Dein Mapping nach. Wie Du selbst vielleicht schon bemerkt hast, ist Deine delete-Methode etwas umständlich (native Query, nested Select, erst laden, dann wieder löschen). Mit der Query versuchst Du ja alle Members zu löschen, die keine Family haben. Das scheint also eine Fachanforderung zu sein. Versuche diese in Deinen Entities abzubilden. Weiters, versuche Dir anzugewöhnen, immer von der Objektwelt her zu denken. Erst dadurch werden einem diese ganzen JPA-Spezifika klar. Wenn Du also definierst: "Ein Member muss eine Family haben.", drückst Du das in der Objektwelt so aus:
Java:
public class Member {

  @JoinColumn(nullable=false)
  @ManyToOne
  private Family family;

  /** 
     * Default-Constructor für Hibernate
     * Client-Code soll den nicht benutzen,
     * deswegen protected. Warum protected und nicht
     * private? Hibernate arbeitet (im Moment) noch mit
     * Subklassen der Entities (deswegen dürfen die nicht final sein)
     * Bei private müsste Hibernate mit Reflection die Sichtbarkeit erhöhen.
     */
  protected Member() {}

  /**
     * Wir haben als Fachanforderung definiert, dass
     * es keinen Member ohne Family geben darf. 
     * Also soll Clientcode bitte die Family beim Erzeugen
     * eines Members gleich mitgeben. Deswegen dieser
     * Constructor public.
     */
  public Member(final Family family) {
    // Ggf. hier noch auf null prüfen und Exception schmeißen.
    // Oder Java Bean Validation API benutzen,
    // aber das ist eine andere Baustelle
    this.family = family;

    // Du hast jetzt eine bidirektionale Beziehung.
    // Du musst selbst dafür sorgen, dass beide Enden korrekt sind.
    family.addMember(this);
  }

  ... weitere Felder...
  ...getter/setter... setFamily protected machen
  // Wenn ein Member seine Family wechseln können soll,
  // implementier eine Methode changeFamily(final Family newFamily), die folgendes macht:
  // - member aus bisheriger Family löschen (this.family.removeMember(this))
  // - neue Family zuweisen (this.family = newFamily)
  // - member neuer Family hinzufügen newFamily.addMember(this);
  // Warum extra Methode und nicht im setter? Setter sollen nichts anderes machen,
  // als eine Instanzvariable zu ändern + ggf. Parameterprüfung (weiß auch nicht wieso).
}
Deine Family sieht jetzt so aus:
Java:
public class Family {

  /**
     * Ich nehm lieber Sets statt Listen, weil:
     * - Jedes Member soll ja nur einmal pro Family vorkommen.
     * - Bei Listen muss in der DB eine extra Index-Spalte geführt werden
     * - Es ist ein klein wenig performanter.
     */
  @OneToMany(cascade = CascadeType.ALL, mappedBy ="family", orphanRemoval=true)
  private Set<Member> members;
  
  ... ggf. weitere Felder...
  ...getter/setter für alle anderen Felder [B]außer members [/B]...

  /** 
     * Protected getter für Hibernate, client code soll den nicht benutzen.
     */
  protected Set<Member> getMembers() {
    return this.members;
  }

  /** 
     * Wenn jemand gerne die members zum iterieren haben will,
     * dann mit dieser methode. Sie stellt sicher, dass niemals null
     * zurück gegeben wird (Vermeidung von NullPointerExceptions
     * und Clientcode muss nicht ständig selber auf null prüfen).
     * Weiters macht sie das Set unmodifiable, d.h. niemand kann direkt
     * Members adden/removen. Dafür soll er die Methoden addMember/removeMember
     * dieser Klasse
     * benutzen.
     */
  public final Set<Member> getMembersUnmodifiable() {

    if (this.members != null) {
       return Collections.unmodifiableSet(this.members);
    }

    return Collections.emptySet();
  }

  /** 
     * Protected setter für Hibernate, client code soll den nicht benutzen.
     * Wir wollen nicht, dass jemand von außen an unserem Set rumfuddelt.
     */
  protected void setMembers(final Set<Member> members) {
    this.members = members;
  }

  protected final boolean addMember(final Member member) {

    // Lazy initialization, um unnötige Erzeugung des Sets
    // zu vermeiden
    if (this.members == null) {
      this.members = new LinkedHashSet<Member>();
    }

    return this.members.add(member);
  }

  public final boolean removeMember(final Member member) {

    if (this.members == null) {
      return false;
    )
    
    return this.members.remove(member);
  }

}
Mit diesem Mapping wird ein Member gelöscht, wenn die Family weg ist (orphanRemoval) und es wird auch gelöscht, wenn es aus dem Set<Member> removed wird. Deine delete-Methode brauchst Du so dann nicht mehr. Damit das alles so klappt, müssen Family und Member übrigens im selben Package liegen!

Was aber auffällt, ist, dass man jetzt gerade bei Family ganz schön viel zusätzlichen Code für das Handling des Sets und bei Member im Constructor das zusätzliche add hat. Ich bin deswegen von Collections fast völlig runter. Ich speicher mir nur die Family im Member, streiche das Set<Member> aus Family und schreibe mir eine dao-Methode loadAllMembersForFamily(final Family family). Das orphanRemoval kann man über @OnDelete abbilden.
 
Zuletzt bearbeitet von einem Moderator:
Ähnliche Java Themen
  Titel Forum Antworten Datum
H letzter Datensatz wird nicht gefunden? Datenbankprogrammierung 6
E Nur der letzte Datensatz wird in Tabelle geschrieben Datenbankprogrammierung 4
W Nur ein Datensatz wird angezeigt, obwohl mehrere existieren Datenbankprogrammierung 7
F SQLite Datensatz löschen Datenbankprogrammierung 13
B Mit Button einen Datensatz löschen Datenbankprogrammierung 2
S sql query, um bestimten datensatz zu finden Datenbankprogrammierung 33
D MySQL Neuen Datensatz mit "new" anlegen Datenbankprogrammierung 16
M Serienbrief aus Datensatz Datenbankprogrammierung 2
E Aufzählung liest nur ersten Datensatz Datenbankprogrammierung 14
S SQLite Selectanfrage lieft datensatz aber cursor ist leer Datenbankprogrammierung 2
M Designfrage zu Rows die sich auf einen Datensatz derselben Tabelle beziehen Datenbankprogrammierung 7
L SQLite fügt nur den ersten Datensatz ein Datenbankprogrammierung 2
C Hibernate Datensatz löschen Datenbankprogrammierung 2
M Access Datensatz beinhaltet ESC-Zeichen Datenbankprogrammierung 3
U Nur den ersten Datensatz löschen wenn mehrere gleiche da sind Datenbankprogrammierung 2
C Gleichzeitiger Zugriff auf Datensatz Datenbankprogrammierung 5
Eldorado MySQL Hibernate - neuen Datensatz einfügen Datenbankprogrammierung 2
Java.getSkill() Datensatz / Tupel automatisch nach xy Tagen / Stunden löschen Datenbankprogrammierung 7
M Datensatz exklusiv öffnen Datenbankprogrammierung 5
M design issue: datensatz lange locken? Datenbankprogrammierung 10
M Datensatz aus Datenbank löschen Datenbankprogrammierung 6
T Theoriefrage : mehrere User vs gleichen Datensatz Datenbankprogrammierung 4
K Schnelle Methode um zu testen ob Datensatz existiert Datenbankprogrammierung 9
R datenbank - datensatz sucjen Datenbankprogrammierung 4
-MacNuke- Hibernate löscht jeden Datensatz einzeln? Datenbankprogrammierung 12
V Doppelten Datensatz vermeiden? Datenbankprogrammierung 7
S Überprüfen ob Datensatz existiert Datenbankprogrammierung 2
P SQL, nur jeder 5. Datensatz Datenbankprogrammierung 9
B Eingefügter Datensatz erneut abfragen. Datenbankprogrammierung 10
J Anzeige Button gibt immer nur 1. Datensatz aus DB aus Datenbankprogrammierung 8
M Letzter Datensatz mit auslesen(brauch kleinen Tipp) Datenbankprogrammierung 15
N SELECT: Datensatz sperren Datenbankprogrammierung 6
C vorheriegen oder letzten Datensatz Datenbankprogrammierung 2
K ResultSet: Erste Zeile ausgeben, Datensatz in String speiche Datenbankprogrammierung 13
C Datensatz einfügen und quittierung Datenbankprogrammierung 31
F suche MySQl-Befehl um Datensatz an Tabellenende zu schreiben Datenbankprogrammierung 2
D JOIN COLUMN wird nicht genommen Datenbankprogrammierung 2
Maxim6394 JPA / EclipseLink - n:m Beziehung wird nicht aktualisiert Datenbankprogrammierung 0
Maxim6394 JPA 3.2 & SQLite - LocalDateTime wird falsch geladen Datenbankprogrammierung 1
D Ich möchte dass ich nachdem man den Kommentar geschrieben hat den Kommentar in den Tabelle Bestellübersicht geschbeichert wird klappt nicht bei mir Datenbankprogrammierung 2
Watsoon Treiber wird in Intellij nicht geladen Datenbankprogrammierung 2
Kirby.exe PreparedStatement wird nicht ausgeführt Datenbankprogrammierung 5
Z Datenbank Choicebox wird nicht befüllt Datenbankprogrammierung 15
N Sqlite DB mit Java wird auf Linuxsystem nicht gefunden Datenbankprogrammierung 9
J Netbeans 11 und Eclipse JPA 2.5 Entity wird nicht gefunden Datenbankprogrammierung 4
F H2 ObjectOptimisticLockingFailureException wird nicht geworfen Datenbankprogrammierung 0
S sun.jdbc.odbc.JdbcOdbcDriver wird nicht gefunden Datenbankprogrammierung 2
S MySQL Speicher wird nicht freigegeben bei Datenbankabfragen Datenbankprogrammierung 6
D MySQL Eingabe wird nicht übernommen... Datenbankprogrammierung 11
L Select Anweisung wird falsch interpretiert Datenbankprogrammierung 3
F sun.jdbc.odbc.jdbcodbcdriver wird nicht gefuden Datenbankprogrammierung 3
L JTable-DB: Letzter wert wird nicht angezeigt. Datenbankprogrammierung 0
K Datenbank wird bei Programmstart als .jar nicht mehr befüllt Datenbankprogrammierung 12
T MySQL Datetime wird auf Sekunden gerundet, soll es aber nicht Datenbankprogrammierung 2
N SQL-Statement Auslesen des Resultset wird immer langsamer Datenbankprogrammierung 6
P JSF + H2 + TomEE + Hibernate/JPA Datenbank wird nicht angelegt Datenbankprogrammierung 3
C Programm wird nach DB-Eintrag nicht weitergeführt Datenbankprogrammierung 5
M OutOfMemoryException obwohl nur 1 Eintrag abgefragt wird Datenbankprogrammierung 7
T f:event preRenderView Trigger wird nicht ausgeführt Datenbankprogrammierung 4
P Oracle Hibernate - Oracle-VarChar-Index wird nicht genutzt Datenbankprogrammierung 3
H Microsoft Access Treiber wird nicht gefunden Datenbankprogrammierung 9
S JPA, Primary Key wird nicht in relationstabelle erstellt Datenbankprogrammierung 3
B MySQL Hibernate (duplicate Entry) Beziehungstabelle wird nicht befüllt Datenbankprogrammierung 8
N batchupdate, es wird nur der letzte Eintrag eingetragen Datenbankprogrammierung 5
N batch-Update wird nicht durchgeführt Datenbankprogrammierung 2
M Rückgabewert aus DB wird falsch gelesen Datenbankprogrammierung 3
S Wie überprüfe ich ob die Instanz einer Connection gerade werwendet wird? Datenbankprogrammierung 4
C DB.Treiber wird nicht gefunden? Datenbankprogrammierung 5
E MySQL Treiber wird nicht geladen Datenbankprogrammierung 5
K org.gjt.mm.mysql.driver wird nicht gefunden Datenbankprogrammierung 11
T Unerwartete Datenbankausgabe bei "select now()" (es wird ein .0 angehängt) Datenbankprogrammierung 5
N Ubuntu 9.04 jdbc connector wird nicht gefunden Datenbankprogrammierung 5
K Wird auf den Eintrag referenziert? Datenbankprogrammierung 4
J SqlConnection Instanz wird immer grösser? Datenbankprogrammierung 2
I Sql Befehl wird nicht ausgeführt Datenbankprogrammierung 8
sparrow [Hibernate] Relation wird nicht gespeichert (Topic changed) Datenbankprogrammierung 11
A REVOKE wird nicht durchgeführt Datenbankprogrammierung 2
M Treiber wird nicht gefunden Datenbankprogrammierung 5
P Datenbank wird nicht geschlossen - problematisch? Datenbankprogrammierung 2
N mysql.jdbc treiber wird nicht gefunden Datenbankprogrammierung 2
D Neuer Query wird nicht erkannt Datenbankprogrammierung 10
S JOptionPane wird nicht angezeigt Datenbankprogrammierung 5
ven000m SQL Querry wird nicht abgesetzt, da vorher underlined! Datenbankprogrammierung 8
D "Distinct" wird nicht ausgeführt Datenbankprogrammierung 8
H mySql Connector/J Treiber wird nicht gefunden Datenbankprogrammierung 2
C SQLException wenn String auf VARCHAR geschrieben wird Datenbankprogrammierung 10
H JDBC-Treiber wird nicht geladen Datenbankprogrammierung 2
L SQL-Problem: Insert wird nicht ausgeführt Datenbankprogrammierung 5
N Cloudscape wird opensource :) Datenbankprogrammierung 3
X Verbindung zu DB wird zurückgewiesen Datenbankprogrammierung 8
M Meine Datenbank lässt sich mit meiner Methode nicht ändern Datenbankprogrammierung 1
P Reicht finally nicht um DB connections zu schließen in (altem) Java? Datenbankprogrammierung 4
J SQLite Abfrage fehlerhaft - komme nicht weiter - please help. Datenbankprogrammierung 3
M Datenbank Zugraff nach Umwandlung in .jar-Datei nicht mehr möglich Datenbankprogrammierung 4
Auf MySql Datenbank zugreifen funktioniert nicht Datenbankprogrammierung 8
A Java DB Server lässt sich nicht starten Datenbankprogrammierung 3
B SQlite Datenbank, trotz Statements wurden nicht alle Zeilen erzeugt? Datenbankprogrammierung 35
T Datenzeilen werden nicht gelöscht Datenbankprogrammierung 6
Warum funktioniert MySQL nicht Datenbankprogrammierung 8
S Das printen der Ausgabe des Oracle-SQL-Statements in der Java-Eclipse-Konsole funktioniert nicht Datenbankprogrammierung 6

Ähnliche Java Themen

Neue Themen


Oben