【问题标题】:Python Database connection ClosePython 数据库连接 关闭
【发布时间】:2010-09-23 23:35:49
【问题描述】:

使用下面的代码可以打开连接,我该如何关闭?

import pyodbc
conn = pyodbc.connect('DRIVER=MySQL ODBC 5.1 driver;SERVER=localhost;DATABASE=spt;UID=who;PWD=testest') 

csr = conn.cursor()  
csr.close()
del csr

【问题讨论】:

    标签: python database-connection


    【解决方案1】:

    连接具有 PE​​P-249(Python 数据库 API 规范 v2.0)中指定的 close 方法:

    import pyodbc
    conn = pyodbc.connect('DRIVER=MySQL ODBC 5.1 driver;SERVER=localhost;DATABASE=spt;UID=who;PWD=testest') 
    
    csr = conn.cursor()  
    csr.close()
    conn.close()     #<--- Close the connection
    

    由于 pyodbc connectioncursor 都是上下文管理器,现在将其写成这样会更方便(也更可取):

    import pyodbc
    conn = pyodbc.connect('DRIVER=MySQL ODBC 5.1 driver;SERVER=localhost;DATABASE=spt;UID=who;PWD=testest') 
    with conn:
        crs = conn.cursor()
        do_stuff
        # conn.commit() will automatically be called when Python leaves the outer `with` statement
        # Neither crs.close() nor conn.close() will be called upon leaving the `with` statement!! 
    

    请参阅https://github.com/mkleehammer/pyodbc/issues/43,了解为什么不调用 conn.close()。

    请注意,与原始代码不同,这会导致调用conn.commit()。使用外部 with 语句来控制您希望何时调用 commit


    另请注意,无论您是否使用with 语句,根据the docs

    连接在被删除时会自动关闭(通常是在超出范围时),因此您通常不需要调用 [conn.close()],但您可以根据需要显式关闭连接。

    同样for cursors(我的重点):

    游标在被删除时会自动关闭(通常是在超出范围时),因此通常不需要调用 [csr.close()]

    【讨论】:

    • 这是最佳实践吗?要手动关闭光标,然后删除它,然后关闭连接?
    • 我也想知道@DustinMichels 问题的答案。
    • Per the docs, “连接在被删除时会自动关闭(通常是当它们超出范围时),因此您通常不需要调用它,但如果您可以显式关闭连接希望”。还有similarly for cursors
    • @TuanDT: del 这里不需要。它确实关闭了游标,但通常没有必要,因为当csr 超出范围时也会自动发生同样的情况(而且这通常很快)。仅当您想从命名空间中删除 csr 和/或减少对对象的引用时才使用它。当我第一次发布这个时,我保留了 del 因为它不一定是错误的,我认为 OP 理解它的目的。但由于它似乎会造成混乱并且不需要关闭连接,我现在将其删除。
    • pyodbc 对象在与上下文管理器一起使用时不会自动关闭!正如您链接到的文档所指出的那样,使用上下文管理器语法等同于提交但不关闭连接或游标。见github.com/mkleehammer/pyodbc/issues/43
    【解决方案2】:

    您可以如下定义数据库类。此外,正如andrewf 建议的那样,使用上下文管理器进行游标访问。我将其定义为成员函数。 这样,它可以在应用程序代码的多个事务中保持连接打开,并节省到服务器的不必要的重新连接。

    import pyodbc
    
    class MS_DB():
        """ Collection of helper methods to query the MS SQL Server database.
        """
    
        def __init__(self, username, password, host, port=1433, initial_db='dev_db'):
            self.username = username
            self._password = password
            self.host = host
            self.port = str(port)
            self.db = initial_db
            conn_str = 'DRIVER=DRIVER=ODBC Driver 13 for SQL Server;SERVER='+ \
                        self.host + ';PORT='+ self.port +';DATABASE='+ \
                        self.db +';UID='+ self.username +';PWD='+ \ 
                        self._password +';'
            print('Connected to DB:', conn_str)
            self._connection = pyodbc.connect(conn_str)        
            pyodbc.pooling = False
    
        def __repr__(self):
            return f"MS-SQLServer('{self.username}', <password hidden>, '{self.host}', '{self.port}', '{self.db}')"
    
        def __str__(self):
            return f"MS-SQLServer Module for STP on {self.host}"
    
        def __del__(self):
            self._connection.close()
            print("Connection closed.")
    
        @contextmanager
        def cursor(self, commit: bool = False):
            """
            A context manager style of using a DB cursor for database operations. 
            This function should be used for any database queries or operations that 
            need to be done. 
    
            :param commit:
            A boolean value that says whether to commit any database changes to the database. Defaults to False.
            :type commit: bool
            """
            cursor = self._connection.cursor()
            try:
                yield cursor
            except pyodbc.DatabaseError as err:
                print("DatabaseError {} ".format(err))
                cursor.rollback()
                raise err
            else:
                if commit:
                    cursor.commit()
            finally:
                cursor.close()
    
    ms_db = MS_DB(username='my_user', password='my_secret', host='hostname')
    with ms_db.cursor() as cursor:
            cursor.execute("SELECT @@version;")
            print(cur.fetchall())
    

    【讨论】:

    • 我认为最好的解决方案是在这个链接https://stackoverflow.com/a/38078544/12274302
    【解决方案3】:

    您可以将整个连接包装在上下文管理器中,如下所示:

    from contextlib import contextmanager
    import pyodbc
    import sys
    
    @contextmanager
    def open_db_connection(connection_string, commit=False):
        connection = pyodbc.connect(connection_string)
        cursor = connection.cursor()
        try:
            yield cursor
        except pyodbc.DatabaseError as err:
            error, = err.args
            sys.stderr.write(error.message)
            cursor.execute("ROLLBACK")
            raise err
        else:
            if commit:
                cursor.execute("COMMIT")
            else:
                cursor.execute("ROLLBACK")
        finally:
            connection.close()
    

    然后在需要数据库连接的地方执行类似的操作:

    with open_db_connection("...") as cursor:
        # Your code here
    

    当您离开 with 块时,连接将关闭。如果发生异常或您没有使用with open_db_connection("...", commit=True) 打开块,这也会回滚事务。

    【讨论】:

    • 好主意,但我用的是 Mysql 和 Sqlite....不是 oracle(也不是直接:-)!)连接字符串到驱动程序的地方...为什么要导入 sys?
    • 哎呀,我以为我用 pyodbc 替换了我的 oracle 特定代码,但我忽略了一个(现已修复)。语法是相同的,因为两者都使用通用的 PEP 249 数据库 API。我导入了 sys 所以我可以写任何标准错误的异常。您还可以使用日志记录或仅使用普通打印语句。然后将之前使用的相同连接字符串传递给 open_db_connection()。
    • 好的,看看代码:我做了什么?似乎要检查连接是否打开了很多额外的代码行?
    • 这正是我一直在寻找的,我已经成功地使用了 MySQLdb,但是你必须将 import contextlib 更改为 from contextlib import contextmanager 或将你的装饰器更改为 @contextlib.contextmanager。不管怎样,我猜,但我更喜欢前者。
    • 很好的方法,我一直在寻找这样的东西。但是我怎么把它放在一个班级里呢?我的代码目前将 pyodbc 连接作为类的字段之一:self.db = pyodbc.connect(path) 现在,当我想将其覆盖到您的:self.db = open_db_connection("...") 或以这种方式的任何内容时,我的连接在初始化后立即关闭@987654329 @ - (我想在运行时连接。
    【解决方案4】:

    根据 pyodbc 文档,与 SQL 服务器的连接 are not closed by default. 一些数据库驱动程序在调用 close() 时不会关闭连接以节省到服务器的往返行程。

    要在调用 close() 时关闭连接,您应该将 pooling 设置为 False:

    import pyodbc
    
    pyodbc.pooling = False
    

    【讨论】:

    • 当你说“调用close()”时,你的意思是cursor.close()吗?我假设是这样,因为 connection.close() 将避免将池设置为 false 的需要。
    【解决方案5】:

    您可以尝试关闭默认启用的池化。有关更多信息,请参阅this 讨论。

    import pyodbc
    pyodbc.pooling = False
    conn = pyodbc.connect('DRIVER=MySQL ODBC 5.1 driver;SERVER=localhost;DATABASE=spt;UID=who;PWD=testest') 
    
    csr = conn.cursor()  
    csr.close()
    del csr
    

    【讨论】:

    • 有趣的是,我可以使用池化,但 mysql 只是使用新 ID 启动另一个 conn。
    【解决方案6】:

    处理连接的最常用方法,如果该语言没有像 .NET 中的 Using 这样的自关闭结构,那么您应该使用 try -> finally 来关闭对象。 pyodbc 可能确实有某种形式的自动关闭,但这是我做的代码以防万一:

    conn = cursor = None
    try:
        conn = pyodbc.connect('DRIVER=MySQL ODBC 5.1 driver;SERVER=localhost;DATABASE=spt;UID=who;PWD=testest') 
    
        cursor = conn.cursor()  
    
        # ... do stuff ...
    
    finally:
        try: cursor.close()
        except: pass
        try: conn.close()
        except: pass
    

    【讨论】:

      猜你喜欢
      • 2011-10-19
      • 2013-10-16
      • 1970-01-01
      • 1970-01-01
      • 2017-07-18
      • 2011-12-29
      • 2016-07-22
      • 1970-01-01
      相关资源
      最近更新 更多