优化SqlFactory,加入对象缓存

master
old-tom 2 years ago
parent 25f99025ea
commit 82d2b2c3d4

@ -7,7 +7,6 @@
# @Desc : # @Desc :
import os import os
from pydantic import BaseModel from pydantic import BaseModel
import toml import toml
# 默认配置文件名 # 默认配置文件名

@ -6,13 +6,17 @@
# @Project : futool-db-lite # @Project : futool-db-lite
# @Desc : 数据库测试 # @Desc : 数据库测试
from session.engine.dbengine import DatabaseEngine from session.engine.dbengine import DatabaseEngine
from session.engine.dbengine import container
if __name__ == '__main__': if __name__ == '__main__':
db_engine = DatabaseEngine('testdb') db_engine = DatabaseEngine('testdb')
db_engine2 = DatabaseEngine('testdb')
print(db_engine.engine is db_engine2.engine)
session_factory = db_engine.create_session_factory() session_factory = db_engine.create_session_factory()
sql_session = session_factory.open_session() session_factory2 = db_engine2.create_session_factory()
# print(sql_session.select_all('select * from metadata_object')) print(session_factory is session_factory2)
sql_session.select_one('select * from metadata_object') # for i in range(100):
sql_session.select_many('select * from metadata_object', 2) # # sess = session_factory.create_session()
# print(sql_session.delete('delete from metadata_object where meta_id=1657707190871527425')) # sess = session_factory.open_session()
sql_session.close() # print(id(sess))
print(container.engines, container.factory)

@ -9,6 +9,7 @@ from urllib.parse import quote_plus as urlquote
from sqlalchemy import create_engine from sqlalchemy import create_engine
from config.db_config import DbConfigLoader, DEFAULT_CONF_PATH from config.db_config import DbConfigLoader, DEFAULT_CONF_PATH
from session.session import SqlsessionFactory from session.session import SqlsessionFactory
from util.futool_lang import str_md5
class DatabaseEngineError(Exception): class DatabaseEngineError(Exception):
@ -16,6 +17,32 @@ class DatabaseEngineError(Exception):
Exception.__init__(self, msg) Exception.__init__(self, msg)
class ObjContainer(object):
# DatabaseEngine容器
engines = {}
# SqlsessionFactory容器
factory = {}
def has_obj(self, attr, k):
return k in getattr(self, attr)
def get_obj(self, attr, k):
return getattr(self, attr)[k]
def put_obj(self, attr, k, v):
getattr(self, attr)[k] = v
def remove_obj(self, attr, k):
del getattr(self, attr)[k]
def clean(self):
self.engines = {}
self.factory = {}
container = ObjContainer()
class DatabaseEngine(object): class DatabaseEngine(object):
""" """
数据库连接 数据库连接
@ -28,6 +55,8 @@ class DatabaseEngine(object):
'oracle': 'oracle+cx_oracle://{0}:{1}@{2}:{3}/?service_name={4}' 'oracle': 'oracle+cx_oracle://{0}:{1}@{2}:{3}/?service_name={4}'
} }
attr = 'engines'
def __init__(self, db_name, conf_path=DEFAULT_CONF_PATH): def __init__(self, db_name, conf_path=DEFAULT_CONF_PATH):
""" """
pool_size:连接池大小 pool_size:连接池大小
@ -42,12 +71,28 @@ class DatabaseEngine(object):
raise DatabaseEngineError(msg='不支持的数据库类型') raise DatabaseEngineError(msg='不支持的数据库类型')
# urlquote 处理密码中的特殊字符 # urlquote 处理密码中的特殊字符
url = self.DB_URL[conf.dialect].format(conf.user, urlquote(conf.passwd), conf.host, conf.port, conf.database) url = self.DB_URL[conf.dialect].format(conf.user, urlquote(conf.passwd), conf.host, conf.port, conf.database)
self.engine = create_engine(url, pool_size=conf.pool_size, pool_recycle=conf.pool_recycle, pool_pre_ping=True, # URL生成唯一ID
echo=conf.show_sql) self.engine_id = str_md5(url)
# 根据数据源ID判断是否新建连接池
if container.has_obj(self.attr, self.engine_id):
self.engine = container.get_obj(self.attr, self.engine_id)
else:
engine = create_engine(url, pool_size=conf.pool_size, pool_recycle=conf.pool_recycle, pool_pre_ping=True,
echo=conf.show_sql)
container.put_obj(self.attr, self.engine_id, engine)
self.engine = engine
def create_session_factory(self): def create_session_factory(self):
""" """
创建session工厂 创建session工厂
相同数据库URL下全局只有一个SqlsessionFactory,除非有其他数据源
根据engine_id判断是否新建对象
:return: :return:
""" """
return SqlsessionFactory(self.engine) attr = 'factory'
if container.has_obj(attr, self.engine_id):
return container.get_obj(attr, self.engine_id)
else:
factory = SqlsessionFactory(self.engine)
container.put_obj(attr, self.engine_id, factory)
return factory

