|
|
|
@ -19,7 +19,7 @@ class PyODBCConnectionPool:
|
|
|
|
"""纯pyodbc连接池实现"""
|
|
|
|
"""纯pyodbc连接池实现"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, dsn: str, pool_size: int = 5, max_overflow: int = 10,
|
|
|
|
def __init__(self, dsn: str, pool_size: int = 5, max_overflow: int = 10,
|
|
|
|
pool_timeout: int = 30, pool_recycle: int = 3600):
|
|
|
|
pool_timeout: int = 30, pool_recycle: int = 3600, lazy_init: bool = False):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
初始化连接池
|
|
|
|
初始化连接池
|
|
|
|
|
|
|
|
|
|
|
|
@ -29,12 +29,14 @@ class PyODBCConnectionPool:
|
|
|
|
max_overflow: 最大溢出连接数
|
|
|
|
max_overflow: 最大溢出连接数
|
|
|
|
pool_timeout: 获取连接超时(秒)
|
|
|
|
pool_timeout: 获取连接超时(秒)
|
|
|
|
pool_recycle: 连接回收时间(秒)
|
|
|
|
pool_recycle: 连接回收时间(秒)
|
|
|
|
|
|
|
|
lazy_init: 是否延迟初始化(不预创建连接)
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
self.dsn = dsn
|
|
|
|
self.dsn = dsn
|
|
|
|
self.pool_size = pool_size
|
|
|
|
self.pool_size = pool_size
|
|
|
|
self.max_overflow = max_overflow
|
|
|
|
self.max_overflow = max_overflow
|
|
|
|
self.pool_timeout = pool_timeout
|
|
|
|
self.pool_timeout = pool_timeout
|
|
|
|
self.pool_recycle = pool_recycle
|
|
|
|
self.pool_recycle = pool_recycle
|
|
|
|
|
|
|
|
self._is_available = False
|
|
|
|
|
|
|
|
|
|
|
|
# 连接池队列
|
|
|
|
# 连接池队列
|
|
|
|
self._pool = Queue(maxsize=pool_size)
|
|
|
|
self._pool = Queue(maxsize=pool_size)
|
|
|
|
@ -42,12 +44,19 @@ class PyODBCConnectionPool:
|
|
|
|
self._overflow_count = 0
|
|
|
|
self._overflow_count = 0
|
|
|
|
self._overflow_lock = Lock()
|
|
|
|
self._overflow_lock = Lock()
|
|
|
|
|
|
|
|
|
|
|
|
# 预创建连接
|
|
|
|
# 预创建连接(如果不是延迟初始化)
|
|
|
|
for _ in range(pool_size):
|
|
|
|
if not lazy_init:
|
|
|
|
conn = self._create_connection()
|
|
|
|
try:
|
|
|
|
self._pool.put((conn, time.time()))
|
|
|
|
for _ in range(pool_size):
|
|
|
|
|
|
|
|
conn = self._create_connection()
|
|
|
|
logger.info(f"ODBC连接池初始化成功: DSN={dsn}, 池大小={pool_size}")
|
|
|
|
self._pool.put((conn, time.time()))
|
|
|
|
|
|
|
|
self._is_available = True
|
|
|
|
|
|
|
|
logger.info(f"ODBC连接池初始化成功: DSN={dsn}, 池大小={pool_size}")
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
|
|
logger.error(f"ODBC连接池初始化失败: {e}")
|
|
|
|
|
|
|
|
self._is_available = False
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
logger.info(f"ODBC连接池延迟初始化: DSN={dsn}, 池大小={pool_size}")
|
|
|
|
|
|
|
|
|
|
|
|
def _create_connection(self):
|
|
|
|
def _create_connection(self):
|
|
|
|
"""创建新连接"""
|
|
|
|
"""创建新连接"""
|
|
|
|
@ -75,9 +84,29 @@ class PyODBCConnectionPool:
|
|
|
|
except:
|
|
|
|
except:
|
|
|
|
return False
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def is_available(self) -> bool:
|
|
|
|
|
|
|
|
"""检查连接池是否可用"""
|
|
|
|
|
|
|
|
return self._is_available
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_connection(self) -> bool:
|
|
|
|
|
|
|
|
"""测试连接是否可用"""
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
conn = self._create_connection()
|
|
|
|
|
|
|
|
conn.close()
|
|
|
|
|
|
|
|
self._is_available = True
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
|
|
logger.error(f"ODBC连接测试失败: {e}")
|
|
|
|
|
|
|
|
self._is_available = False
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
@contextmanager
|
|
|
|
@contextmanager
|
|
|
|
def get_connection(self):
|
|
|
|
def get_connection(self):
|
|
|
|
"""获取连接(上下文管理器)"""
|
|
|
|
"""获取连接(上下文管理器)"""
|
|
|
|
|
|
|
|
# 检查连接池是否可用
|
|
|
|
|
|
|
|
if not self._is_available:
|
|
|
|
|
|
|
|
raise Exception("ODBC连接池不可用,请检查数据源配置")
|
|
|
|
|
|
|
|
|
|
|
|
conn = None
|
|
|
|
conn = None
|
|
|
|
created_time = None
|
|
|
|
created_time = None
|
|
|
|
is_overflow = False
|
|
|
|
is_overflow = False
|
|
|
|
@ -150,6 +179,7 @@ class ERPODBCManager:
|
|
|
|
|
|
|
|
|
|
|
|
_instance = None
|
|
|
|
_instance = None
|
|
|
|
_pool = None
|
|
|
|
_pool = None
|
|
|
|
|
|
|
|
_initialized = False
|
|
|
|
|
|
|
|
|
|
|
|
def __new__(cls):
|
|
|
|
def __new__(cls):
|
|
|
|
"""单例模式"""
|
|
|
|
"""单例模式"""
|
|
|
|
@ -159,22 +189,54 @@ class ERPODBCManager:
|
|
|
|
|
|
|
|
|
|
|
|
def init_app(self, app):
|
|
|
|
def init_app(self, app):
|
|
|
|
"""初始化(在app启动时调用)"""
|
|
|
|
"""初始化(在app启动时调用)"""
|
|
|
|
|
|
|
|
if self._initialized:
|
|
|
|
|
|
|
|
logger.warning("ERP ODBC管理器已初始化,跳过重复初始化")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
config = app.config.get("ERP_ODBC_CONFIG", {})
|
|
|
|
config = app.config.get("ERP_ODBC_CONFIG", {})
|
|
|
|
|
|
|
|
|
|
|
|
# 创建连接池
|
|
|
|
# 检查是否启用ODBC
|
|
|
|
self._pool = PyODBCConnectionPool(
|
|
|
|
if not config.get("enabled", True):
|
|
|
|
dsn=config.get("dsn", "YHC-test"),
|
|
|
|
logger.info("ERP ODBC功能已禁用(配置项 enabled=False)")
|
|
|
|
pool_size=config.get("pool_size", 5),
|
|
|
|
self._pool = None
|
|
|
|
max_overflow=config.get("max_overflow", 10),
|
|
|
|
self._initialized = True
|
|
|
|
pool_timeout=config.get("pool_timeout", 30),
|
|
|
|
return
|
|
|
|
pool_recycle=config.get("pool_recycle", 3600),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.info("ERP ODBC管理器初始化成功")
|
|
|
|
try:
|
|
|
|
|
|
|
|
# 创建连接池(延迟初始化)
|
|
|
|
|
|
|
|
self._pool = PyODBCConnectionPool(
|
|
|
|
|
|
|
|
dsn=config.get("dsn", "YHC-test"),
|
|
|
|
|
|
|
|
pool_size=config.get("pool_size", 5),
|
|
|
|
|
|
|
|
max_overflow=config.get("max_overflow", 10),
|
|
|
|
|
|
|
|
pool_timeout=config.get("pool_timeout", 30),
|
|
|
|
|
|
|
|
pool_recycle=config.get("pool_recycle", 3600),
|
|
|
|
|
|
|
|
lazy_init=True, # 延迟初始化,不预创建连接
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 测试连接
|
|
|
|
|
|
|
|
if self._pool.test_connection():
|
|
|
|
|
|
|
|
logger.info("ERP ODBC管理器初始化成功,连接测试通过")
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
logger.warning("ERP ODBC管理器初始化完成,但连接测试失败,ODBC功能将不可用")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self._initialized = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
|
|
logger.error(f"ERP ODBC管理器初始化失败: {e}", exc_info=True)
|
|
|
|
|
|
|
|
logger.warning("应用将继续启动,但ODBC功能将不可用")
|
|
|
|
|
|
|
|
self._pool = None
|
|
|
|
|
|
|
|
self._initialized = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def is_available(self) -> bool:
|
|
|
|
|
|
|
|
"""检查ODBC连接是否可用"""
|
|
|
|
|
|
|
|
return self._pool is not None and self._pool.is_available()
|
|
|
|
|
|
|
|
|
|
|
|
@contextmanager
|
|
|
|
@contextmanager
|
|
|
|
def get_connection(self):
|
|
|
|
def get_connection(self):
|
|
|
|
"""获取连接(上下文管理器)"""
|
|
|
|
"""获取连接(上下文管理器)"""
|
|
|
|
|
|
|
|
if not self.is_available():
|
|
|
|
|
|
|
|
raise Exception("ODBC连接不可用,请检查数据源配置或联系管理员")
|
|
|
|
|
|
|
|
|
|
|
|
with self._pool.get_connection() as conn:
|
|
|
|
with self._pool.get_connection() as conn:
|
|
|
|
yield conn
|
|
|
|
yield conn
|
|
|
|
|
|
|
|
|
|
|
|
@ -188,7 +250,13 @@ class ERPODBCManager:
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Returns:
|
|
|
|
查询结果列表(字典格式)
|
|
|
|
查询结果列表(字典格式)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
|
|
|
Exception: 当ODBC连接不可用或查询失败时
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
if not self.is_available():
|
|
|
|
|
|
|
|
raise Exception("ODBC连接不可用,无法执行查询")
|
|
|
|
|
|
|
|
|
|
|
|
with self.get_connection() as conn:
|
|
|
|
with self.get_connection() as conn:
|
|
|
|
cursor = conn.cursor()
|
|
|
|
cursor = conn.cursor()
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
@ -224,7 +292,13 @@ class ERPODBCManager:
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
Returns:
|
|
|
|
影响的行数
|
|
|
|
影响的行数
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
|
|
|
|
Exception: 当ODBC连接不可用或执行失败时
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
if not self.is_available():
|
|
|
|
|
|
|
|
raise Exception("ODBC连接不可用,无法执行操作")
|
|
|
|
|
|
|
|
|
|
|
|
with self.get_connection() as conn:
|
|
|
|
with self.get_connection() as conn:
|
|
|
|
cursor = conn.cursor()
|
|
|
|
cursor = conn.cursor()
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
|