前面一篇博客聊聊数据权限哪些事儿介绍了数据权限配置文件和数据权限的几个示例,这篇文章来详细介绍下tiny版本的数据权限是如何设计的?
Tiny版本的数据权限是基于jdbc底层实现的,可以把它理解为jdbc驱动的增强版实现,可以完全替代底层数据库的驱动。它是轻量级框架,以jar包形式提供服务,无proxy代理层,无需额外部署,只需依赖底层数据库的驱动,而且DBA也无需改变原有的运维方式。
它有以下特点:
(1)、无侵入性,可以在任何java环境下运行
(2)、支持jdk1.5到1.8版本环境下运行
(3)、跨数据库,实现不跟具体底层数据库绑定,可以支持任何第三方数据库
(4)、适用于任何第三方数据库连接池如:DBCP, C3P0, BoneCP, Druid等
(5)、适用于任何基于java的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
(6)、对程序员透明,数据权限相关代码不需要进行硬编码
(7)、与不同的用户权限能较好集成。
主要提供以下功能:
(1)、数据行权限控制–让不同的人看到不同的数据行的权限控制
(2)、数据列权限控制–对使用者限制其访问某些列中的数据的权限控制。
下面这个图是tinydac的整体架构图。
从架构图可以看出,tinydac内部有以下几部分组成,数据权限配置、JDBC接口规范实现、SQL解析层、数据权限匹配处理层、以及SQL执行层。下面将详细这些层次是如何设计的?
在介绍各层次如何设计之前,我们先来介绍下tinydac的基础概念
中文名称 | 英文名称 | 说明 |
数据权限配置 | DataAccessControl | 数据权限配置节点对象,里面可以包含多个权限规则配置,以及物理数据源配置信息 |
物理连接配置 | OriginalConnectionGetter | 配置物理数据源相关信息,可以支持datasource和jndi方式 |
表规则 | TableRule | 表规则配置,下面可以定义增删改查的规则配置 |
新增规则 | InsertRule | 新增操作的规则配置,支持值检查规则、字段过滤规则 |
更新规则 | UpdateRule | 更新操作的规则配置,支持值检查规则、字段过滤规则、附加条件规则 |
删除规则 | DeleteRule | 删除操作的规则配置,支持值检查规则、附加条件规则 |
查询规则 | SelectRule | 查询操作的规则配置,支持值检查规则、字段过滤规则、附加条件规则 |
值检查规则 | ValueCheckRule | 值检查规则配置信息 |
字段过滤规则 | FilterRule | 字段过滤规则配置 |
附加条件规则 | ConditionRule | 附加条件规则配置 |
数据权限上下文 | DataAccessControlContext | 数据权限上下文接口,可以加载框架属性配置信息、设置全局上下文参数与基于线程上下文的参数 |
SQL解析接口 | SQLParser | SQL解析接口,解析SQL,解析结果信息由SqlParserResult封装 |
SQL解析结果集 | SqlParserResult | SQL解析结果集对象 |
SQL解析工厂 | SQLParserFactory | 创建SQL解析实例的工厂 |
SQL解析缓存 | ParserCache | 存储SQL解析过程中创建的访问者对象 |
表格规则匹配器 | TableRuleMatcher | 根据解析sql的表名获取表名相关的表规则信息 |
增删改查操作类型匹配器 | SqlTypeMatcher | 用于匹配增删改查操作类型 |
SQL操作规则处理接口 | SqlRuleProcessor | 增删改查SQL操作规则处理的抽象接口 |
值检查规则处理接口 | ValueCheckRuleProcessor | 定义值检查规则处理接口方法 |
字段过滤规则处理接口 | OperationFieldFilterRuleProcessor | 定义字段过滤规则处理接口方法 |
字段过滤处理接口 | FieldFilterProcessor | 定义字段过滤处理接口方法 |
字段过滤处理接口的工厂 | FieldFilterProcessorFactory | 创建字段过滤处理接口的实例 |
附加条件规则处理接口 | AppendConditionRuleProcessor | 定义附加条件规则处理接口 |
附加条件别名处理接口 | AliasConditionProcessor | 附加条件处理时,原生sql存在别名,需要给附加条件加上原生SQL中的表别名信息 |
附加条件别名处理接口的工厂 | ConditionProcessorFactory | 创建附加条件别名处理接口的实例 |
新增操作规则处理接口 | InsertRuleProcessor | 提供了值检查规则处理、字段过滤规则处理 |
删除操作规则处理接口 | DeleteRuleProcessor | 提供了值检查规则处理、附加条件规则处理 |
更新操作规则处理接口 | UpdateRuleProcessor | 提供值检查规则处理、字段过滤规则处理、附加条件规则处理 |
查询操作规则处理接口 | SelectRuleProcessor | 提供值检查规则处理、字段过滤规则处理、附加条件规则处理 |
表元数据获取接口 | TableMetaDataProvider | 获取表相关的数据库元数据信息,包括表信息以及表中定义的列信息 |
表达式执行的引擎接口 | ExpressionEngine | 定义表达式执行的接口方法,内部默认实现是基于groovy语言的表达式引擎 |
创建表达式执行引擎的接口 | ExpressionEngineFactory | 创建表达式执行引擎实例的工厂 |
SQL执行上下文参数对象 | StatementTableActionContext | SQL执行上下文,存放sql语句相关的参数上下文以及SQL解析相关的信息 |
数据权限配置管理器 | DataAccessControlManager | 数据权限配置信息的管理对象 |
PreparedStatement参数处理器 | ParameterHandler | 定义PreparedStatement参数信息的处理器 |
PreparedStatement参数上下文 | ParameterContext | PreparedStatement参数上下文 |
PreparedStatement参数设置方法的枚举 | ParameterMethod | PreparedStatement参数设置方法的枚举 |
值转换接口 | ValueConverter | 用于ResultSet.getObject方法,把设置的字符串默认参数值转换成列对应的实际类型。 |
存储当前执行SQL的对象 | ThreadLocalSqlHolder | 在线程上下文中存储当前执行的SQL语句 |
数据权限配置文件序列化生成的对象是DataAccessControls,数据权限配置文件内容示例:
<data-access-controls> <data-access-control id="selDataAccessControl"> <connection driver="org.h2.Driver" user-name="sa" password="123456" url="jdbc:h2:./dbfile/dac_simple;mode=MYSQL;DB_CLOSE_ON_EXIT=FALSE" class="org.tinygroup.dac.config.DataSourceMode"> </connection> <access-control-rule> <table-rules> <table-rule table-name="custom"> <select-rule> <condition-rules> <condition-rule> <expression> <![CDATA[ #s_age#!=null ]]> </expression> <append-conditions> <append-condition> <condition> <![CDATA[ "age>"+#s_age# ]]> </condition> </append-condition> </append-conditions> </condition-rule> </condition-rules> <filter-rules> <filter-rule> <expression> <![CDATA[ #age#<20 ]]> </expression> <filter-columns> <filter-column column-name="age" value="20"></filter-column> </filter-columns> </filter-rule> </filter-rules> <value-check-rules> <value-check-rule column-name="name"> <expression> <![CDATA[ #name#.contains("sds") ]]> </expression> </value-check-rule> </value-check-rules> </select-rule> </table-rule> </table-rules> </access-control-rule> </data-access-control></data-access-controls>
配置文件说明
1.data-access-controls
对应的配置对象DataAccessControls,作为配置文件的根节点出现,节点的所有内容会组织成DataAccessControls对象,可以包含多个数据权限配置
2.data-access-control
数据权限配置节点,对应的配置对象DataAccessControl。
id:数据权限配置的唯一标识,在jdbc url参数中体现,例如:url=jdbc:dataaccesscontrol://selDataAccessControl,selDataAccessControl就是数据权限配置id
connection:获取物理数据库连接的配置节点,节点内容对应的配置对象OriginalConnectionGetter,TinyDAC支持两种方式获取物理数据库连接。
(1)、DataSourceMode:数据库连接池方式获取connection,上面的配置示例就是DataSourceMode方式
user-name:数据库用户名称,password:数据库密码,url:物理数据库的url,driver:数据库驱动
(2)、JndiMode:通过jndi方式获取connection
配置示例:
<connection jndi-name="test0" env-file="jndi.properties" class="org.tinygroup.dac.config.JndiMode"></connection>
jndi-name:绑定在jndi的名称,env-file:加载到jndi上下文的属性信息
3.access-control-rule
数据权限规则的节点,对应的配置对象AccessControlRule,节点下面可以定义多个基于表的数据权限规则配置
4.table-rule
数据权限表规则节点,对应的配置对象TableRule,节点下可以定义增删改查sql操作的配置
5.insert-rule
插件语句的规则配置节点,对应的配置对象InsertRule,新增操作支持值检查规则、字段过滤规则
6.delete-rule
删除语句的规则配置节点,对应的配置对象DeleteRule,删除操作支持值检查规则、附加条件规则
7.update-rule
更新语句的规则配置节点,对应的配置对象UpdateRule,更新操作支持值检查规则,附加条件规则,字段过滤规则
8.select-rule
查询语句的规则配置节点,对象的配置对象SelectRule,查询操作支持值检查规则,附加条件规则,字段过滤规则
9.value-check-rule
值检查规则配置节点,对象的配置对象ValueCheckRule
column-name:指定要检查的列
expression:值检查规则的表达式,需要是一个返回boolean类型的表达式
10.filter-rule
字段过滤规则配置节点,对应的配置对象FilterRule
expression:字段过滤规则的表达式,也是一个返回boolean类型的表达式,只有返回值是true,才会进行下一步字段过滤操作
filter-columns:定义所有需要过滤的字段信息
filter-column:定义过滤字段信息,column-name:指定需要过滤的字段名称,value:设置默认值,在查询语句中有用,查询项被过滤可以返回这个默认值。
11.condition-rule
附加条件规则配置节点,对应的配置对象ConditionRule
expression:附加条件规则的表达式,也是一个返回boolean类型的表达式,只有返回值是true,才会附加下面的查询条件
append-conditions:定义所有附加条件的节点
append-condition:定义附加条件的节点,condition:指定要附加的条件
DataAccessControlContext是数据权限上下文接口,其接口定义如下:
public interface DataAccessControlContext { /** * */ public Object getValue(String key); /** * */ public void setValue(String key, Object value); /** * 以map的形式返回所有上下文参数 * @return */ public Map<String, Object> getContextValueMap(); /** * 清除所有参数信息,包括线程上下文中的参数信息 */ public void clearAllValues(); /** * 设置绑定在线程中的上下文信息 * @param context */ public void setThreadContext(Context context); /** * 获取绑定在线程中的上下文对象 * @return */ public Context getThreadContext(); /** * 往线程上下文中存放参数信息 * @param key * @param value */ public void setThreadContextValue(String key,Object value); /** * 获取属性值,所有内容读取自框架配置属性文件 * @param key * @return */ public String getProperty(String key); /** * 设置属性值 */ public void setProperty(String key, String value); /** * 移除值属性 */ public void removeProperty(String key); /** * 返回所有属性 */ public Map<String, String> getProperties(); }
通过上下文接口,可以读取设置框架级别的属性配置信息,读取设置全局的参数信息,以及读取设置基于线程上下文的参数信息
框架级别的属性配置分为两种:
1、定义在TinyDAC内部的框架默认配置文件---default.tinydac.properties
2、定义在客户工程中的属性配置文件—tinydac.properties,当然这个属性配置文件名称不一定命名为tinydac.properties,配置文件名称可以由System对象org.tinygroup.dac.properties的属性值指定
如果两个属性文件中存在相同key,以客户定义的属性值为准,这样可以覆盖框架默认的特性。
下面是TinyDAC内部的框架默认配置文件介绍:
key | 默认值 | 说明 |
org.tinygroup.dac.update.sql.enable | true | 是否开启更新操作规则处理,默认为true |
org.tinygroup.dac.update.rule.processor | org.tinygroup.dac.processor.impl.DefaultUpdateRuleProcessor | 更新操作规则处理器的实现类,默认实现DefaultUpdateRuleProcessor |
org.tinygroup.dac.thread.sql.enable | true | 是否把当前执行的sql存放到线程上下文中,默认为true |
org.tinygroup.dac.sql.parser.factory | org.tinygroup.dac.parser.impl.DefaultSQLParserFactory | 指定创建SQLParser实例的工厂,默认的实现是DefaultSQLParserFactory |
org.tinygroup.dac.select.sql.enable | true | 是否开启查询操作规则处理,默认为true |
org.tinygroup.dac.select.rule.processor | org.tinygroup.dac.processor.impl.DefaultSelectRuleProcessor | 查询操作规则处理器的实现类,默认实现DefaultSelectRuleProcessor |
org.tinygroup.dac.insert.sql.enable | true | 是否开启插入操作规则处理,默认为true |
org.tinygroup.dac.insert.rule.processor | org.tinygroup.dac.processor.impl.DefaultInsertRuleProcessor | 新增操作规则处理器的实现类,默认实现DefaultInsertRuleProcessor |
org.tinygroup.dac.field.filter.processor.factory | org.tinygroup.dac.processor.fieldfilter.impl.JsqlParserFieldFilterProcessorFactory | 字段过滤处理工厂类的类路径 |
org.tinygroup.dac.expression.engine.factory | org.tinygroup.dac.engine.impl.GroovyExpressionEngineFactory | 表达式引擎工厂实现类的类路径 |
org.tinygroup.dac.delete.sql.enable | true | 是否开启删除操作规则处理,默认为true |
org.tinygroup.dac.delete.rule.processor | org.tinygroup.dac.processor.impl.DefaultDeleteRuleProcessor | 删除操作规则处理器的实现类,默认实现DefaultDeleteRuleProcessor |
org.tinygroup.dac.date.format | yyyy-MM-dd HH:mm:ss | 日期格式,用于查询项值从字符类型转换为日期类型。默认为“yyyy-MM-dd HH:mm:ss” |
org.tinygroup.dac.condition.processor.factory | org.tinygroup.dac.processor.appendcondition.impl.AliasFunctionConditionProcessorFactory | 附加条件别名处理工厂类的类路径 |
org.tinygroup.dac.cache.timeout | 60 | 缓存的超时时间 |
org.tinygroup.dac.cache.size | 5000 | 缓存存储内容的最大数 |
public Object getValue(String key);public void setValue(String key, Object value);
/** * 以map的形式返回所有上下文参数 * @return */public Map<String, Object> getContextValueMap();/** * 清除所有参数信息,包括线程上下文中的参数信息 */public void clearAllValues();/** * 设置绑定在线程中的上下文信息 * @param context */public void setThreadContext(Context context);/** * 获取绑定在线程中的上下文对象 * @return */public Context getThreadContext();/** * 往线程上下文中存放参数信息 * @param key * @param value */public void setThreadContextValue(String key,Object value);
DataAccessControlManager是数据权限配置管理器接口类,主要用于加载、读取、删除数据权限配置。接口定义如下。
public interface DataAccessControlManager { /** * 新增数据权限配置 * @param dataAccessControls */ public void addDataAccessControls(DataAccessControls dataAccessControls); /** * 移除数据权限配置 * @param dataAccessControls */ public void removeDataAccessControls(DataAccessControls dataAccessControls); /** * 根据数据权限id获取数据权限配置 * @param rightId * @return */ public DataAccessControl getDataAccessControls(String rightId); /** * 新增数据权限配置,location可以是相对于classpath的路径也可以是文件路径,也可以是url路径 * @param location */ public void addDataAccessControls(String location); /** * 新增数据权限配置 * @param inputStream 数据权限配置的流 */ public void addDataAccessControls(InputStream inputStream); }
DataAccessControlManagerImpl是框架提供的数据权限配置管理器的默认实现。
SQLParser 是SQL解析接口类,SQL解析处理接口用来解析SQL,组装SQL解析结果集,接口定义如下:
/** * SQL解析接口 * */public interface SQLParser { /** * 解析sql语句 * @param sql * @return */ SqlParserResult parse(String sql);}
SQL解析接口类关系图如下:
类名 | 说明 |
SQLParserFactory | 创建SQL解析器实例的工厂接口 |
DefaultSQLParserFactory | 框架提供的创建SQL解析器实例的工厂实现类 |
SQLParser | SQL解析器接口 |
DefaultSQLParser | 框架提供的SQL解析器接口实现类,内部提供jsqlparser进行SQL解析 |
SqlParserResult | SQL解析结果集接口 |
DefaultSelectParserResult | sql可以解析支持的解析结果集 |
SqlNotSupportParserResult | sql不支持解析的解析结果集 |
可以进行SQL解析的sql,会把解析结果组装成DefaultSelectParserResult对象,然后根据这个结果集对象进行数据权限处理,如果传人的sql不能进行解析,会跳过数据权限处理,直接把传人的SQL丢到底层数据库去执行。
数据权限规则处理接口对SQL操作进行数据权限控制,其数据权限规则处理类关系图如下:
类名 | 说明 |
SqlTypeMatcher | 匹配SQL操作类型的匹配接口 |
SqlRuleProcessor | SQL规则处理的统一接口 |
ValueCheckRuleProcessor | 值检查规则处理接口 |
OperationFieldFilterRuleProcessor | 字段过滤规则处理接口 |
AppendConditionRuleProcessor | 附加条件规则处理接口 |
InsertRuleProcessor | 新增操作规则处理接口,支持值检查、字段过滤规则处理 |
DeleteRuleProcessor | 删除操作规则处理接口,支持值检查、附加条件规则处理 |
UpdateRuleProcessor | 更新操作规则处理接口,支持值检查、附加条件、字段过滤规则处理 |
SelectRuleProcessor | 查询操作规则处理接口,支持值检查、附加条件、字段过滤规则处理 |
ExpressionEngine是表达式引擎处理接口,表达式引擎是用来渲染SQL权限规则中出现的表达式,渲染表达式的返回值,可以是boolean或者是String类型。其接口定义如下:
/** * 表达式执行引擎接口 * @author renhui * */public interface ExpressionEngine { /** * 执行表达式,返回boolean * @param expression 表达式 * @param params 参数信息 * @return 渲染结果为boolean */ boolean evalBoolean(String expression,Map<String, Object> params); /** * 执行表达式,渲染结果为String * @param expression 表达式 * @param params 参数 * @return 渲染结果为字符串 */ String evalString(String expression,Map<String, Object> params);}
表达式引擎类关系图:
异常号 | 异常信息 |
异常号 | 异常信息 |
0TE120120001 | SQL:[{0}],检查的列:[{1}],expression:[{2}],表达式的返回结果是false,列检查不通过 |
0TE120120002 | condition-rule:[{0}]中附加的条件表达式为空 |
0TE120120003 | 数据权限配置:[{0}]中不存在connection节点配置信息 |
0TE120120004 | sql:[{0}]中,不存在columnName:[{1}]的操作信息,请检查过滤规则配置 |
0TE120120005 | SQL语句有误,找不到列:[{0}]所在的表名 |
0TE120120006 | Connection was null |
0TE120120007 | Connection:[{0}]获取的DatabaseMetaData对象为空 |
0TE120120008 | 找不到框架默认配置文件:[{0}] |
0TE120120009 | 加载配置文件:[{0}]出错 |
0TE120120010 | 类:{0}与{1}类型不匹配 |
0TE120120011 | 加载框架属性文件过程中出现错误 |
0TE120120012 | 原生SQL:{0},由于数据权限过滤规则处理所有操作字段都被过滤,生成的sql是无效的,此次sql操作将被忽略 |
0TE120120013 | 打开文件:{0}出错 |
0TE120120014 | 打开网址:{0}错误 |
0TE120120015 | SQL:[{0}]暂且不支持,交由底层数据库进行处理 |
0TE120120016 | 从缓存中获取sql解析结果集出错 |
0TE120120017 | 获取jndiName:[{0}]对象出现异常 |
0TE120120018 | expression:[{0}]的返回值类型出错,要求返回值的类型是boolean类型,实际类型:[{1}] |
0TE120120019 | expression:[{0}]的返回值类型出错,要求返回值的类型是String类型,实际类型:[{1}] |
0TE120120020 | SQL片段:[{0}]不支持处理 |
0TE120120021 | 附加条件:[{0}],进行别名处理过程出错 |
0TE120120022 | 附加条件:[{0}],获取的AliasSettingExpressionVisitor对象为空 |
TinyDAC是在JDBC层实现的数据权限框架,它可以看做是对数据库驱动的扩展,按照JDBC规范进行实现的。
DataAccessControlDriver直接实现了java.sql.Driver接口,通过重写connect(String url, Properties info) 方法来获取数据权限数据库连接。url参数规范:“jdbc:dataaccesscontrol://”+数据权限配置id,connect方法返回DataAccessControlConnection类型的数据库连接。
DataAccessControlConnection内部管理真正的物理数据库连接,其物理数据库连接的配置就是数据权限配置的connection节点,创建物理数据库连接方式有2种,一种是通过datasource方式创建连接,另一种是通过jndi方式创建连接。DataAccessControlConnection大部分实现方法是直接委托内部管理的connection来实现的,主要是重写了创建Statement和PreparedStatement接口实例的方法。
下面是创建Statement和PreparedStatement接口实例的接口方法列表说明
创建Statement的接口方法
接口方法 | 说明 |
createStatement() | 使用返回的 Statement 对象创建的结果集在默认情况下类型为 TYPE_FORWARD_ONLY,并带有 CONCUR_READ_ONLY 并发级别 |
createStatement(int resultSetType, int resultSetConcurrency) | 创建一个 Statement 对象,该对象将生成具有给定类型和并发性的 ResultSet 对象。此方法与上述 createStatement 方法相同,但它允许重写默认结果集类型和并发性。 |
createStatement(int resultSetType,int resultSetConcurrency, int resultSetHoldability) | 创建一个 Statement 对象,该对象将生成具有给定类型、并发性和可保存性的 ResultSet 对象。此方法与上述 createStatement方法相同,但它允许重写默认结果集类型、并发性和可保存性。 |
创建PreparedStatement接口方法
接口方法 | 说明 |
接口方法 | 说明 |
prepareStatement(String sql) | 创建一个 PreparedStatement 对象来将参数化的 SQL 语句发送到数据库,返回的 PreparedStatement 对象创建的结果集在默认情况下类型为TYPE_FORWARD_ONLY,并带有 CONCUR_READ_ONLY 并发级别 |
prepareStatement(String sql, int resultSetType, | 创建一个 PreparedStatement 对象,该对象将生成具有给定类型和并发性的 ResultSet 对象。此方法与上述 prepareStatement 方法相同,但它允许重写默认结果集类型和并发性。 |
prepareStatement(String sql, int resultSetType, | 创建一个 PreparedStatement 对象,该对象将生成具有给定类型和并发性的 ResultSet 对象。此方法与上述 prepareStatement 方法相同,但它允许重写默认结果集类型和并发性。 |
prepareStatement(String sql, int autoGeneratedKeys) | 创建一个默认 PreparedStatement 对象,该对象能检索自动生成的键。给定常量告知驱动程序是否应该使自动生成的键可用于检索。如果该 SQL 语句不是一条 INSERT 语句,则忽略此参数。 |
prepareStatement(String sql, int[] columnIndexes) | 创建一个能够返回由给定数组指定的自动生成键的默认 PreparedStatement 对象。此数组包含目标表中的列的索引,而该目标表包含应该使其可用的自动生成的键。如果该 SQL 语句不是一条 INSERT 语句,则忽略此数组。 |
prepareStatement(String sql, String[] columnNames) | 创建一个能够返回由给定数组指定的自动生成键的默认 PreparedStatement 对象。此数组包含目标表中的列的索引,而该目标表包含应该使其可用的自动生成的键。如果该 SQL 语句不是一条 INSERT 语句,则忽略此数组。 |
java.sql.Statement接口的实现DataAccessControlStatement,它是数据权限框架的核心类,数据权限控制功能通过此类完成的。先来看看DataAccessControlStatement的类关系图:
从类图可以看出,DataAccessControlStatement通过SQLParser接口进行SQL解析,返回解析结果集对象,SQL解析结果集会告知此次SQL操作语句是那种类型(增删改查SQL类型),然后交给对应的SQL操作数据权限规则处理器进行数据权限控制。例如此次SQL语句是新增语句,那么DataAccessControlStatement内部会交给InsertRuleProcessor进行新增SQL数据权限规则处理。
DataAccessControlPreparedStatement的类关系图
DataAccessControlPreparedStatement继承了DataAccessControlStatement,并实现了PreparedStatement接口,用于执行预编译的SQL。DataAccessControlPreparedStatement采用ParameterContext来存储预编译的参数,先看看ParameterContext的类代码
public class ParameterContext { private ParameterMethod parameterMethod; /** * args[0]: parameterIndex args[1]: 参数值 args[2]: length * 适用于:setAsciiStream、setBinaryStream、setCharacterStream、setUnicodeStream * 。。。 * */ private Object[] args; public ParameterContext() { } public ParameterContext(ParameterMethod parameterMethod, Object[] args) { this.parameterMethod = parameterMethod; this.args = args; } public ParameterMethod getParameterMethod() { return parameterMethod; } public void setParameterMethod(ParameterMethod parameterMethod) { this.parameterMethod = parameterMethod; } public Object[] getArgs() { return args; } public void setArgs(Object[] args) { this.args = args; } public ParameterContext copyParameterContext(){ ParameterContext newParameterContext=new ParameterContext(); newParameterContext.setParameterMethod(parameterMethod); Object[] newArgs=new Object[args.length]; System.arraycopy(args, 0, newArgs, 0, args.length); newParameterContext.setArgs(newArgs); return newParameterContext; } public String toString() { StringBuilder buffer = new StringBuilder(); buffer.append(parameterMethod).append("("); for (int i = 0; i < args.length; ++i) { buffer.append(args[i]); if (i != args.length - 1) { buffer.append(", "); } } buffer.append(")"); return buffer.toString(); } @Override public int hashCode() { return HashCodeUtil.reflectionHashCode(this); } @Override public boolean equals(Object obj) { return EqualsUtil.reflectionEquals(this, obj); } /** * 重新设置参数值 * @param value */ public void setVaule(Object value) { args[1] = value; } /** * 重新设置参数位置 * @param index */ public void setParameterIndex(int index){ args[0]=index; }}
args由设置预编译参数接口方法传递的参数值组成的对象数组。ParameterMethod 是一个枚举类,是根据PreparedStatement设置预编译参数接口方法来定义的,可代码如下:
public enum ParameterMethod { setArray, setAsciiStream,setAsciiStream1,setAsciiStream2, setBigDecimal, setBinaryStream,setBinaryStream1,setBinaryStream2,setBlob,setBlob1,setBlob2,setBoolean,setByte,setBytes, setCharacterStream,setCharacterStream1,setCharacterStream2, setClob,setClob1,setClob2, setDate1, setDate2, setDouble, setFloat, setInt, setLong, setNull1, setNull2, setObject1, setObject2, setObject3, setRef, setShort, setString, setTime1, setTime2, setTimestamp1, setTimestamp2, setURL, setUnicodeStream, setNCharacterStream, setNCharacterStream1, setNClob,setNClob1,setNClob2, setSQLXML, setRowId, setNString,setSQLType,setSQLType1}
例如设置int类型的参数值
public void setInt(int parameterIndex, int x) throws SQLException { parameterContextList.add(new ParameterContext(ParameterMethod.setInt, new Object[] { parameterIndex, x }));}
通过parameterContextList来收集客户端设置的预编译参数信息,那么它如何设置到底层数据库创建的PreparedStament对象中呢?
DataAccessControlPreparedStatement内部定义了
protected static Map<ParameterMethod, ParameterHandler> parameterHandlers = ParameterUtil.getParameterHandlerMap();
注册了所有设置预编译参数方法的参数处理器。ParameterHandler就是给底层数据库PreparedStatement实例设置预编译参数的,其接口定义如下:
public interface ParameterHandler { /** * * * @param stmt * @param args * @throws SQLException */ void setParameter(PreparedStatement stmt, Object[] args) throws SQLException;}
框架提供了很多接口实现,还是以设置int类型参数为例:SetIntHandler
public class SetIntHandler implements ParameterHandler { public void setParameter(PreparedStatement stmt, Object[] args) throws SQLException { stmt.setInt((Integer) args[0], (Integer) args[1]); }}
这样就可以把DataAccessControlPreparedStatement对象收集的预编译参数信息设置到底层数据库创建的PreparedStatement实例中。
DataAccessControlResultSet是对java.sql.ResultSet接口的实现,其内部大部分实现都是委托底层数据库创建的ResultSet的方法。查询显示项的过滤功能就是通过DataAccessControlResultSet来完成的,在DataAccessControlResultSet内部重写了获取查询项信息的方法。可以给过滤的查询项设置默认值,我们知道通过ResultSet接口可以获取查询项的值,如果通过getObject方法来获取查询项的值,就要涉及类型转换的问题,如果某个字段被过滤,设置在配置文件的值都是字符串类型,我们就需要设计一个可以把字符串转换成实际类型的类型转换接口。ValueConverter接口就是干这个用的,接口代码如下:
public interface ValueConverter<T> { T convert(String defaultValue); }
框架内部定义了多个值转换接口的实现,例如:IntValueConverter、BooleanValueConverter、DateValueConverter等。
我们知道不同jdk版本,其JDBC定义的接口规范是不一样的,例如jdk5定义的是JDBC3的规范,jdk6定义的是JDBC4的规范,jdk7定义的是JDBC41的规范,jdk8定义的是JDBC42的规范。 TinyDAC是可以支持jdk5~jdk8所有JDBC规范。
框架内部提供maven-ant插件配置多个jdk对源代码进行编译,所有非JDBC4开头的类是用jdk5进行编译的,以JDBC4开头的类用jdk6进行编译的,以JDBC42开头的类用jdk8进行编译的。
好了,关于数据权限框架设计思路大致介绍到此,如果有开发人员对本框架感兴趣,想做一些扩展开发,可以联系本人。
如果您对我的博客感兴趣,请点击左上角的关注,以便及时收到我的相关通知。
联系客服