Kattava opas SQLAlchemy-istunnonhallintaan Pythonissa, keskittyen tehokkaisiin transaktioiden käsittelytekniikoihin tietojen eheyden ja johdonmukaisuuden varmistamiseksi sovelluksissasi.
Python SQLAlchemy-istunnonhallinta: Transaktioiden hallinnan hallitseminen tietojen eheyden varmistamiseksi
SQLAlchemy on tehokas ja joustava Python-kirjasto, joka tarjoaa kattavan työkalupakin tietokantojen kanssa vuorovaikutukseen. SQLAlchemy:n ytimessä on käsite istunto, joka toimii väliaikaisena alueena kaikille tietokantaan suorittamillesi toiminnoille. Oikea istunto- ja transaktiohallinta on ratkaisevan tärkeää tietojen eheyden ylläpitämiseksi ja johdonmukaisen tietokannan toiminnan varmistamiseksi, erityisesti monimutkaisissa sovelluksissa, jotka käsittelevät samanaikaisia pyyntöjä.
SQLAlchemy-istuntojen ymmärtäminen
SQLAlchemy-istunto edustaa työskentely-yksikköä, keskustelua tietokannan kanssa. Se seuraa objekteihin tehtyjä muutoksia, jolloin voit tallentaa ne tietokantaan yhtenä atomisena operaationa. Ajattele sitä työtilana, jossa teet muutoksia tietoihin ennen niiden virallista tallentamista. Ilman hyvin hallittua istuntoa vaarana ovat tietojen epäjohdonmukaisuudet ja mahdollinen korruptio.
Istunnon luominen
Ennen kuin voit aloittaa vuorovaikutuksen tietokantasi kanssa, sinun on luotava istunto. Tämä edellyttää ensin yhteyden muodostamista tietokantaan SQLAlchemy:n moottorin avulla.
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
# Database connection string
db_url = 'sqlite:///:memory:' # Replace with your database URL (e.g., PostgreSQL, MySQL)
# Create an engine
engine = create_engine(db_url, echo=False) # echo=True to see the generated SQL
# Define a base for declarative models
Base = declarative_base()
# Define a simple model
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
email = Column(String)
def __repr__(self):
return f"<User(name='{self.name}', email='{self.email}')>"
# Create the table in the database
Base.metadata.create_all(engine)
# Create a session class
Session = sessionmaker(bind=engine)
# Instantiate a session
session = Session()
Tässä esimerkissä:
- Tuomme tarvittavat SQLAlchemy-moduulit.
- Määritämme tietokannan yhteysmerkkijonon (`db_url`). Tämä esimerkki käyttää muistissa olevaa SQLite-tietokantaa yksinkertaisuuden vuoksi, mutta sinun tulisi korvata se tietokantajärjestelmääsi (esim. PostgreSQL, MySQL) sopivalla yhteysmerkkijonolla. Tarkka muoto vaihtelee käyttämäsi tietokantamoottorin ja ajurin mukaan. Katso oikea yhteysmerkkijonon muoto SQLAlchemy-dokumentaatiosta ja tietokantapalvelun tarjoajasi dokumentaatiosta.
- Luomme `engine`-objektin käyttämällä `create_engine()`-funktiota. Moottori vastaa yhteyspoolin hallinnasta ja viestinnästä tietokannan kanssa. `echo=True`-parametri voi olla hyödyllinen virheenkorjauksessa, sillä se tulostaa generoidut SQL-lauseet konsoliin.
- Määritämme perusluokan (`Base`) käyttämällä `declarative_base()`-funktiota. Tätä käytetään kaikkien SQLAlchemy-malliemme perusluokkana.
- Määritämme `User`-mallin, joka vastaa tietokantataulua nimeltä `users`.
- Luomme taulukon tietokantaan käyttämällä `Base.metadata.create_all(engine)`.
- Luomme istuntoluokan käyttämällä `sessionmaker(bind=engine)`. Tämä konfiguroi istuntoluokan käyttämään määritettyä moottoria.
- Lopuksi luomme istunnon käyttämällä `Session()`.
Transaktioiden ymmärtäminen
Transaktio on sarja tietokantaoperaatioita, joita käsitellään yhtenä loogisena työskentely-yksikkönä. Transaktiot noudattavat ACID-ominaisuuksia:
- Atomisuus: Kaikki transaktion operaatiot joko onnistuvat täysin tai epäonnistuvat täysin. Jos jokin osa transaktiosta epäonnistuu, koko transaktio peruuntuu.
- Johdonmukaisuus: Transaktion on pidettävä tietokanta kelvollisessa tilassa. Se ei saa rikkoa mitään tietokannan rajoituksia tai sääntöjä.
- Eristys: Samanaikaiset transaktiot on eristetty toisistaan. Yhden transaktion tekemät muutokset eivät ole näkyvissä muille transaktioille ennen kuin ensimmäinen transaktio on commitattu.
- Kestävyys: Kun transaktio on commitattu, sen muutokset ovat pysyviä ja säilyvät myös järjestelmävioista huolimatta.
SQLAlchemy tarjoaa mekanismit transaktioiden hallintaan varmistaen näiden ACID-ominaisuuksien ylläpidon.
Perustransaktioiden käsittely
Yleisimmät transaktio-operaatiot ovat commit ja rollback.
Transaktioiden commitointi
Kun kaikki transaktion sisällä olevat operaatiot on suoritettu onnistuneesti, commitoit transaktion. Tämä tallentaa muutokset tietokantaan pysyvästi.
try:
# Add a new user
new_user = User(name='Alice Smith', email='alice.smith@example.com')
session.add(new_user)
# Commit the transaction
session.commit()
print("Transaction committed successfully!")
except Exception as e:
# Handle exceptions
print(f"An error occurred: {e}")
session.rollback()
print("Transaction rolled back.")
finally:
session.close()
Tässä esimerkissä:
- Lisäämme uuden `User`-objektin istuntoon.
- Kutsumme `session.commit()`-metodia tallentaaksemme muutokset tietokantaan.
- Käärimme koodin `try...except...finally`-lohkoon käsitelläksemme mahdolliset poikkeukset.
- Jos poikkeus ilmenee, kutsumme `session.rollback()`-metodia peruuttaaksemme kaikki transaktion aikana tehdyt muutokset.
- Kutsumme aina `session.close()`-metodia `finally`-lohkossa vapauttaaksemme istunnon ja palauttaaksemme yhteyden yhteyspooliin. Tämä on ratkaisevan tärkeää resurssivuotojen välttämiseksi. Istuntojen sulkematta jättäminen voi johtaa yhteyksien loppumiseen ja sovelluksen epävakauteen.
Transaktioiden peruuttaminen
Jos transaktion aikana ilmenee virhe, tai jos päätät, ettei muutoksia tule tallentaa, peruutat transaktion. Tämä palauttaa tietokannan tilaan, jossa se oli ennen transaktion alkamista.
try:
# Add a user with an invalid email (example to force a rollback)
invalid_user = User(name='Bob Johnson', email='invalid-email')
session.add(invalid_user)
# The commit will fail if the email is not validated on the database level
session.commit()
print("Transaction committed.")
except Exception as e:
print(f"An error occurred: {e}")
session.rollback()
print("Transaction rolled back successfully.")
finally:
session.close()
Tässä esimerkissä, jos `invalid_user`-käyttäjän lisääminen aiheuttaa poikkeuksen (esim. tietokannan rajoitteen rikkomisen vuoksi), `session.rollback()`-kutsu kumoaa yrityksen lisätä käyttäjä, jättäen tietokannan muuttumattomaksi.
Edistyksellinen transaktioiden hallinta
`with`-lauseen käyttö transaktioiden rajaamiseen
Pythonisempi ja vankempi tapa hallita transaktioita on käyttää `with`-lausetta. Tämä varmistaa, että istunto suljetaan asianmukaisesti, vaikka poikkeuksia ilmenisi.
from contextlib import contextmanager
@contextmanager
def session_scope():
"""Provide a transactional scope around a series of operations."""
session = Session()
try:
yield session
session.commit()
except Exception:
session.rollback()
raise
finally:
session.close()
# Usage:
with session_scope() as session:
new_user = User(name='Charlie Brown', email='charlie.brown@example.com')
session.add(new_user)
# Operations within the 'with' block
# If no exceptions occur, the transaction is committed automatically.
# If an exception occurs, the transaction is rolled back automatically.
print("User added.")
print("Transaction completed (committed or rolled back).")
`session_scope`-funktio on kontekstihallitsija. Kun siirryt `with`-lohkoon, luodaan uusi istunto. Kun poistut `with`-lohkosta, istunto joko commitataan (jos poikkeuksia ei ilmennyt) tai perutaan (jos poikkeus ilmeni). Istunto suljetaan aina `finally`-lohkossa.
Sisäkkäiset transaktiot (Savepoints)
SQLAlchemy tukee sisäkkäisiä transaktioita käyttämällä savepointteja. Savepointin avulla voit peruuttaa tiettyyn pisteeseen suuremman transaktion sisällä vaikuttamatta koko transaktioon.
try:
with session_scope() as session:
user1 = User(name='David Lee', email='david.lee@example.com')
session.add(user1)
session.flush() # Send changes to the database but don't commit yet
# Create a savepoint
savepoint = session.begin_nested()
try:
user2 = User(name='Eve Wilson', email='eve.wilson@example.com')
session.add(user2)
session.flush()
# Simulate an error
raise ValueError("Simulated error during nested transaction")
except Exception as e:
print(f"Nested transaction error: {e}")
savepoint.rollback()
print("Nested transaction rolled back to savepoint.")
# Continue with the outer transaction, user1 will still be added
user3 = User(name='Frank Miller', email='frank.miller@example.com')
session.add(user3)
except Exception as e:
print(f"Outer transaction error: {e}")
#Commit will commit user1 and user3, but not user2 due to the nested rollback
try:
with session_scope() as session:
#Verify only user1 and user3 exist
users = session.query(User).all()
for user in users:
print(user)
except Exception as e:
print(f"Unexpected Exception: {e}") #Should not happen
Tässä esimerkissä:
- Aloitamme ulomman transaktion käyttämällä `session_scope()`-funktiota.
- Lisäämme `user1`-objektin istuntoon ja tallennamme muutokset tietokantaan. `flush()` lähettää muutokset tietokantapalvelimelle, mutta *ei* committaa niitä. Sen avulla voit tarkistaa, ovatko muutokset kelvollisia (esim. ei rajoitusrikkomuksia) ennen koko transaktion committaamista.
- Luomme savepointin käyttämällä `session.begin_nested()`-funktiota.
- Sisäkkäisessä transaktiossa lisäämme `user2`-objektin ja simuloimme virheen.
- Peruutamme sisäkkäisen transaktion savepointiin käyttämällä `savepoint.rollback()`-metodia. Tämä kumoaa vain sisäkkäisen transaktion aikana tehdyt muutokset (eli `user2`-objektin lisäyksen).
- Jatkamme ulomman transaktion kanssa ja lisäämme `user3`-objektin.
- Ulkoinen transaktio commitoidaan, tallentaen `user1` ja `user3` tietokantaan, kun taas `user2` hylätään savepointin peruuttamisen vuoksi.
Eristystasojen hallinta
Eristystasot määrittelevät, kuinka eristettyjä samanaikaiset transaktiot ovat toisistaan. Korkeammat eristystasot tarjoavat paremman tietojen johdonmukaisuuden, mutta voivat vähentää samanaikaisuutta ja suorituskykyä. SQLAlchemy antaa sinun hallita transaktioidesi eristystasoa.
Yleisiä eristystasoja ovat:
- Read Uncommitted: Alhaisin eristystaso. Transaktiot voivat nähdä muiden transaktioiden tekemät committaamattomat muutokset. Tämä voi johtaa likaisiin lukuihin.
- Read Committed: Transaktiot voivat nähdä vain muiden transaktioiden committaamat muutokset. Tämä estää likaiset luvut, mutta voi johtaa ei-toistettaviin lukuihin ja haamulukuihin.
- Repeatable Read: Transaktiot voivat nähdä saman tiedon koko transaktion ajan, vaikka muut transaktiot muokkaisivat sitä. Tämä estää likaiset luvut ja ei-toistettavat luvut, mutta voi johtaa haamulukuihin.
- Serializable: Korkein eristystaso. Transaktiot ovat täysin eristyksissä toisistaan. Tämä estää likaiset luvut, ei-toistettavat luvut ja haamuluvut, mutta voi merkittävästi vähentää samanaikaisuutta.
Oletuseristystaso riippuu tietokantajärjestelmästä. Voit asettaa eristystason luodessasi moottorin tai aloittaessasi transaktion.
Esimerkki (PostgreSQL):
from sqlalchemy.dialects.postgresql import dialect
# Set isolation level when creating the engine
engine = create_engine('postgresql://user:password@host:port/database',
connect_args={'options': '-c statement_timeout=1000'} #Example of timeout
)
# Set the isolation level when beginning a transaction (database specific)
# For postgresql, it's recommended to set it on the connection, not engine.
from sqlalchemy import event
from sqlalchemy.pool import Pool
@event.listens_for(Pool, "connect")
def set_isolation_level(dbapi_connection, connection_record):
existing_autocommit = dbapi_connection.autocommit
dbapi_connection.autocommit = True
cursor = dbapi_connection.cursor()
cursor.execute("SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE")
dbapi_connection.autocommit = existing_autocommit
cursor.close()
# Then transactions created via SQLAlchemy will use the configured isolation level.
Tärkeää: Eristystasojen asettamismenetelmä on tietokantakohtainen. Katso tietokantasi dokumentaatiosta oikea syntaksi. Eristystasojen virheellinen asettaminen voi johtaa odottamattomaan käyttäytymiseen tai virheisiin.
Samanaikaisuuden käsittely
Kun useat käyttäjät tai prosessit käyttävät samaa dataa samanaikaisesti, on ratkaisevan tärkeää käsitellä samanaikaisuutta oikein tietojen korruptoitumisen estämiseksi ja tietojen johdonmukaisuuden varmistamiseksi. SQLAlchemy tarjoaa useita mekanismeja samanaikaisuuden käsittelyyn, mukaan lukien optimistinen ja pessimistinen lukitus.
Optimistinen lukitus
Optimistinen lukitus olettaa, että ristiriitoja esiintyy harvoin. Se tarkistaa muiden transaktioiden tekemät muutokset ennen transaktion commitointia. Jos ristiriita havaitaan, transaktio perutaan.
Optimistisen lukituksen toteuttamiseksi lisätään tyypillisesti version sarakkeesi taulukkoon. Tämä sarake kasvattaa automaattisesti arvoaan aina, kun rivi päivitetään.
from sqlalchemy import Column, Integer, String, Integer
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class Article(Base):
__tablename__ = 'articles'
id = Column(Integer, primary_key=True)
title = Column(String)
content = Column(String)
version = Column(Integer, nullable=False, default=1)
def __repr__(self):
return f"<Article(title='{self.title}', version='{self.version}')>"
#Inside of the try catch block
def update_article(session, article_id, new_content):
article = session.query(Article).filter_by(id=article_id).first()
if article is None:
raise ValueError("Article not found")
original_version = article.version
# Update the content and increment the version
article.content = new_content
article.version += 1
# Attempt to update, checking the version column in the WHERE clause
rows_affected = session.query(Article).filter(
Article.id == article_id,
Article.version == original_version
).update({
Article.content: new_content,
Article.version: article.version
}, synchronize_session=False)
if rows_affected == 0:
session.rollback()
raise ValueError("Conflict: Article has been updated by another transaction.")
session.commit()
Tässä esimerkissä:
- Lisäämme `version`-sarakkeen `Article`-malliin.
- Ennen artikkelin päivittämistä tallennamme nykyisen versionumeron.
- `UPDATE`-lauseessa sisällytämme `WHERE`-lauseen, joka tarkistaa, onko versiosarake edelleen sama kuin tallennettu versio. `synchronize_session=False` estää SQLAlchemyä lataamasta päivitettyä objektia uudelleen; käsittelemme versiointia eksplisiittisesti.
- Jos versiosarake on muuttunut toisen transaktion toimesta, `UPDATE`-lause ei vaikuta mihinkään riveihin (rows_affected on 0), ja nostamme poikkeuksen.
- Peruutamme transaktion ja ilmoitamme käyttäjälle, että ristiriita on ilmennyt.
Pessimistinen lukitus
Pessimistinen lukitus olettaa, että ristiriitoja esiintyy todennäköisesti. Se hankkii lukon riville tai taulukolle ennen sen muokkaamista. Tämä estää muita transaktioita muokkaamasta tietoja, kunnes lukitus vapautetaan.
SQLAlchemy tarjoaa useita toimintoja lukitusten hankkimiseen, kuten `with_for_update()`.
# Example using PostgreSQL
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, declarative_base
# Database setup (replace with your actual database URL)
db_url = 'postgresql://user:password@host:port/database'
engine = create_engine(db_url, echo=False) #Set echo to true if you would like to see the SQL generated
Base = declarative_base()
class Item(Base):
__tablename__ = 'items'
id = Column(Integer, primary_key=True)
name = Column(String)
value = Column(Integer)
def __repr__(self):
return f"<Item(name='{self.name}', value='{self.value}')>"
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
#Function to update the item (within a try/except)
def update_item_value(session, item_id, new_value):
# Acquire a pessimistic lock on the item
item = session.query(Item).filter(Item.id == item_id).with_for_update().first()
if item is None:
raise ValueError("Item not found")
# Update the item's value
item.value = new_value
session.commit()
return True
Tässä esimerkissä:
- Käytämme `with_for_update()`-funktiota hankkiaksemme lukon `Item`-riville ennen sen päivittämistä. Tämä estää muita transaktioita muokkaamasta riviä, kunnes nykyinen transaktio on commitoitu tai peruutettu. `with_for_update()`-funktio on tietokantakohtainen; katso tietokantasi dokumentaatiosta lisätietoja. Joillakin tietokannoilla voi olla erilaisia lukitusmekanismeja tai syntakseja.
Tärkeää: Pessimistinen lukitus voi heikentää samanaikaisuutta ja suorituskykyä, joten käytä sitä vain tarvittaessa.
Parhaat käytännöt poikkeusten käsittelyyn
Asianmukainen poikkeusten käsittely on ratkaisevan tärkeää tietojen eheyden varmistamiseksi ja sovelluksen kaatumisten estämiseksi. Kääri tietokantaoperaatiosi aina `try...except`-lohkoihin ja käsittele poikkeukset asianmukaisesti.
Tässä muutamia parhaita käytäntöjä poikkeusten käsittelyyn:
- Sieppaa tietyt poikkeukset: Vältä geneeristen poikkeusten, kuten `Exception`, sieppaamista. Sieppaa tietyt poikkeukset, kuten `sqlalchemy.exc.IntegrityError` tai `sqlalchemy.exc.OperationalError`, käsitelläksesi erityyppisiä virheitä eri tavoin.
- Peruuta transaktiot: Peruuta aina transaktio, jos poikkeus ilmenee.
- Kirjaa poikkeukset: Kirjaa poikkeukset auttaaksesi ongelmien diagnosoinnissa ja korjaamisessa. Sisällytä lokiisi mahdollisimman paljon kontekstia (esim. käyttäjätunnus, syöttötiedot, aikaleima).
- Nosta poikkeukset uudelleen tarvittaessa: Jos et voi käsitellä poikkeusta, nosta se uudelleen, jotta ylemmän tason käsittelijä voi käsitellä sen.
- Siivoa resurssit: Sulje aina istunto ja vapauta kaikki muut resurssit `finally`-lohkossa.
import logging
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, declarative_base
from sqlalchemy.exc import IntegrityError, OperationalError
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Database setup (replace with your actual database URL)
db_url = 'postgresql://user:password@host:port/database'
engine = create_engine(db_url, echo=False)
Base = declarative_base()
class Product(Base):
__tablename__ = 'products'
id = Column(Integer, primary_key=True)
name = Column(String)
price = Column(Integer)
def __repr__(self):
return f"<Product(name='{self.name}', price='{self.price}')>"
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
# Function to add a product
def add_product(session, name, price):
try:
new_product = Product(name=name, price=price)
session.add(new_product)
session.commit()
logging.info(f"Product '{name}' added successfully.")
return True
except IntegrityError as e:
session.rollback()
logging.error(f"IntegrityError: {e}")
#Handle database constraint violations (e.g., duplicate name)
return False
except OperationalError as e:
session.rollback()
logging.error(f"OperationalError: {e}")
#Handle connection errors or other operational issues
return False
except Exception as e:
session.rollback()
logging.exception(f"An unexpected error occurred: {e}")
# Handle any other unexpected errors
return False
finally:
session.close()
Tässä esimerkissä:
- Määritämme lokituksen tallentamaan tapahtumia prosessin aikana.
- Sieppaamme tietyt poikkeukset, kuten `IntegrityError` (rajoitusrikkomukset) ja `OperationalError` (yhteysvirheet).
- Peruutamme transaktion `except`-lohkoissa.
- Kirjaamme poikkeukset käyttämällä `logging`-moduulia. `logging.exception()`-metodi sisällyttää automaattisesti kutsupinon lokiviestiin.
- Nostamme poikkeuksen uudelleen, jos emme voi käsitellä sitä.
- Suljemme istunnon `finally`-lohkossa.
Tietokannan yhteyspooli
SQLAlchemy käyttää yhteyspoolia hallitsemaan tietokantayhteyksiä tehokkaasti. Yhteyspooli ylläpitää joukkoa avoimia yhteyksiä tietokantaan, jolloin sovellukset voivat käyttää olemassa olevia yhteyksiä uudelleen sen sijaan, että loivat uusia jokaiseen pyyntöön. Tämä voi parantaa merkittävästi suorituskykyä, erityisesti sovelluksissa, jotka käsittelevät suuren määrän samanaikaisia pyyntöjä.
SQLAlchemy:n `create_engine()`-funktio luo automaattisesti yhteyspoolin. Voit määrittää yhteyspoolin välittämällä argumentteja `create_engine()`-funktiolle.
Yleisiä yhteyspoolin parametreja ovat:
- pool_size: Yhteyksien enimmäismäärä poolissa.
- max_overflow: Yhteyksien määrä, joka voidaan luoda `pool_size`-arvon ylitse.
- pool_recycle: Sekuntien määrä, jonka jälkeen yhteys kierrätetään.
- pool_timeout: Sekuntien määrä, jonka odotetaan, että yhteys tulee saataville.
engine = create_engine('postgresql://user:password@host:port/database',
pool_size=5, #Maximum pool size
max_overflow=10, #Maximum overflow
pool_recycle=3600, #Recycle connections after 1 hour
pool_timeout=30
)
Tärkeää: Valitse sovelluksesi tarpeisiin ja tietokantapalvelimesi ominaisuuksiin perustuvat sopivat yhteyspoolin asetukset. Huonosti määritetty yhteyspooli voi johtaa suorituskykyongelmiin tai yhteyksien loppumiseen.
Asynkroniset transaktiot (Async SQLAlchemy)
Nykyaikaisille sovelluksille, jotka vaativat suurta samanaikaisuutta, erityisesti niille, jotka on rakennettu asynkronisilla viitekehyksillä kuten FastAPI tai AsyncIO, SQLAlchemy tarjoaa asynkronisen version nimeltä Async SQLAlchemy.
Async SQLAlchemy tarjoaa asynkroniset versiot SQLAlchemy:n ydinkomponenteista, jolloin voit suorittaa tietokantaoperaatioita estämättä tapahtumasilmukkaa. Tämä voi parantaa merkittävästi sovellustesi suorituskykyä ja skaalautuvuutta.
Tässä on perusesimerkki Async SQLAlchemy:n käytöstä:
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, Integer, String
import asyncio
# Database setup (replace with your actual database URL)
db_url = 'postgresql+asyncpg://user:password@host:port/database'
engine = create_async_engine(db_url, echo=False)
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
email = Column(String)
def __repr__(self):
return f"<User(name='{self.name}', email='{self.email}')>"
async def create_db_and_tables():
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async def add_user(name, email):
async with AsyncSession(engine) as session:
new_user = User(name=name, email=email)
session.add(new_user)
await session.commit()
async def main():
await create_db_and_tables()
await add_user("Async User", "async.user@example.com")
if __name__ == "__main__":
asyncio.run(main())
Tärkeimmät erot synkronisesta SQLAlchemy:stä:
- `create_async_engine`-funktiota käytetään `create_engine`-funktion sijaan.
- `AsyncSession`-luokkaa käytetään `Session`-luokan sijaan.
- Kaikki tietokantaoperaatiot ovat asynkronisia ja ne on odotettava käyttämällä `await`-avainsanaa.
- Asynkronisia tietokanta-ajureita (esim. `asyncpg` PostgreSQL:lle) on käytettävä.
Tärkeää: Async SQLAlchemy vaatii tietokanta-ajurin, joka tukee asynkronisia operaatioita. Varmista, että sinulla on oikea ajuri asennettuna ja konfiguroituna.
Yhteenveto
SQLAlchemy-istuntojen ja transaktioiden hallinnan hallitseminen on olennaista vankkojen ja luotettavien Python-sovellusten rakentamisessa, jotka ovat vuorovaikutuksessa tietokantojen kanssa. Ymmärtämällä istuntojen, transaktioiden, eristystasojen ja samanaikaisuuden käsitteet sekä noudattamalla parhaita käytäntöjä poikkeusten käsittelyssä ja yhteyspoolien käytössä voit varmistaa tietojen eheyden ja optimoida sovellustesi suorituskyvyn.
Rakennatpa sitten pientä verkkosovellusta tai laajamittaista yritysjärjestelmää, SQLAlchemy tarjoaa työkalut, joita tarvitset tietokantavuorovaikutusten tehokkaaseen hallintaan. Muista aina priorisoida tietojen eheys ja käsitellä mahdolliset virheet sujuvasti varmistaaksesi sovellustesi luotettavuuden.
Harkitse seuraavien edistyneiden aiheiden tutkimista:
- Kaksivaiheinen commit (2PC): Transaktioille, jotka ulottuvat useisiin tietokantoihin.
- Sharding: Tietojen jakamiseen useille tietokantapalvelimille.
- Tietokantamigraatiot: Työkalujen, kuten Alembicin, käyttö tietokannan skeemamuutosten hallintaan.