← 返回文章列表

oa-backend-framework项目架构设计 - 数据库层

分类:计算机/架构/oa-backend-framework项目架构设计

OA系统使用weaver.conn.RecordSet类和weaver.conn.RecordSetTrans类来执行数据库操作,其中RecordSet不支持事务,RecordSetTrans支持事务。这两个类的代码量非常大,在没有文档和注释的情况下,仅通过逆向分析,我们根本无法保证能正确地使用其特性。因此,为了更加可控,我们自己实现数据库层。

数据源管理器

DataSourceManager类负责初始化数据源,并从数据源获取数据库连接。启动应用时,会调用DataSourceManager执行初始化。

初始化数据源:我们使用Druid(在OA系统的三方库中)作为数据库连接池。读取OA系统的数据源配置文件weaver.properties,用其中的配置创建DruidDataSource实例,并保存在DataSourceManager的静态属性中。

获取数据库连接:从DruidDataSource实例上获取数据库连接。

线程的数据库连接

ConnectionHolder类是一个ThreadLocal容器,用于保存当前线程的数据库连接,确保一条线程的所有操作都在同一个连接中,即在同一个事务中。

ConnectionHolder提供获取连接、关闭连接、提交事务、回滚事务这4个方法。

数据库服务

应用没有专门的DAO层框架,直接用JDBC模式操作数据库又很繁琐,因此,我们以服务的形式提供通用的数据库操作功能。

DatabaseService类是通用的数据库服务类,封装了通用的查库和写库操作的方法。此服务可以通过IoC容器注入到其他类中,应用中所有的数据库操作最终都是通过调用DatabaseService来完成。

DatabaseService使用Apache Commons DBUtils(在OA系统的三方库中)的QueryRunner来执行SQL。执行时,总是从ConnectionHolder获取数据库连接。

DatabaseService类提供以下查库方法:

方法描述查询结果为0行查询结果为1行查询结果为多行
queryRowOrNull获取唯一行返回null返回唯一行抛出异常
queryRow获取唯一行抛出异常返回唯一行抛出异常
queryFirstRowOrNull获取第一行返回null返回第一行抛出异常
queryFirstRow获取第一行抛出异常返回第一行抛出异常
queryRows获取行列表返回行列表(空)返回行列表(1行)返回行列表(多行)

DatabaseService类提供以下写库方法:

所有查库和写库方法都支持参数String sql, Object... params,防止注入攻击。

通用的行数据结构

为了通用地表示DatabaseService查库方法查询到的任意结构的行数据,我们定义了Row类。

Row类是一个Map,类似于JavaScript的对象、Python的字典,可以表示任意的数据库行、实体、对象等数据结构。用Map表示对象,可以无需对属性做预定义,可以按需自由地增删属性,这带来了一些灵活性(例如:可以构造更新部分字段的更新数据对象,可以将一种对象修改为另一种对象,可以在字段增减时热更新程序),也降低了一些确定性(例如:IDE因为不知道有哪些属性而无法做代码提示和检查错误)。

Row类继承自LinkedHashMap<String, String>。考虑实际使用场景,我们对其做了一些约束。

键,即对象的属性名,是大小写无关的。通过重写Mapget()put()等方法,统一将键转换为小写形式,来实现大小写无关性。

值,即对象的属性值,存储为字符串。通过重写Mapput()等方法,实现无论给的值是何种类型,都统一转换为字符串存储,其中null会存储为空字符串。通过增加getInt()getBigDecimal()等方法,提供获取合适数据类型的功能。这种设计,屏蔽了数据类型的不确定性(例如:小数是Double还是BigDecimal),也使其不支持嵌套的对象。

对于Map具有的、但对对象来说意义不大的方法,如compute(),我们重写之并直接抛异常,以阻止其被使用。

应用中的各种业务数据类都继承自Row,或者直接使用RowDatabaseService的查库方法支持泛型,可以返回指定的业务数据类(RowRow的子类)。