@ -5,6 +5,7 @@
# @File : session.py # @File : session.py
# @Project : futool-db-lite # @Project : futool-db-lite
# @Desc : # @Desc :
import threading
from sqlalchemy import Engine from sqlalchemy import Engine
from executor.sql_executor import SQLExecutor from executor.sql_executor import SQLExecutor
from transaction.connect_transaction import TransactionFactory from transaction.connect_transaction import TransactionFactory
@ -15,6 +16,38 @@ class CreateSessionError(Exception):
Exception.__init__(self, msg) Exception.__init__(self, msg)
class SqlSessionCache(object):
"""
利用thread-local 实现线程隔离及单个线程获取到的session一致
"""
def __init__(self, create_session_func):
self.create_session_func = create_session_func
self.cache = threading.local()
def __call__(self):
try:
if self.has_session():
return self.cache.value
else:
val = self.cache.value = self.create_session_func()
return val
except Exception as e:
raise CreateSessionError(msg=f'从cache获取session异常,e={e}')
def put_session(self, session):
self.cache.value = session
def has_session(self) -> bool:
return hasattr(self.cache, "value")
def remove_session(self):
try:
del self.cache.value
except AttributeError:
pass
class SqlsessionFactory(object): class SqlsessionFactory(object):
""" """
工厂模式创建sqlSession 工厂模式创建sqlSession
@ -22,21 +55,29 @@ class SqlsessionFactory(object):
def __init__(self, engine: Engine): def __init__(self, engine: Engine):
self._engine = engine self._engine = engine
self.cache = SqlSessionCache(self.create_session)
def open_session(self): def open_session(self):
"""
从缓存获取,如果没有会自动调用create_session创建
"""
return self.cache()
def create_session(self):
try: try:
conn = self._engine.connect() conn = self._engine.connect()
tx_factory = TransactionFactory(conn) tx_factory = TransactionFactory(conn)
tx = tx_factory.create_transaction() tx = tx_factory.create_transaction()
executor = SQLExecutor(tx) executor = SQLExecutor(tx)
return Sqlsession(executor) return Sqlsession(executor, self.cache)
except Exception as e: except Exception as e:
raise CreateSessionError(msg=f'创建sqlSession异常,e={e}') raise CreateSessionError(msg=f'创建sqlSession异常,e={e}')
class Sqlsession(object): class Sqlsession(object):
def __init__(self, executor: SQLExecutor): def __init__(self, executor: SQLExecutor, session_cache: SqlSessionCache):
self.executor = executor self.executor = executor
self.session_cache = session_cache
def select_one(self, sql): def select_one(self, sql):
return self.executor.query(sql).fetchone() return self.executor.query(sql).fetchone()
@ -83,3 +124,4 @@ class Sqlsession(object):
:return: :return:
""" """
self.executor.get_connection().close() self.executor.get_connection().close()
self.session_cache.remove_session()

@ -0,0 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023/6/23 17:29
# @Author : old tom
# @File : __init__.py.py
# @Project : futool-db-0.1
# @Desc :

@ -0,0 +1,17 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023/6/23 17:30
# @Author : old tom
# @File : futool_lang.py
# @Project : futool-db-0.1
# @Desc :
import hashlib
def str_md5(content):
"""
字符串转MD5
"""
md5 = hashlib.md5()
md5.update(content.encode(encoding='utf-8'))
return md5.hexdigest()
Loading…
Cancel
Save