ORM

ORM,对象关系映射,对象和关系之间的映射,使用面向对象的方式来操作数据库

关系模型和Python对象之间的映射
table => class         表映射为类
row =>object          行映射为实例
column=> property  字段映射为属性

SQLAIchemy

SQLAIchemy是一个ORM框架

  • 安装
pip install sqlalchemy

开发

  • 创建连接
    SQLAIchemy内部使用连接池
    数据库连接的事情,交给引擎
dialect+driver:// username:password@host:port/database
# mysqldb连接
mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
engine=sqlalchemy.create_engine("mysql+mysqldb://user:password@host:port/database")
#pymysql的连接
mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
engine=sqlalchemy.create_engine("mysql+mysqldb://user:password@host:port/database")
engine=sqlalchemy.create_engine("mysql+mysqldb://user:password@host:port/database",echo=True)
echo=True
引擎是否打印执行的语句,调试的时候打开很方便
lazy connecting:懒连接,创建引擎并不会马上连接数据库,直到让数据库执行任务时才连接
  • Declace a Mapping创建映射

1、创建基类

from sqlalchemy.ext.declarative import declarative_base
Base=declarative_base()

2、创建实体类
student表
在这里插入图片描述

import sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column,Integer,String
engine=sqlalchemy.create_engine("mysql+pymysql://xiaobai:xiaobai@192.168.163.130:3306/school",echo=True)
Base=declarative_base()
class Student(Base):
    __tablename__='student'
    id=Column(Integer,primary_key=True,autoincrement=True)
    name=Column(String(64),nullable=False)
    age=Column(Integer)
    def __repr__(self):
        return "{} id={} name={} age={}".format(self.__class__.__name__,self.id,self.name,self.age)
print(Student)
print(repr(Student.__table__))

__tablename__指定表名
Column类指定对应的字段,必须指定
3、创建表
可以使用SQLAlchemy来创建、删除表

#删除继承自Base的所有表
Base.metadata.drop_all(engine)
#创建继承自Base的所有表
Base.metadata.create_all(engine)

生产环境很少这样创建表,都是系统上线的时候由脚本生成
生成环境很少删除表,宁可废弃都不能删除

4、创建会话session
在一个会话中操作数据库,会话建立在连接上,连接被引擎管理。
当第一次使用数据库时,从引擎维护的连接池中获取一个连接使用

from sqlalchemy.orm import sessionmaker
#创建session
Session=sessionmaker(bind=engine) #工厂方法返回类
session=Session()  #实例化
#依然在第一次使用时连接数据库

session对象线程不安全,所以不同线程应该使用不同的session对象
Session类和engine有一个就可以了

CRUD操作


add():增加一个对象
add_all():可迭代对象,元素是对象

import sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column,Integer,String
from sqlalchemy.orm import sessionmaker
engine=sqlalchemy.create_engine("mysql+pymysql://xiaobai:xiaobai@192.168.163.132:3306/test",echo=True)
# 创建基类
Base=declarative_base()
# 创建实体类
class Student(Base):
    #指定表明
    __tablename__='student'
    #定义属性对应字段
    id=Column(Integer,primary_key=True,autoincrement=True)
    name=Column(String(64),nullable=False)
    age=Column(Integer)
    def __repr__(self):
        return "<{}{}{}>".format(self.__class__.__name__,self.id,self.name,self.age)
#删除表
Base.metadata.drop_all(engine)
#创建表
Base.metadata.create_all(engine)

#创建session
Session=sessionmaker(bind=engine)
session=Session()

s=Student(name='tom') # 构造时传入
s.age=20   #属性赋值
print(s)

session.add(s)
print(s)
session.commit()
print('`````````````````````````````')
try:
    session.add_all([s])
    print(s,'````````````````````````')
    session.commit()
    print(s)
except:
    session.collback()
    print('roll back')
    raise

