sqlalchemy session


What does the Session do?

1 session创建和管理数据库连接的会话 2 model object 通过session对象访问数据库,并把访问到的数据以 Identity Map的方式,映射到Model object中

A typical lifespan of a Session

理解: 1 session在刚被创建的时候,还没有和任何model object 绑定,可认为是无状态的 2 session 接受到query查询语句, 执行的结果或保持或者关联到session中 3 任意数量的model object被创建,并绑定到session中,session会管理这些对象 4 一旦session 里面的objects 有变化,那可是要commit/rollback提交或者放弃changs

how to use Session

from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine

# configure Session class with desired options
Session = sessionmaker()

# later, we create the engine
engine = create_engine('postgresql://...')

# associate it with our custom Session class
Session.configure(bind=engine)

# work with the session
session = Session()

如果需要改变关联的engine, 比如数据库是主从设计,某些时候我们需要从主数据库,我们可能需要切换不同的数据库

# at the module level, the global sessionmaker,
# bound to a specific Engine
Session = sessionmaker(bind=engine)

# later, some unit of code wants to create a
# Session that is bound to a specific Connection
conn = engine.connect()
session = Session(bind=conn)

When do I construct a Session, when do I commit it, and when do I close it?

一般来说,session在需要访问数据库的时候创建,在session访问数据库的时候,准确来说,应该是“add/update/delete”数据库的时候,会开启database transaction, 假设没有修改autocommit的默认值(False), 那么,database transaction 一直会保持,只有等到session rolled back, committed, or closed的时候才结束,一般建议,当database transaction结束的时候,同时close session, 保证,每次发起请求,都创建一个新的session

特别是对web应用来说,发起一个请求,若请求使用到Session访问数据库,则创建session,处理完这个请求后,关闭session,如图:

Web Server          Web Framework        SQLAlchemy ORM Code
--------------      --------------       ------------------------------
startup        ->   Web framework        # Session registry is established
                    initializes          Session = scoped_session(sessionmaker())

incoming
web request    ->   web request     ->   # The registry is *optionally*
                    starts               # called upon explicitly to create
                                         # a Session local to the thread and/or request
                                         Session()

                                         # the Session registry can otherwise
                                         # be used at any time, creating the
                                         # request-local Session() if not present,
                                         # or returning the existing one
                                         Session.query(MyClass) # ...

                                         Session.add(some_object) # ...

                                         # if data was modified, commit the
                                         # transaction
                                         Session.commit()

                    web request ends  -> # the registry is instructed to
                                         # remove the Session
                                         Session.remove()

                    sends output      <-
outgoing web    <-
response

还有一点需要注意的是,保证session是一个全局的对象,所以和数据库通信的session在任何时候只有一个,为毛,因为我们只需要一个session对象,同时,管理一个session对象远比管理两个对象简单

不要这么做:

class ThingOne(object):
    def go(self):
        session = Session()
        try:
            session.query(FooBar).update({"x": 5})
            session.commit()
        except:
            session.rollback()
            raise

class ThingTwo(object):
    def go(self):
        session = Session()
        try:
            session.query(Widget).update({"q": 18})
            session.commit()
        except:
            session.rollback()
            raise

def run_my_program():
    ThingOne().go()
    ThingTwo().go()

推荐这么做:

class ThingOne(object):
    def go(self, session):
        session.query(FooBar).update({"x": 5})

class ThingTwo(object):
    def go(self, session):
        session.query(Widget).update({"q": 18})

def run_my_program():
    session = Session()
    try:
        ThingOne().go(session)
        ThingTwo().go(session)

        session.commit()
    except:
        session.rollback()
        raise
    finally:
        session.close()

Is the Session a cache

session 没有任何缓存

Is the session thread-safe

session 不是线程安全的,上面提到,我们需要保证session object是全局的,在多线程的环境中,默认情况下,多个线程将会共享同一个session, 试想一下,假设A线程正在使用session处理数据库,B线程已经执行完成,把session给close了,那么此时A在使用session就会报错,怎么避免这个问题

必须保证每个线程使用的session都不一样

Thread-local Sessions

from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import sessionmaker

session_factory = sessionmaker(bind=some_engine)
Session = scoped_session(session_factory)
some_session = Session()
some_other_session = Session()
some_session is some_other_session #True

使用了scoped_session 默认情况下,创建的session都是Thread-Local Scope,创建的session对象具体有两点变化: 1 使用Session()创建的session对象都是一样的,这可以保证代码在不同的多次调用session()依然获得到相同的session 对象 2 使用Session()创建的session对象 是 Thread-local, session在线程与线程之间没有任何联系

sqlalchemy怎么做的,猛击 thrending.local()

how to close session

Session.remove()

# This will first call Session.close() method on the current Session, which releases any existing transactional/connection resources still being held; transactions specifically are rolled back. The Session is then discarded. Upon next usage within the same scope, the scoped_session will produce a new Session object

# 注意,未commit的transactions会被回滚

那么session.close会做什么事情呢,文章开头说道,session创建和管理对数据库的连接,当调用close的时候,注意,sqlalchemy不会关闭与mysql的连接,而是把连接返回到连接池。

model object关联到session的状态

Session Object States

Since we have already seen an Session object in action, it's important to also know the four different states of session objects:

Transient: an instance that's not included in a session and has not been persisted to the database. Pending: an instance that has been added to a session but not persisted to a database yet. It will be persisted to the database in the next session.commit(). Persistent: an instance that has been persisted to the database and also included in a session. You can make a model object persistent by committing it to the database or query it from the database. Detached: an instance that has been persisted to the database but not included in any sessions.

>>> from sqlalchemy import inspect
>>> david = User(name='David')
>>> ins = inspect(david)
>>> print('Transient: {0}; Pending: {1}; Persistent: {2}; Detached: {3}'.format(ins.transient, ins.pending, ins.persistent, ins.detached))
Transient: True; Pending: False; Persistent: False; Detached: False
>>> s.add(david)
>>> print('Transient: {0}; Pending: {1}; Persistent: {2}; Detached: {3}'.format(ins.transient, ins.pending, ins.persistent, ins.detached))
Transient: False; Pending: True; Persistent: False; Detached: False
>>> s.commit()
>>> print('Transient: {0}; Pending: {1}; Persistent: {2}; Detached: {3}'.format(ins.transient, ins.pending, ins.persistent, ins.detached))
Transient: False; Pending: False; Persistent: True; Detached: False
>>> s.close()
>>> print('Transient: {0}; Pending: {1}; Persistent: {2}; Detached: {3}'.format(ins.transient, ins.pending, ins.persistent, ins.detached))
Transient: False; Pending: False; Persistent: False; Detached: True

results matching ""

    No results matching ""