利用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))
    """学号"""
StudentEntity

下面是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)
DbFactory

最后是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

 

非常简单的实现,仅供娱乐。

posted @ 2020-11-04 22:50  奥卡修罗  阅读(565)  评论(0编辑  收藏  举报