在这里插入图片描述
add_all()方法不会提交成功的,不是因为它不对,而是s,s成功提交后,s的主键就有了值,所以只要s没有修改过,就认为没有改动。
s主键没有值,就是新增,主键有值,就是找到主键对应的记录修改。

简单查询
使用query()方法,返回一个Query对象

students=session.query(Student) #无条件
print(students)
for student in students:
    print(repr(student))
print('```````````````````````````')

student=session.query(Student).get(1)
print(student)

在这里插入图片描述
query方法将实体类传入,返回类的对象可迭代对象,这时候并不查询,迭代它就执行SQL来查询数据库,封装数据到指定类的实例
get方法使用主键查询,返回一条传入类的一个实例

student=session.query(Student).get(2)
print(student)
student.name='ergou'
student.age=50
print(student)
session.add(student)
session.commit()

在这里插入图片描述
先查回来,修改后,再提交更改

删除

try:
    student=Student(id=2,name="sam",age=30)
    session.delete(student)
    session.commit()
except Exception as e:
    session.rollback()
    print('``````````````')
    print(e)

在这里插入图片描述
会产生一个异常

Instance '<Student at 0x1f997aa9748>' is not persisted 未持久化异常!

状态
每一个实体,都有一个状态属性_sa_instance_state,其类型是sqlalchemy.orm.state.InstanceState,可以使用sqlalchemy.inspect(entity)函数查看状态
常见的状态值有transient(短暂)、pending(未决定)、persistent(持久)、deleted(已删除)、detached(分离)

状态说明
transient实体尚未加入到session中,同时并没有保存到数据库中
pendingtransient的实体被add()到session中,状态切换到pending,但它还没有flush到数据库中
persistentsession中的实体对象对应着数据库中的真实记录。pending状态在提交成功后可以变成persistent状态,或者查询成功返回的实体也是persistent状态
deleted实体被删除且已经flush但未commit完成。事务提交成功了实体变成detached,事务失败,返回persistent状态
detached删除成功的实体进入这个状态

新建一个实体,状态是transient临时的
一旦add()后从transient变成pending状态
成功commit()后从pending变成persistent状态
成功查询返回的实体对象,也是persistent状态
persistent状态的实体,修改依然是persistent状态
persistent状态的实体,删除后,flush后但没有commit,就变成detele状态,成功提交,变为detached状态,提交失败,还原到persistent状态,flush方法,主动把概念应用大数据库中去。
删除、修改操作,需要对应一个真实的记录,所以要求实体对象是persistent状态

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

connsrt="{}://{}:{}@{}:{}/{}".format('mysql+pymysql','xiaobai','xiaobai','192.168.163.133',3306,'test')
# 创建连接
engine=create_engine(connsrt,echo=True)

# 创建基类
Base=declarative_base()
#创建实体类
class Student(Base):
    # 指定表名
    __tablename__='student'
    # 定义属性对应字段
    id=Column(Integer,primary_key=True,autoincrement=True)
    name=Column(String(64),nullable=False)
    age=Column(Integer)

    def __repr__(self):
        return "{} id={} name={} age={}".format(__class__.__name__,self.id,self.name,self.age)
# 创建会话
Session=sessionmaker(bind=engine)
session=Session()
from sqlalchemy.orm.state import InstanceState
def getstate(instance,i):
    inp: InstanceState=sqlalchemy.inspect(instance)
    states="{}:key={},nsid={},attached={},transient={},pending={}\n,persistent={},deleted={},detached={}".format(
        i,inp.key,
        inp.session_id,inp._attached,inp.transient,
        inp.pending,inp.persistent,inp.deleted,inp.detached
    )
    print(states,end='\n------------------------\n')
student=session.query(Student).get(2)
getstate(student,1)

try:
    student=Student(id=2,name='ergou',age=50)
    getstate(student,2)

    student=Student(name='sammy',age=30)
    getstate(student,3)
    session.add(student)
    getstate(student,4)

    session.commit()
    getstate(student,5)

    session.delete(student)
    getstate(student,6)
    session.commit()
    getstate(student,7)
