利用SQLAlchemy和反射实现DAO的基类
在使用SQLAlchemy时,要根据每一个类的情况写对应增删改查,习惯了JAVA的虚类、反射,抽象出daobase类,如果python也有就会少很多重复代码,于是有了下面的简单实现。
注:我的Entity都是最简单的没有其他方法,只有属性,如以下例子:
Base = declarative_base() class StudentBasicInfo(Base): """ 学生基本信息类 表名: studentbasic 用于记录学生最基本的信息,在导入时清除以前数据,用新的数据替换。 """ __tablename__ = 'studentbasic' """表名""" id = Column(Integer, primary_key=True) studentName = Column(String(10)) """学生姓名,限10个字符串""" cardId = Column(String(18)) """身份证号,限18个字符串""" sYear = Column(Integer) """入学年度,纯数字""" sClass = Column(Integer) """班级,纯数字""" sex = Column(String(2)) """性别,字符串类型""" studyNo = Column(String(25)) """学号"""
下面是DbFactory用于连接数据库(本例为Sqlite3数据库),生成Session:
class DbFactory(object): """数据库连接工厂类""" def __init__(self): """ 初始化,类私有变量session和engine初始化为NONE,后调用init_db方法。 """ self.session = None self.engine = None self.init_db() def init_db(self, path="data.db"): """设置engine :param path 字符串,设置数据文件位置,默认tools目录下的data.db :return 无 """ self.engine = create_engine('sqlite:///' + path, echo=True) def get_session(self): """获得session,如果self.engine没有初始化,调用init_db方法,如果已经初始化检查self.session是否有值,没有获取,有则返回 :return: Session()实例,每个DbFactory的引用保持一个Session()实例。 """ if self.engine is None: self.init_db() Session = sessionmaker(bind=self.engine) if self.session is None: self.session = Session() return self.session def create_db(self): """ 初始化数据库表,用于正式使用软件以前初始化数据库。 :return 无 """ Base.metadata.create_all(self.engine)
最后是DaoBase实现:
class DaoBase(object, metaclass=abc.ABCMeta): """MVC中M层基类,在类中描述公共抽象方法""" def __init__(self): """初始化,引入DbFactory实例,并初始备用 :return 无 """ self.dbfactory = DbFactory() @abc.abstractmethod def get_entityclass(self): """抽象方法,返回DAO维护的实体类,用于查询语句 :return 类.class """ pass def find_by_id(self, ids): """用ID查找 :param ids 整数型 :return 类实例 """ return self.dbfactory.get_session().query(self.get_entityclass()).filter(self.get_entityclass().id == ids).one() def create(self, obj): """存贮类实例至数据库 :param obj 具体类实例 :return 无 """ self.dbfactory.get_session().add(obj) self.dbfactory.get_session().commit() def update(self, obj): """ 更新数据,根据obj的ID查询数据库中的数据 如果obj是利用session查询出来的,且和update使用同一个session,则没有变化中间的判断不会执行,这时已经修改过session中的数据直接commit 如果obj只是保留id,自行定义的一个实例而非在同一个session中查询出来,则遍历实例中的所有属性(属性非以_开头)循环中会产生不一致的数据, 利用setattr进行修改并更新,同样调用commit最终提交,所以下面这个commit必须执行。 :param obj 修改后的实例 :raise EntryNotFoundException 查询不到实例 :raise ObjectNotMatchException obj与数据库查询的类不是同一种类 :return 无 """ if isinstance(obj, self.get_entityclass()): try: obj_saved = self.find_by_id(obj.id) if obj_saved is None: raise EntryNotFoundException() for attribute_name in dir(obj_saved): if attribute_name[0:1] != "_" and \ getattr(obj, attribute_name) != getattr(obj_saved, attribute_name): setattr(obj_saved, attribute_name, getattr(obj, attribute_name)) self.__commit() except Exception as e: raise e else: raise ObjectNotMatchException() def __commit(self): self.dbfactory.get_session().commit() @abc.abstractmethod def delete(self, obj): pass
非常简单的实现,仅供娱乐。