SQLalchemy - ORM - commit() ohne add()?

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute,

ich habe ein kleines Verständnisproblem. Im nachfolgenden Beispiel werden Änderungen, Einträge und Löschungen mit commit() durchgeführt. In manchen Übungen werden zum Beispiel bei Eintragungen oder Änderungen immer ein add() vor dem commit() gesetzt. Das nachfolgende Beispiel scheint jedoch auch ohne add() aus zukommen - zumindest bei Änderung von Datensätzen. Ich habe mir dazu einiges gelesen und bin hier: SQLAlchemy: What's the difference between flush() and commit()? stehen geblieben. Wenn ich also recht verstehe, registriert das Session-Objekt mittels der Operation add() nur die Transaktion, aber kommuniziert keineswegs mit der Datenbank? Das heißt, es muss entweder ein flush() oder ein commit() folgen? Aber dann bin ich noch mehr verwirrt. Ich habe auf meinem System den nachfolgenden Quelltext einfach mal das commit() bei Löschungen, Änderungen und Eintragungen auskommentiert und nur add() und delete() durchgeführt. Die Datensätze wurden trotzdem erfolgreich in der Datenbank gespeichert oder wurden erfolgreich aus der Datenbank gelöscht. Mit dieser Verwirrung wende ich mich an euch. Kann mir da jemand etwas mehr Licht in die Runde bringen? Mir geht es dabei um die Frage, ob ich dabei Gefahr laufe, wenn ich zum Beispiel nur add() / delete() oder nur commit() benutze? Denn ich verstehe nun nicht, weshalb ich beides benutzen sollte, wenn doch eines von beiden ihre Aufgabe erledigt? Selbst beim Löschen kann ich auf einen commit() verzichten.

Code: Alles auswählen

from sqlalchemy import create_engine  
from sqlalchemy import Column, String  
from sqlalchemy.ext.declarative import declarative_base  
from sqlalchemy.orm import sessionmaker

db_string = "postgres://admin:donotusethispassword@aws-us-east-1-portal.19.dblayer.com:15813/compose"

db = create_engine(db_string)  
base = declarative_base()

class Film(base):  
    __tablename__ = 'films'

    title = Column(String, primary_key=True)
    director = Column(String)
    year = Column(String)

Session = sessionmaker(db)  
session = Session()

base.metadata.create_all(db)

# Create 
doctor_strange = Film(title="Doctor Strange", director="Scott Derrickson", year="2016")  
#session.add(doctor_strange)  
session.commit()

# Read
films = session.query(Film)  
for film in films:  
    print(film.title)

# Update
doctor_strange.title = "Some2016Film"  
#session.add(doctor_strange)
session.commit()

# Delete
session.delete(doctor_strange)  
session.commit()  
BlackJack

@Sophus: Ohne den ersten auskommentierten `add()`-Aufruf sollte der Film nicht in der Datenbank landen.

Der zweite auskommentierte `add()`-Aufruf ist überflüssig. `add()` fügt ein *neues* Objekt der Sitzung hinzu. Wenn das einmal mit `add()` hinzugefügt wurde, dann gehört es ja zur Sitzung. Das gilt auch für Objekte die über die Sitzung von der Datenbank abgefragt wurden, die braucht man natürlich nicht mit `add()` hinzufügen.

Wie hast Du denn geprüft ob die Daten ohne `commit()` in der Datenbank waren? Für die laufende Transaktion sind sie natürlich ”in der Datenbank”, was je nach `flush()` oder nicht bedeuten kann sie sind nur SQLAlchemy bekannt oder aber auch schon der Datenbank, aber ohne `commit()` nur in der aktuellen Transaktion.

`add()` und `commit()` sind zwei unterschiedliche Dinge, man kann die nicht austauschen und auch nicht durcheinander ersetzen. Und auf `commit()` verzichten schon gar nicht, es sei denn man möchte nichts persistent in der Datenbank speichern.

Das Verhalten welches Du beschreibst kann ich mir nicht vorstellen. Da müsste man in der Datenbank schon Transaktionen ausschalten. Was bei MySQL noch vorstellbar wäre, weil die DB-Engine vor Inno-DB keine Transaktionen konnte. Aber Du verwendest da ja PostgreSQL und da wäre es ungewöhnlich wenn Transaktionen in der Konfiguration der Datenbank ausgeschaltet wären.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: Einige Korrekturen meinerseits. Also das erstmalige Abspeichern geht natürlich nicht ohne add(), das ist richtig. Aber ich kann diesen Datensatz ohne commit() in die Datenbank speichern. Und beim Löschen und Ändern brauche ich ebenfalls kein commit(). Und dann wäre noch eine Sache. Ich benutze zur Testzwecke MySql. Das im Beispiel PostGre-Treiber benutzt wird, ist mein Fehler. Dort sollte eigentliyl der MySQL-Treiber stehen.

Und wie ich das überprüft habe? Indem ich mittels der PowerShell auf den MySQL-Datenbankserver zugreife und nachschaue. Und dort sind dann die entsprechenden Einträge geändert oder gelöscht worden - ohne commit().
BlackJack

@Sophus: Dann müsstest Du schauen ob die MySQL-Datenbank Transaktionen unterstützt oder nicht und gegebenenfalls dafür sorgen.
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

AFAIK läuft MySQL standardmäßig mit `autocommit`, d.h. man muss nicht explizit `commit()` aufrufen, das erfolgt automatisch. War jedenfalls früher so und bei der alten MySQL DB-Egnine MyISAM gab's so wie so keinen expliziten `commit`. Zwar hat MySQL ja schon vor längerem auf InnoDB als Standard-Enginge gewechselt, aber der autocommit ist geblieben.

In diesem Punkt sind PostgreSQL und SQLite strikter, hier wird der SQL-Befehl `commit` explizit erwartet, damit die Operation durchgeführt wird.

Gruß, noisefloor
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Ok, wenn ich das mal zusammenfassen darf. Es handelt sich hierbei um ein MySQL spezifisches Problem? Gut, dann werde ich nach jedem add() und nach jedem delete() das commit() zur Sicherheit aufrufen. Ich war nur etwas verwirrt. Danke euch.
BlackJack

@noisefloor: Das stimmt zwar für MySQL aber die DB API 2.0 ist standardmässig *nicht* autocommit, das heisst die entsprechenden Module schalten das aus bzw. Transaktionen ein. Und SQLAlchemy wird das dann sicher nicht wieder ändern, denn das rechnet ja mit Transaktionen. Das sieht also schon danach aus als wenn die verwendete Engine keine Transaktionen kann, oder das irgendwie hart ausgeschaltet wurde in der Konfiguration der Datenbank.

@Sophus: Ich würde ja eher zusehen das die Datenbank sinnvoll konfiguriert wird, also Transaktionen kann.
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Das stimmt zwar für MySQL aber die DB API 2.0 ist standardmässig *nicht* autocommit,
Aha - ok.

@Sophus: welche MySQL Version und welche DB-Engine verwendest du denn?
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@noisefloor: Also die Server-Version von MySQL ist 5.9.25 und als Engine habe ich InnoDB. Ich muss aber hinzufügen, dass ich den Server vor ungefähr einem Jahr als Standard-Installation installiert habe. Daher weiß ich auch nicht ob nun die Transaktion ein- oder ausgeschalten ist.
Antworten