MySQL Transaktionen in Entity-Class

Hallo alle zusammen,
aktuell beschäftige ich mich ein wenig mit JPA (EclipseLink). Jetzt stoße ich auf ein Problem in den Abhängigkeiten.
Ich demonstriere das mal in einem Beispiel:
Java:
public class Update {
    
    Spieler spieler = new Spieler("Testspieler");
    
    public static void main(String[] args) {
        settings.setProperty("javax.persistence.schema-generation.database.action", "create");
        emfactory = Persistence.createEntityManagerFactory("MeinePU", settings);
        em = emfactory.createEntityManager();

        em.getTransaction().begin();
        
        Spiel spiel = em.find(Spiel.class, 1);
        spiel.beitreten(spieler);
        em.persist(spiel);

        em.getTransaction().commit();
        
        em.close();
        emfactory.close();
    }
}
Code:
@Entity
public class Spiel {
    @Id
    private int id;
    @OneToMany(mappedBy = "spiel")
    private List<Spieler> spieler;
    
    public Spiel(int id) {
        this.id = id;
    }

    public Spiel() {
        this.id = 0;
    }
    
    public List<Spieler> getSpieler() {
        return spieler;
    }
    
    public void beitreten(Spieler spieler) {
        getSpieler().add(spieler);
    }
}
Code:
@Entity
public class Spieler {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    @Column(length = 25)
    private String name;
    @ManyToOne
    private Spiel spiel;
    
    public Spieler(String name) {
        this.name = name;
    }
    
    public Spieler() {
        this.name = "Testspieler";
    }
}
Mein Problem ist, ich möchte über meine Update.class einen Spieler für ein Spiel hinzufügen. Jedoch bekomme ich jedes Mal eine Fehlermeldung, es gäbe nichts zu commiten. Muss ich den EntityManager im Konstruktor an "Spiel" weitergeben und dort die Transaction machen, wenn "beitreten" aufgerufen wird?
Danke allen schon mal!
 
Wie sieht denn Deine persistence.xml aus?

Nachtrag: unabhängig davon dürfte das, was Du da machst, nicht funktionieren. Mit persist werden neue Objekte hinzugefügt, Spiel hast Du bereits aus der DB geladen.
 
Zuletzt bearbeitet:
Wie sieht denn Deine persistence.xml aus?

Nachtrag: unabhängig davon dürfte das, was Du da machst, nicht funktionieren. Mit persist werden neue Objekte hinzugefügt, Spiel hast Du bereits aus der DB geladen.
Wird also durch den reinen Aufruf von "beitreten" das direkt geupdatet, wenn es commited wird?

Die Persistence funktioniert... aber hier, bitte ;)
XML:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="MeinePU" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <class>de.JohnHSmith.RHFactions.Faction</class>
        <class>de.JohnHSmith.RHFactions.Player</class>
        <properties>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/rhfactions"/>
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="javax.persistence.jdbc.password" value=""/>
            <property name="javax.persistence.schema-generation.database.action" value="create"/>
            <property name="javax.persistence.schema-generation.scripts.action" value="create"/>
            <property name="eclipselink.ddl-generation" value="create-tables"/>
        </properties>
    </persistence-unit>
</persistence>
 
Heißen Deine Entities jetzt Spiel und Spieler oder Game und Faction?

Die Persistence funktioniert..
Ich will ja nur sicher gehen...

Wird also durch den reinen Aufruf von "beitreten" das direkt geupdatet, wenn es commited wird?
Jein. Wenn eine Entity vom EntityManager gemanaged wird, dann werden Änderungen an der Entity registriert und zu bestimmten Zeitpunkten weggeschrieben.

Dabei muss man bei Beziehungen aufpassen, denn es ist ein Unterschied zwischen der Entity selbst und der Beziehung zu dieser Entity.

Würdest Du in dem Beispiel oben einfach das persist weglassen, würde folgendes passieren:
1. es wird ein neues Spieler-Objekt erzeugt, von dem der EntityManager keine Kenntnis hat. Aus sich des EntityManagers hat ein solches Objekt den Status NEW.
2. das Spiel wird aus der DB gelesen und ist im MANAGED-State.
3. das Spieler-Objekt wird zur Liste spieler der Spiel-Entity hinzugefügt.

Da die Spiel-Entity geändert wurde, würde der EntityManager versuchen, die Änderungen wegzuschreiben. Dabei stößt er auf die Spieler-Entity, die sich im NEW-State befindet und quittiert dies mit einem Fehler.

Du könntest also hergehen, und den Spieler vorher per persist wegschreiben. Dann nämlich befindet sich der Spieler ebenfalls im MANAGED-State und für den EntityManager wäre alles in Ordnung. Alternativ zum expliziten Aufruf von persist kannst Du auch angeben, dass der EntityManager neue Entities in der Beziehung automatisch persistieren soll. Dazu fügst Du der OneToMany-Annotation ein "cascade=CascadeType.PERSIST" hinzu.
 
Ah sorry, hab vergessen in der persistence.xml die Klassen umzubenennen für das Beispiel.
Ich probier mal noch ein bisschen rum, sollte dann ja klappen ;)
 
Hallo,
ich habe jetzt folgendes versucht:
Code:
public class Update {
   
    public static void main(String[] args) {
        settings.setProperty("javax.persistence.schema-generation.database.action", "create");
        emfactory = Persistence.createEntityManagerFactory("MeinePU", settings);
        em = emfactory.createEntityManager();

        em.getTransaction().begin();
       
        Spiel spiel = em.find(Spiel.class, 1);
        Spieler spieler = new Spieler("Testspieler");
        em.persist(spieler);
        spiel.beitreten(spieler);

        em.getTransaction().commit();
       
        em.close();
        emfactory.close();
    }
}
Allerdings heißt es immernoch, in der Transaction wäre nichts zu committen. Auch habe ich cascade auf Persist gestellt, jedoch passiert da auch nichts.
 
