建立连接

  • 时区设定东8区 time_zone='+8:00'
  • 字符集默认为 UTF8(或者加上 charset="utf8"
  • autocommit 默认为 True,启动自动提交(重要)
1
2
3
import torndb

db = torndb.Connection('127.0.0.1:3306', 'database', 'user', 'password', time_zone='+8:00')

查询

查询是个 独立的事务

  • 单个查询 get
    • 得到单行记录,一个字典
  • 批量查询 query
    • 得到多行记录,单行为字典
1
2
3
4
5
sql = '''SELECT * FROM user WHERE id = %s'''
info = db.get(sql, 1)

sql = '''SELECT * FROM user WHERE id > %s'''
rows = db.query(sql, 1)

更改

单个更改语句也是个 独立的事务,不需要显式启用事务,autocommit 会自动提交

  • 修改 update
  • 插入 insert
  • 删除 delete
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
sql = '''UPDATE user SET `status` = %s WHERE id = %s'''
# affected_row_count: 修改到的记录数量
affected_row_count = db.update(sql, 0, 1)

sql = '''INSERT INTO user (name, status) VALUES (%s, %s)'''
args = ["小明", 0]
# last_id: 插入后的记录 ID
last_id = db.insert(sql, *args)

sql = '''DELETE FROM user WHERE id = %s'''
# affected_row_count: 删除的记录数量
affected_row_count = db.execute_rowcount(sql, 1)

事务处理

多条 SQL 语句执行,为了避免并发操作带来的数据安全问题,需要显式启用事务。

  • begin 开启事务
  • rollback 回滚事务
  • commit 提交事务
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 强制设定 autocommit=1
db._db.autocommit(True)

def do_transacion():
    # begin 开启事务, 不受 autocommit 的影响
    db._db.begin()
    try:
        sql = ''' SELECT `status` FROM user WHERE id = %s FOR UPDATE '''
        info = db.get(sql, 1)
        if not info:
            raise Exception(u"记录不存在")
        sql = ''' UPDATE user SET `status`=%s WHERE id=%s '''
        db.update(sql, 1, 1)

        db._db.commit()
    except Exception as e:
        db._db.rollback()
        print(str(e))
        return False
    return True

# 事务是否执行成功?
is_success = do_transacion()

autocoomit 的说明

MySQL 默认操作模式就是 autocommit=1 自动提交模式。这就表示除非显式地开始一个事务,否则每个查询都被当做一个单独的事务自动执行。

  • 如果 autocommit=1,MySQL 会在收到 SQL 请求时立刻提交,单条 DML 语句(select、update、insert、delete)就是一个事务
  • 如果 autocommit=0,MySQL 需要等待 client 提交 commit 语句,才提交待执行的 SQL 语句。
  • autocommit 不影响 begin 语句开启的事务,必须等待 client 提交 rollback/commit 语句,主动触发提交。

以上论述都是针对默认情况下,MySQL autocommit=1 的数据库操作方式,建议在 torndb 中将 autocommit 设置为 1, 有些场景或者项目里,autocommit=0,这个时候就要特别小心了,没有 commit 之前的所有 SQL 都会处在一个事务中!

查询

查询操作不是独立的事务,和其他在途的 SQL 处于同一个事务内,依赖 更改 操作 commit, 所以使用方式同上

更改

更改操作需要主动执行 commit 才会提交生效,所以无法获得 affected_row_count 和 last_id

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 强制设定 autocommit=0
db._db.autocommit(False)

sql = '''UPDATE user SET `status` = %s WHERE id = %s'''
# affected_row_count: 无法获取
affected_row_count = db.update(sql, 0, 1)
db._db.commit()

sql = '''INSERT INTO user (name, status) VALUES (%s, %s)'''
args = ["小明", 0]
# last_id: 插入后的记录 ID
last_id = db.insert(sql, *args)
db._db.commit()

sql = '''DELETE FROM user WHERE id = %s'''
# affected_row_count: 删除的记录数量
affected_row_count = db.execute_rowcount(sql, 1)
db._db.commit()

事务处理

如果 autocommit=0,数据库没有开启自动提交,事务处理有两种方式,差不多。

  • 方式一:begin 开启事务,不受 autocommit 的影响,同上
  • 方式二:autocommit=0 本身就处于事务中,可以省略 begin
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 强制设定 autocommit=0
db._db.autocommit(False)

def do_transacion():
    # begin 可以省略掉
    # db._db.begin()
    try:
        sql = ''' SELECT `status` FROM user WHERE id = %s FOR UPDATE '''
        info = db.get(sql, 1)
        if not info:
            raise Exception(u"记录不存在")
        sql = ''' UPDATE user SET `status`=%s WHERE id=%s '''
        db.update(sql, 1, 1)

        db._db.commit()
    except Exception as e:
        db._db.rollback()
        print(str(e))
        return False
    return True

# 事务是否执行成功?
is_success = do_transacion()

特别注意:同一个事务内的 SQL 操作,一定要使用同一个 MySQL 连接!

很多人意识不到这个问题, 在使用连接池的时候特别容易导致 MySQL 事务跨连接,造成未知的后果。