except Exception as e:
    session.rollback()
    print(e,'````````````````')

在这里插入图片描述
复杂查询

from sqlalchemy import Column,Integer,String,Date,Enum,ForeignKey,create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
import enum
Base=declarative_base()
connstr="{}://{}:{}@{}:{}/{}".format(
    "mysql+pymysql",'xiaobai','xiaobai',
    "192.168.163.133",3306,'test'
)
engine=create_engine(connstr,echo=True)
Session=sessionmaker(bind=engine)
session=Session()

class MyEnum(enum.Enum):
    M='M'
    F='F'

class Employee(Base):
    #指定表名
    __tablename__="employees"
    # 定义属性对应字段
    emp_no=Column(Integer,primary_key=True)
    birth_date=Column(Date,nullable=False)
    first_name=Column(String(14),nullable=False)
    last_name=Column(String(16),nullable=False)
    gender=Column(Enum(MyEnum),nullable=False)
    hire_date=Column(Date,nullable=False)

    def __repr__(self):
        return "{} no={} name={} {} gender={}".format(
            self.__class__.__name__,self.emp_no,self.first_name,self.last_name,self.gender.value
        )
def show(emps):
    for x in emps:
        print(x)
    print("````````````````````")

#简单查询
emps=session.query(Employee).filter(Employee.emp_no>10015)
show(emps)
# 与或非
from sqlalchemy import or_,and_,not_
# AND条件
emps=session.query(Employee).filter(Employee.emp_no>10015).filter(Employee.gender==MyEnum.F)
show(emps)

emps=session.query(Employee).filter(Employee.emp_no>10015,Employee.emp_no<10018)
show(emps)

emps=session.query(Employee).filter(and_(Employee.emp_no>10015,Employee.gender==MyEnum.M))
show(emps)

emps=session.query(Employee).filter((Employee.emp_no>10015)&(Employee.gender==MyEnum.F))
show(emps)
# OR条件
emps=session.query(Employee).filter((Employee.emp_no>10018)|(Employee.emp_no<10003))
show(emps)

emps=session.query(Employee).filter(or_(Employee.emp_no>10018,Employee.emp_no<10003))
show(emps)

# Not
emps=session.query(Employee).filter(not_(Employee.emp_no<10018))
show(emps)

emps=session.query(Employee).filter(~(Employee.emp_no<10018))
show(emps)

#in
emplist=[10010,10015,10018]
emps=session.query(Employee).filter(Employee.emp_no.in_(emplist))
show(emps)

#not in
emps=session.query(Employee).filter(~Employee.emp_no.in_(emplist))
show(emps)

emps=session.query(Employee).filter(Employee.emp_no.notin_(emplist))
show(emps)
#like
emps=session.query(Employee).filter(Employee.last_name.like('P%'))
show(emps)
# not like
emps=session.query(Employee).filter(Employee.last_name.notlike('P%'))
show(emps)

# 排序
emps=session.query(Employee).filter(Employee.emp_no>10010).order_by(Employee.emp_no)
show(emps)

emps=session.query(Employee).filter(Employee.emp_no>10010).order_by(Employee.emp_no.asc())
show(emps)

#降序
emps=session.query(Employee).filter(Employee.emp_no>10010).order_by(Employee.emp_no.desc())
show(emps)

#多列排序
emps=session.query(Employee).filter(Employee.emp_no>10010).order_by(
    Employee.last_name).order_by(Employee.emp_no.desc())
show(emps)

# 分页
emps=session.query(Employee).limit(4)
show(emps)

emps=session.query(Employee).limit(4).offset(18)
show(emps)

# 总行数
emps=session.query(Employee)
print(len(list(emps)))
print(emps.count())

# 取所有数据
print(emps.all())

# 取行首
print(emps.first())