Zuletzt bearbeitet:
Kannst Du mal ein kleines Beispiel zusammenschustern (am besten eines, das Maven verwendet), zippen und hier hochladen?

Das einzige, was meine Glaskugel noch hergibt, wäre dass Du die Spiel und/oder Spieler-Klasse in der persistence.xml nicht aufgeführt hast.
 
Kannst Du mal ein kleines Beispiel zusammenschustern (am besten eines, das Maven verwendet), zippen und hier hochladen?

Das einzige, was meine Glaskugel noch hergibt, wäre dass Du die Spiel und/oder Spieler-Klasse in der persistence.xml nicht aufgeführt hast.
Mit Maven hab ich bisher noch nicht gearbeitet....
Ich glaube, ich versuche hier zu weit advanced in JPA einzusteigen. Vielleicht gehen wir das ganze besser Schritt für Schritt an....
Ich lade mal was hoch, kannst du mir erklären wieso der Spieler zwar angelegt wird, jedoch als spiel-id (sollte ja ein FK sein) immer NULL eingetragen bekommt und nicht die tatsächliche Spiel-Nummer?
(Datei ist leider zu groß um sie hier hochzuladen, da die Libraries mit dabei sind... deshalb hier ein Dropbox-Link: https://www.dropbox.com/s/os6to7jechwdzfu/TestPersistence.zip?dl=0)
Danke schonmal für deine Hilfe!
 
Ich vermute weil Getter und Setter in der Entity Klasse fehlt. Kann aber auch falsch liegen. Bin nicht so in den JPA internas...
 
kannst du mir erklären wieso der Spieler zwar angelegt wird, jedoch als spiel-id (sollte ja ein FK sein) immer NULL eingetragen bekommt und nicht die tatsächliche Spiel-Nummer?
Also wird ja doch etwas committed... Zur Frage: weil Du eine bidirektionale Beziehung definiert hast. Mit mappedBy gibst Du dabei das Attribut an, über das die Beziehung hergestellt wird. Du musst also auf der Spielerseite das Spiel angeben. Das kannst Du z. B. auch in der beitreten-Methode machen:
Java:
    public void beitreten(Spieler spieler) {
        getSpieler().add(spieler);
        spieler.setSpiel(this);
    }
Natürlich braucht die Spieler-Klasse einen entsprechenden Setter.
 
Also wird ja doch etwas committed...
Das war nur ein Beispiel-Code. Der lustigerweise auch funktioniert. In meinem echten Code habe ich jetzt mal folgendes gemacht:
Java:
Query query = em.createNamedQuery("Spieler.findByName");
query.setParameter("name", "Testspieler");
Spieler spieler = null;
if( !query.getResultList().isEmpty() ) {
    spieler = (Spieler) query.getSingleResult();
} else {
    spieler = new Spieler("Testspieler");
}
em.persist(spieler);

em.getTransaction().commit();
Ich bekomme trotz allem eine Fehlermeldung, dass keine Transaktion ausstehend wäre. Das ist doch nicht normal? In meinem Testprogramm läuft es auch... Aber da ich die Transaktion beginnen lasse, müsste er doch was finden!

EDIT:
Ich habe gerade herausgefunden, dass ich in der gleichen Main-Class weiter oben schon mal committed habe. Nachdem ich danach ein em.getTransaction().begin() eingefügt habe, ging es! Muss ich das jedesmal nach Committen neu beginnen?
 
Zuletzt bearbeitet:
Ist das, was Du in der dropbox geschickt hast, nun irgendein Testcode, der funktioniert oder ist das "richtiger" Code, der nicht funktioniert?

Zum Schnipsel kann man wenig sagen (außer, dass das persist in den else-Zweig gehört).

Mach mal eine Kopie Deines Projekts und aus dem entfernst Du alles, was nicht benötigt wird, um den Fehler nachvollziehen zu können. Oft findet man dabei schon die Ursache des Problems. Wenn nicht, machst Du ein ZIP draus, lädst es hier hoch (die Bibliotheken kannst Du weglassen), damit sich das jemand aus dem Forum am lebenden Objekt nachvollziehen kann.
 
Ist das, was Du in der dropbox geschickt hast, nun irgendein Testcode, der funktioniert oder ist das "richtiger" Code, der nicht funktioniert?
Das ist ein Testcode.
Zum Schnipsel kann man wenig sagen (außer, dass das persist in den else-Zweig gehört).
Danke für den Hinweis!
Mach mal eine Kopie Deines Projekts und aus dem entfernst Du alles, was nicht benötigt wird, um den Fehler nachvollziehen zu können. Oft findet man dabei schon die Ursache des Problems. Wenn nicht, machst Du ein ZIP draus, lädst es hier hoch (die Bibliotheken kannst Du weglassen), damit sich das jemand aus dem Forum am lebenden Objekt nachvollziehen kann.
Genau das habe ich gemacht, weshalb ich den Fehler herausgefunden haben :) Danke!
 
Wie ich oben im Edit schon geschrieben hatte, ich hatte weiter oben im Code bereits committed und danach nicht mehr die Transaction neu begonnen. Offenbar muss man das jedes Mal machen, nachdem committed wurde.
 
Passende Stellenanzeigen aus deiner Region:

Neue Themen

Oben