You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用SQLAlchemy向Oracle数据库插入自定义对象类型数据

解决方案

错误原因

你之前的bind_processor返回的是字符串形式的对象构造语句,SQLAlchemy会将这个字符串当作CHAR类型的值传递给Oracle,但Oracle期望接收的是ADDRESS_TYPE类型的对象,因此触发ORA-00932类型不匹配错误。

方案一:利用Oracle驱动原生对象支持(推荐)

直接通过底层Oracle驱动(如oracledb/cx_Oracle)创建自定义类型的实例,让数据库直接识别为目标类型:

from sqlalchemy import create_engine, Column, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.types import UserDefinedType

Base = declarative_base()

class AddressType(UserDefinedType):
    def get_col_spec(self):
        return "ADDRESS_TYPE"

    def bind_processor(self, dialect):
        def process(value):
            if value is None:
                return None
            # 获取数据库中自定义类型的定义
            raw_conn = dialect.raw_connection()
            address_otype = raw_conn.gettype("ADDRESS_TYPE")
            # 创建自定义类型实例并赋值
            address_obj = address_otype.newobject()
            address_obj.STREET = value['street']
            address_obj.CITY = value['city']
            address_obj.STATE = value['state']
            address_obj.POSTAL_CODE = value['postal_code']
            return address_obj
        return process

    def result_processor(self, dialect, coltype):
        def process(value):
            if value is not None:
                return {
                    'street': value.STREET,
                    'city': value.CITY,
                    'state': value.STATE,
                    'postal_code': value.POSTAL_CODE
                }
            return None
        return process

class MyEntity(Base):
    __tablename__ = 'MY_TABLE'

    id = Column(Integer, primary_key=True)
    custom_address = Column(AddressType, nullable=False)

# 插入逻辑保持不变
new_entity = MyEntity(id=1, custom_address={'street':'street', 'city':'city', 'state':'ST', 'postal_code':'000000'})
db_session.add(new_entity)
db_session.commit()

方案二:使用参数化SQL表达式构造对象

通过bind_expression生成参数化的对象构造语句,避免字符串拼接带来的类型错误和SQL注入风险:

from sqlalchemy import create_engine, Column, Integer, text, bindparam
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.types import UserDefinedType

Base = declarative_base()

class AddressType(UserDefinedType):
    def get_col_spec(self):
        return "ADDRESS_TYPE"

    def bind_expression(self, bindvalue):
        # 构造参数化的对象创建SQL
        return text(
            "ADDRESS_TYPE(:street, :city, :state, :postal_code)"
        ).bindparams(
            bindparam("street", bindvalue['street']),
            bindparam("city", bindvalue['city']),
            bindparam("state", bindvalue['state']),
            bindparam("postal_code", bindvalue['postal_code'])
        )

    def result_processor(self, dialect, coltype):
        def process(value):
            if value is not None:
                return {
                    'street': value.STREET,
                    'city': value.CITY,
                    'state': value.STATE,
                    'postal_code': value.POSTAL_CODE
                }
            return None
        return process

class MyEntity(Base):
    __tablename__ = 'MY_TABLE'

    id = Column(Integer, primary_key=True)
    custom_address = Column(AddressType, nullable=False)

# 插入逻辑保持不变
new_entity = MyEntity(id=1, custom_address={'street':'street', 'city':'city', 'state':'ST', 'postal_code':'000000'})
db_session.add(new_entity)
db_session.commit()

方案对比

  • 方案一:依赖Oracle驱动的原生API,类型匹配精准,性能更优,完全规避SQL注入风险。
  • 方案二:纯SQLAlchemy层面实现,不依赖底层驱动细节,通用性更强,同样通过参数化查询避免安全问题。

注意事项

确保数据库中自定义类型名称(ADDRESS_TYPE)与get_col_spec返回值一致,Oracle默认不区分大小写,但如果创建类型时使用了引号包裹,需严格匹配大小写。

内容的提问来源于stack exchange,提问作者Ali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 22:45:27