#有且只能有一行
print(emps.limit(1).one())

#删除
session.query(Employee).filter(Employee.emp_no>10018).delete()

# 聚合函数
#count
from sqlalchemy import func

query=session.query(func.count(Employee.emp_no))
# 列表中一个元素
print('``````````````',query.all(),'````````````````````')
#一个只有一个元素的元组
print(query.first())
# 只能有一行返回,一个元组
print(query.one())
# 取one()的第一个元素
print(query.scalar())

# max/min/avg
print(session.query(func.max(Employee.emp_no)).scalar())
print(session.query(func.min(Employee.emp_no)).scalar())
print(session.query(func.avg(Employee.emp_no)).scalar())

# 分组
query=session.query(Employee.gender,
                    func.count(Employee.emp_no)).group_by(Employee.gender).all()
for g,y in query:
    print(g,g.value,y)

关联查询
在这里插入图片描述

from sqlalchemy import Column,create_engine,Integer,String,Enum,Date,ForeignKey
from  sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker,relationship
import sqlalchemy,enum
Base=declarative_base()
# 创建会话
engine=create_engine("mysql+pymysql://xiaobai:xiaobai@192.168.163.134:3306/test",echo=True)
Session=sessionmaker(bind=engine)
session=Session()
class Myenum(enum.Enum):
    M="M"
    F="F"
# 创建实体类
class Employee(Base):
     # 指定表名
     __tablename__ = 'employees'
     # 定义属性对应字段
     emp_no = Column(Integer, primary_key=True)
     birth_date = Column(Date, nullable=False)
     first_name = Column(String(14), nullable=False)
     last_name = Column(String(16), nullable=False)
     gender = Column(Enum(Myenum), nullable=False)
     hire_date = Column(Date, nullable=False)
     departments = relationship('Dept_emp')
     # 第一参数是字段名,如果和属性名不一致,一定要指定
     # age = Column('age', Integer)
     def __repr__(self):
        return "{} no={} name={} {} gender={},depts={}".format(
            self.__class__.__name__, self.emp_no, self.first_name, self.last_name,
            self.gender.value,self.departments)
class Department(Base):
     __tablename__ = 'departments'
     dept_no = Column(String(4), primary_key=True)
     dept_name = Column(String(40), nullable=False, unique=True)
     def __repr__(self):
         return "{} no={} name={}".format(
         type(self).__name__, self.dept_no, self.dept_name)
class Dept_emp(Base):
     __tablename__ = "dept_emp"
     emp_no = Column(Integer, ForeignKey('employees.emp_no',
    ondelete='CASCADE'),primary_key=True)
     dept_no = Column(String(4), ForeignKey('departments.dept_no', ondelete='CASCADE'),
    primary_key=True)
     from_date = Column(Date, nullable=False)
     to_date = Column(Date, nullable=False)
     def __repr__(self):
         return "{} empno={} deptno={}".format(
         type(self).__name__, self.emp_no, self.dept_no)

def show(emps):
    for x in emps:
        print(x)
    print("````````````````````")
results=session.query(Employee,Dept_emp).filter(Employee.emp_no==Dept_emp.emp_no).filter(
    Employee.emp_no==10010).all()
show(results)

results=session.query(Employee).join(Dept_emp).filter(Employee.emp_no==10010).all()
show(results)

results=session.query(Employee).join(Dept_emp,Employee.emp_no==Dept_emp.emp_no).filter(Employee.emp_no
                                                                                  ==10010).all()
print(results)

# 查询10010员工的所在的部门编号及其员工信息
results=session.query(Employee).join(Dept_emp).filter(Employee.emp_no==Dept_emp.emp_no).filter(Employee.emp_no==10010)
show(results)
print(11111111111)

results=session.query(Employee).join(Dept_emp,Employee.emp_no==Dept_emp.emp_no).filter(Employee.emp_no==10010)
show(results)

