SQLAlchemy 嵌套事务的解决方案

 sqlachemy 是python的orm框架,在使用一段时间后,我们通常会出现事务嵌套的情况,看到很多人写代码的时候,居然是session到处传递,这无疑是加大了代码之间的耦合度。
    案例:
    def save(session):
        # TODO

    def update(session):
        # TODO

    def service():
        session = getSession();
        try:
            save(session);
            update(session);
            session.commit();
        except Exception as e:
            session.rollback();
        finally:
            if not session:
                session.close();

    假设save和update是同一个事务,但是上述的实践缺强制了save和update的 session相耦合来达成,好的实践应该是:save和update无任何关系,只是在实现业务逻辑时,组合到一个事务,确保事务性即可,即:

    def service():
        try:
            save();
            update();
        except Exception as e:
            # TODO 
    因此如何解决这个问题是本文需要阐述的主题。

解决方案是:

ACID是事务的四个基本特征,通常我们的理解事务是一个操作单元,要么一起成功要么一起失败(原子性);通过一个例子来直接说明如何解决的。
    场景:注册用户(添加一个用户,会未用户送10个积分)

创建表

create table user(
        id int not null auto_increment,
        name varchar(255) not null default '' comment '用户名',
        created_at datetime not null default current_timestamp comment '创建时间',
        updated_at datetime not null default current_timestamp comment '更新时间',
        primary key (`id`)
    )engine innodb charset=utf8 comment '用户表';  

    create table user_credits(
        id int not null auto_increment,
        user_id int not null default 0 comment '用户ID',
        user_name varchar(255) not null default '' comment '用户名',
        score int not null default 0 comment '积分',
        created_at datetime not null default current_timestamp comment '创建时间',
        updated_at datetime not null default current_timestamp comment '更新时间',
        primary key (`id`),
        unique key `uk_user_id` (`user_id`)
    )engine innodb charset=utf8 comment '用户积分表';

sqlalchemy 实现

# -*- coding:utf-8 -*-

import datetime
from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, scoped_session

engine = create_engine("mysql+pymysql://root:root@localhost:3306/csdn", echo=True)

# 必须使用scoped_session,域session可以将session进行共享
DBSession = scoped_session(sessionmaker(bind=engine))

BaseModel = declarative_base()


# ----------- Relation Model Object---------------- #

class User(BaseModel):

    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    name = Column(String)
    created_at = Column(DateTime, default=datetime.datetime.now)
    updated_at = Column(DateTime, default=datetime.datetime.now)


class UserCredits(BaseModel):

    __tablename__ = "user_credits"

    id = Column(Integer, primary_key=True)
    user_id = Column(Integer)
    user_name = Column(String)
    score = Column(Integer)
    created_at = Column(DateTime, default=datetime.datetime.now)
    updated_at = Column(DateTime, default=datetime.datetime.now)

# ----------- Service implements---------------- #


def add_user(user):
    " 添加用户 "
    session = DBSession()
    try:
        session.add(user)
        session.commit()
    except Exception as e:
        session.rollback()
        print("AddUser: ======={}=======".format(e))
    finally:
        if not session:
            session.close()


def add_user_credits(userCredits, interrupt=True):
    " 添加用户积分记录 "
    session = DBSession()
    try:
        if interrupt:
            raise Exception("--- interrupt ---")

        session.add(userCredits)
        session.commit()
    except Exception as e:
        session.rollback()
        print("AddUserCredits: ======={}=======".format(e))
    finally:
        if not session:
            session.close()


def regist_user():

    session = DBSession()
    try:
        # 开启子事务
        session.begin(subtransactions=True)

        # TODO Service
        user = User(name='wangzhiping')
        add_user(user)
        add_user_credits(UserCredits(
            user_id=user.id,
            user_name=user.name,
            score=10
        ), False)

        session.commit()
    except Exception as e:
        session.rollback()
        print("AddUserCredits: ======={}=======".format(e))
    finally:
        if not session:
            session.close()

# ---------- exec -----------
regist_user()
 1,设置session时,需要指定为scoped_session,目的是session可以共享(ThreadLocal);
    2,session.begin(subtransactions=True) 开启子事务管理;
    这是实际上regist_user是在同一个线程中的session,这是add_user,add_user_credits实际上session是同一个,所以可以实现。其实这个可以更进一步扩展,把事务隔离级别,传播属性,这里不做介绍

---------------------
作者:紫守笨 
来源:优快云 
原文:https://blog.youkuaiyun.com/program_red/article/details/55194130?utm_source=copy 
版权声明:本文为博主原创文章,转载请附上博文链接!

转载:https://blog.youkuaiyun.com/program_red/article/details/55194130

转载于:https://www.cnblogs.com/1a2a/p/9766548.html

### MongoDB与MySQL的功能差异 MongoDB是一个开源、模式自由、面向文档的数据库,采用C++编写[^1]。其设计目标在于提供高扩展性和高性能的数据存储解决方案。相比之下,MySQL是一种关系型数据库管理系统(RDBMS),支持结构化查询语言(SQL)[^2]。 #### 功能对比 - **数据模型** MongoDB使用 BSON 文档作为主要数据格式,允许嵌套对象和数组,适合处理半结构化或非结构化的数据[^5]。而 MySQL 基于表和行的关系模型,适用于严格定义的表格形式数据。 - **事务支持** 虽然传统 NoSQL 数据库缺乏 ACID 特性,但 MongoDB 已经在其基础上引入了原子性和事务支持[^4]。然而,MySQL 自诞生以来就完全支持 ACID 的四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation) 和持久性(Durability)。 - **索引机制** 两者都提供了强大的索引能力来加速查询操作。不过,在 MongoDB 中可以创建多种类型的索引(如文本索引、地理空间索引),这使得它非常适合某些特定领域应用,比如地理位置服务。 - **复制与分片** MongoDB 提供内置的自动分片功能用于水平扩展大规模分布式系统,并通过副本集实现高可用性配置[^3];而在 MySQL 方面,则依赖主从复制或者 Galera Cluster 等第三方插件完成类似的多节点部署架构。 ### 使用场景分析 | 场景描述 | 推荐使用的数据库 | | --- | --- | | 复杂报表生成、联接查询频繁的情况 | MySQL / PostgreSQL | | 实时数据分析平台构建 | MongoDB | | 高并发读写环境下的缓存层搭建 | Redis 或者 Memcached 结合 MySQL/MongoDB | | 日志收集与管理 | Elasticsearch + Logstash + Kibana (ELK Stack); 可能也会考虑 MongoDB | 对于需要灵活 schema 设计的应用程序来说,例如社交媒体网站上的用户资料更新频率较高且字段可能随时变化的情形下,选用 MongoDB 更加合适因为它的动态 Schema 不强制要求每条记录拥有相同的字段数量及类型。另一方面,如果业务逻辑涉及大量复杂的 JOIN 操作或者是金融交易类对一致性的强需求项目,则应该倾向于选择 MySQL 这样的 RDBMS 解决方案。 ```python # 示例 Python 代码展示如何连接两种不同的数据库 import pymongo from sqlalchemy import create_engine def connect_to_mongodb(): client = pymongo.MongoClient("mongodb://localhost:27017/") db = client["test_database"] collection = db["sample_collection"] def connect_to_mysql(): engine = create_engine('mysql+pymysql://username:password@host/db_name') connect_to_mongodb() connect_to_mysql() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值