Python自动化测试之Oracle数据库操作封装解析
一、OracleClient核心实现
1.1 类初始化与配置
class OracleClient ( DataBase) :
@classmethod
def setup ( cls, * args, ** kwargs) :
return cls( 'oracle' , * args, ** kwargs)
def select ( self, sql: str , param: [ list , None ] = None ,
rows: [ int , None ] = 1 , ** kwargs) :
if param and kwargs:
raise Exception( f'参数类型冲突: { param} , { kwargs} ' )
with self. _oracle_pool. acquire( ) as conn:
with conn. cursor( ) as cur:
cur. execute( sql, ( param or kwargs) )
columns = [ col[ 0 ] for col in cur. description]
cur. rowfactory = lambda * args: dict ( zip ( columns, args) )
if rows:
rs = cur. fetchone( ) if rows == 1 else cur. fetchmany( rows)
else :
rs = cur. fetchall( )
return rs
参数说明表
参数 类型 必需性 作用描述 sql str 是 SQL查询语句 param list/tuple 否 位置参数列表 kwargs dict 否 命名参数键值对 rows int/None 否 返回行数限制
1.2 数据操作实现
def execute ( self, sql: str , param: List[ tuple ] , ** kwargs) :
with self. _oracle_pool. acquire( ) as conn:
with conn. cursor( ) as cur:
if param:
cur. executemany( sql, param)
else :
cur. execute( sql, ** kwargs)
rowcount = cur. rowcount
conn. commit( )
return rowcount
方法特性对比
特性 select方法 execute方法 返回类型 字典列表 影响行数 事务管理 自动提交 显式提交 参数绑定方式 位置/命名参数 批量/单条 结果集处理 自动转字典 无
二、实战操作示例
2.1 查询操作演示
result1 = oracle. select(
r'SELECT * FROM employees WHERE id = :1 AND name = :2' ,
[ 101 , 'Alice' ] ,
rows= 1
)
result2 = oracle. select(
r'SELECT * FROM departments WHERE dept_id = :id' ,
rows= None ,
id = 'D001'
)
print ( f"员工信息: { result1} " )
print ( f"部门信息: { result2} " )
示例输出
员工信息:{'ID': 101, 'NAME': 'Alice', 'SALARY': 8500}
部门信息:[{'DEPT_ID':'D001', 'NAME':'研发部'}, {...}]
2.2 数据更新操作
batch_params = [
( '高级工程师' , 'E101' ) ,
( '资深经理' , 'M202' )
]
affected = oracle. execute(
r'UPDATE positions SET title = :1 WHERE emp_id = :2' ,
param= batch_params
)
single_affected = oracle. execute(
r'UPDATE salaries SET amount = :amount WHERE emp_id = :id' ,
amount= 12000 ,
id = 'E101'
)
print ( f"批量更新影响行数: { affected} " )
print ( f"单条更新影响行数: { single_affected} " )
示例输出
批量更新影响行数:2
单条更新影响行数:1
三、代码优化建议
3.1 现存问题清单
问题描述 风险等级 改进方案 参数绑定语法错误 高 使用正确命名绑定语法 缺乏事务回滚机制 高 添加try/except回滚逻辑 结果集转换性能问题 中 使用高效字典生成方式 未处理空结果集情况 低 添加空值判断逻辑
3.2 增强型实现
from contextlib import contextmanager
class SafeOracleClient ( OracleClient) :
@contextmanager
def transaction ( self) :
conn = self. _oracle_pool. acquire( )
try :
yield conn
conn. commit( )
except Exception as e:
conn. rollback( )
raise
finally :
conn. close( )
def select ( self, sql, ** kwargs) :
sanitized = { k: v for k, v in kwargs. items( ) if not isinstance ( v, str ) }
return super ( ) . select( sql, ** sanitized)
with SafeOracleClient. setup( ) . transaction( ) as conn:
conn. execute( "..." )
四、企业级最佳实践
4.1 某金融系统Oracle操作规范
参数化查询 :禁止字符串拼接SQL连接管理 :单个事务时间不超过5秒批量操作 :每次最多1000条记录审计日志 :记录所有数据变更操作性能规范 :查询结果超过1万行需分页
def get_paginated_data ( page= 1 , size= 100 ) :
offset = ( page- 1 ) * size
return oracle. select(
"SELECT * FROM transactions ORDER BY id "
"OFFSET :offset ROWS FETCH NEXT :size ROWS ONLY" ,
offset= offset,
size= size
)
4.2 性能优化方案
from cx_Oracle import Cursor
class OptimizedOracleClient ( OracleClient) :
def __init__ ( self) :
self. _prepared = { }
def select ( self, sql, ** kwargs) :
if sql not in self. _prepared:
stmt = self. _oracle_pool. prepare( sql)
self. _prepared[ sql] = stmt
return self. _prepared[ sql] . execute( ** kwargs) . fetchall( )
optimized = OptimizedOracleClient. setup( )
result = optimized. select( "SELECT * FROM products WHERE category=:cat" , cat= 'ELECTRONIC' )
五、完整代码
"""
Python :3.13.3
Selenium: 4.31.0
database.py
"""
import asyncio
from chap5. file_reader import INIReader
from setting import DATABASE_INI_PATH
from aiomysql import create_pool, DictCursor
from cx_Oracle import SessionPool
from asyncio import ensure_future
from typing import List
class DataBase :
def __init__ ( self, database: str = 'mysql' , autocommit: bool = True , * args, ** kwargs) :
self. _args, self. _kwargs = args, kwargs
self. _autocommit = autocommit
if database. lower( ) == 'mysql' :
self. _database = create_pool
self. _ini = INIReader( DATABASE_INI_PATH) . data
self. _loop = asyncio. new_event_loop( )
asyncio. set_event_loop( self. _loop)
self. _mysql_pool = self. mysql_pool
if database. lower( ) == 'oracle' :
self. _database = SessionPool
self. _ini = INIReader( DATABASE_INI_PATH, section= 'oracle' ) . data
self. _oracle_pool = self. oracle_pool
@property
def oracle_pool ( self) :
return self. _database( * self. _args, ** self. _ini, ** self. _kwargs)
@property
def mysql_pool ( self) :
self. _ini[ 'autocommit' ] = self. _autocommit
pool_task = ensure_future( self. _database( * self. _args, ** self. _ini, ** self. _kwargs) )
self. _loop. run_until_complete( pool_task)
return pool_task. result( )
class MysqlClient ( DataBase) :
@classmethod
def setup ( cls, * args, ** kwargs) :
return cls(
* args, ** kwargs
)
async def _select ( self, sql: str , param: tuple = ( ) , rows: [ int , None ] = 1 ) :
async with self. _mysql_pool. acquire( ) as conn:
async with conn. cursor( DictCursor) as cur:
await cur. execute( sql. replace( '?' , '%s' ) , param)
if rows:
rs = await cur. fetchmany( rows)
else :
rs = await cur. fetchall( )
return rs
def select ( self, * args, ** kwargs) :
self. _loop. run_until_complete( select_task := ensure_future( self. _select( * args, ** kwargs) ) )
return select_task. result( )
async def _execute ( self, sql: str , param: tuple = ( ) ) :
async with self. _mysql_pool. acquire( ) as conn:
async with conn. cursor( ) as cur:
await cur. execute( sql. replace( '?' , '%s' ) , param)
return cur. rowcount
def execute ( self, * args, ** kwargs) :
self. _loop. run_until_complete( execute_task := ensure_future( self. _execute( * args, ** kwargs) ) )
return execute_task. result( )
class OracleClient ( DataBase) :
@classmethod
def setup ( cls, * args, ** kwargs) :
return cls(
'oracle' , * args, ** kwargs
)
def select ( self, sql: str , param: [ list , None ] = None , rows: [ int , None ] = 1 , ** kwargs) :
if param and kwargs:
raise Exception( f'两种参数类型不能同时传入: { param} , { kwargs} ' )
with self. _oracle_pool. acquire( ) as conn:
with conn. cursor( ) as cur:
cur. execute( sql, ( param or kwargs) )
columns = [ col[ 0 ] for col in cur. desciption]
cur. rowfactory = lambda * args: dict ( zip ( columns, args) )
if rows:
if rows == 1 :
rs = cur. fetchone( )
else :
rs = cur. fetchmany( rows)
else :
rs = cur. fetchall( )
return rs
def execute ( self, sql: str , param: List[ tuple ] , ** kwargs) :
with self. _oracle_pool. acquire( ) as conn:
with conn. cursor( ) as cur:
if param:
cur. executemany( sql, param)
else :
cur. execute( sql, ** kwargs)
rowcount = cur. rowcount
conn. commit( )
return rowcount
oracle = OracleClient. setup( )
oracle. select( r'SELECT * FROM TABLEA WHERE ID = :ID AND NAME = :SAM' , [ 1 , 'SAM' ] , 1 )
oracle. select( r'SELECT * FROM TABLEA WHERE ID = :ID AND NAME = :SAM' , rows= 1 , ID= 1 , SAM= 'SAM' )
oracle. execute( r'UPDATE DEMO_TABLE SET NAME = :SAM WHERE ID = :ID' ,
[ ( 'SAM' , 1 ) , ( 'TOM' , 2 ) ] )
oracle. execute( r'UPDATE DEMO_TABLE SET NAME = :SAM WHERE ID = :ID' , param= [ ] , SAM= 'SAM' , ID= 1 )
「小贴士」 :点击头像→【关注 】按钮,获取更多软件测试的晋升认知不迷路! 🚀