ORM模式中的静态定义与动态映射
ORM(Object-Relational Mapping,对象关系映射),其核心思想是将数据库中的表映射成对象,使得可以使用面向对象的方式来进行数据库操作。ORM不仅仅局限于静态定义的模型类,也支持动态映射。今天就以python中的sqlalchemy为例来介绍一下“静态定义”与“动态映射”:
首先看一下动态映射的代码:
from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, select, insert, func
from sqlalchemy.orm import sessionmaker
# 数据库配置
DATABASE_URI = f'mysql+mysqlconnector://{$database_username}:{$database_password}@{$database_ip}:' \
f'{$database_port}/{$database_name}?charset=utf8mb4'
mysql_engine = create_engine(DATABASE_URI)
mysql_Session = sessionmaker(bind=mysql_engine)
mysql_session = mysql_Session()
mysql_metadata = MetaData()
mysql_metadata.bind = mysql_engine
# 映射表
mysql_table = Table('common', rag_metadata, autoload_with=rag_engine)
with mysql_engine.connect() as mysql_conn:
total_number_sql = select(func.count()).where(
mysql_table.c.source == 'A'
)
total_number = mysql_conn.execute(total_number_sql).scalar()
insert_sql = mysql_table.insert().values({"source": "A",
"number": f"{total_number+1}")
mysql_conn.execute(insert_sql)
mysql_conn.commit()
print('插入成功')
这时静态定义的代码:
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy import func
# 数据库配置
DATABASE_URI = f'mysql+mysqlconnector://{{$database_username}}:{{$database_password}}@{{$database_ip}}:' \
f'{{$database_port}}/{{$database_name}}?charset=utf8mb4'
Base = declarative_base()
# 定义数据模型
class Common(Base):
__tablename__ = 'common'
id = Column(Integer, primary_key=True)
source = Column(String(1))
number = Column(String)
# 创建数据库引擎和 Session
mysql_engine = create_engine(DATABASE_URI)
Base.metadata.create_all(mysql_engine) # 创建表结构
Session = sessionmaker(bind=mysql_engine)
session = Session()
# 使用 ORM 进行操作
with session.begin():
total_number_query = session.query(func.count(Common.id)).filter_by(source='A')
total_number = total_number_query.scalar()
new_entry = Common(source='A', number=str(total_number + 1))
session.add(new_entry)
session.commit()
print('插入成功')
从代码中可以看到,“动态映射”使用了 SQLAlchemy 的核心(Core)API,而“静态定义”的代码使用了 SQLAlchemy 的 ORM(对象关系映射)API。
这两种方法的主要区别在于如何处理数据和如何与数据库交互。
前者允许直接编写 SQL 查询语句,并提供了较低级别的控制,这种方式更接近于原生 SQL 的编写方式,但需要手动处理一些细节,比如结果集的处理等。
而后面的代码使用了 ORM API,它提供了一种面向对象的方式来处理数据库操作。
两者的主要区别如下:
- 数据模型定义:Core API使用
Table
对象来描述表结构;ORM API:使用 Python 类来定义表结构,每个类代表一个数据库表。 - 增删改查等操作:以查询操作为例,Core API使用
select
和 insert 函数构建 SQL 语句;ORM API使用session.query()<方法来执行查询,并可以使用 Python 对象来表示查询结果。而插入操作,Core API使用 insert()函数和execute()方法来执行插入操作;ORM API则创建一个新的 Python 对象实例,使用 session.add()添加到会话中,并通过 session.commit() 提交更改。 - 事务管理上:Core API需要显式地开始和提交事务;ORM API使用
session.begin()
自动管理事务上下文; - 结果处理上:core api通常要对结果集进行处理,也就是说,以查询操作为例,还得对查询返回的数据,进一步提取和处理信息,例如使用
mysql_conn.execute(query)
,得到一个结果集对象后,通常还要调用 result.fetchall()` 或者迭代结果集来处理每一行数据;而在使用 ORM API 时,这些过程通常会被自动管理。
值得强调的是,”动态映射“的方法同样遵循ORM的基本原则,因为它允许以面向对象的方式来处理数据库中的数据,例如使用select、insert等方法来构建SQL查询,而不是直接编写SQL语句。
动态映射(Dynamic Mapping)可以视为ORM的一个子集或一种变体,它允许在运行时根据数据库的实际情况来创建映射,而不是在编写代码时就确定好所有的映射关系。这种方式特别适用于那些表结构已经存在并且不想或者不能更改的情况,尤其适合那些需要灵活适应数据库变化的应用场景。
在sqlalchemy中,相比于定义ORM模型类,直接使用Table类反射表结构有以下好处:
- 减少前期工作量:如果有一个庞大的数据库需要处理,手动定义每个表对应的ORM模型类可能会非常耗时。通过反射,可以快速地开始进行数据库操作。
- 灵活性高:当数据库表结构变化时,不再需要手动更新ORM模型类。只需要重新加载表定义即可。
- 适合已有系统的集成:对于已经有完整表结构的系统,可以直接利用现有的数据库模式,避免重复劳动。
然而,这种方式也有一些局限性:
- 类型安全性和代码提示:由于没有显式定义模型类,IDE和其他工具可能无法提供类型检查和代码补全功能。
- 可读性和可维护性:显式定义模型类通常会让代码更加清晰易懂,特别是当涉及到复杂的关联关系时。
- 操作限制:定义ORM模型类可以让你更精细地控制如何与数据库交互,包括添加验证逻辑、事件监听器等。
总结来说,在sqlalchemy中,直接使用”动态映射“的方式适合快速原型开发或已有数据库的集成场景,而定义ORM模型类则更适合长期维护的大型项目,尤其是在需要类型安全和更好的代码组织时。