results=session.query(Employee).join(Dept_emp,(Employee.emp_no==Dept_emp.emp_no)&
                                     (Employee.emp_no==10010))

在这里插入图片描述
第一种方法join(Dept_emp)中没有等值条件,会自动生成一个等值条件,如果后面有filter,哪怕是filter(Employee.emp_no==Ddept_emp.emp_no),这个条件会在where中出现,第一种这种自动增加join的等值条件的方式不好。
第二种在join中增加等值条件,阻止了自动的等值条件的生成

总结

在开发中,一般都会采用ORM框架,这样就可以使用对象操作表了。
定义表映射的类,使用Column的描述器定义类属性,使用ForeignKey来定义外键约束。
如果在一个对象中,想查看其它表对应的对象的内容,就要使用relationship来定义关系。
是否使用外键约束?
1、力挺派
能使数据保证完整性一致性
2、弃用派
开发难度增加,大量数据的时候影响插入、修改、删除的效率。
在业务层保证数据的一致性。

### ORM全称及其工作原理 ORM 的全称是 **Object-Relational Mapping**,即对象关系映射。它是一种编程技术,用于将对象模型与关系型数据库模型之间进行转换[^1]。在应用程序开发中,开发者通常会使用面向对象的语言(如 Python),而底层的数据存储则多采用关系型数据库(如 MySQL 或 PostgreSQL)。由于这两者之间的数据表示方式存在差异,因此需要一种机制来进行桥接。 #### 工作原理 ORM 的核心功能在于建立程序中的对象与其对应的数据库记录之间的映射关系。具体来说: 1. 将类映射为数据库中的表; 2. 将类的属性映射为表中的字段; 3. 将对象实例映射为表中的单条记录。 这种映射使得开发者可以像操作普通的对象一样对数据库进行增删改查操作,无需手动编写复杂的 SQL 语句。例如,在 Django 中可以通过 `filter` 方法实现查询条件过滤。 以下是 Django ORM 使用的一个简单例子: ```python from myapp.models import MyModel # 查询所有符合条件的对象 results = MyModel.objects.filter(field__condition=value) for result in results: print(result.some_field) ``` 在这个过程中,Django 自动将上述代码翻译成相应的 SQL 并执行查询。 --- ### 数据库对象关系映射的具体实现方式 为了更好地理解 ORM 的实现细节,可以从以下几个方面入手: #### 1. 类与表的关系定义 ORM 框架允许开发者通过定义类的方式描述数据库结构。例如,在 Django 中,每一个 Model 类都代表一张数据库表,其属性则对应于表中的列。下面是一个简单的示例[^4]: ```python class User(models.Model): name = models.CharField(max_length=50) age = models.IntegerField() ``` 在此基础上,ORM 框架会在后台生成并维护所需的元数据,以便将其转化为实际的数据库模式。 #### 2. 动态构建 SQL 当调用诸如 `.filter()` 这样的方法时,ORM 不会立即执行查询,而是先构建一个 QuerySet 对象。这个对象包含了所有的查询条件,并且只有在真正需要用到结果集的时候才会触发实际的 SQL 执行。 #### 3. 缓存优化 许多现代 ORM 实现还提供了缓存机制以提高性能。比如,如果多次请求相同的数据,则可以直接返回之前的结果而不是每次都重新发起数据库查询[^2]。 #### 4. 跨平台兼容性 某些高级框架(如同 ThinkPHP 提供的 DAL 层)能够屏蔽不同 RDBMS 间的语法差异,让应用更容易移植到其他类型的数据库上运行。 --- ### 总结 综上所述,ORM 是连接高层抽象世界(对象)和低级物理存储媒介(表格)的重要桥梁之一。借助这一工具,软件工程师不仅减少了重复劳动量——因为不再需要手写大量繁琐的基础 CRUD (Create Read Update Delete) 操作逻辑;同时也增强了系统的可扩展性和灵活性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值