一种面向HyperledgerFabric的SQL访问方法

文档序号:26004729发布日期:2021-07-23 21:22阅读:216来源:国知局
本发明涉及区块链
技术领域
:,特别是一种基于面向面向hyperledgerfabric的sql访问方法。
背景技术
::传统的数据库管理系统、nosql数据库管理系统都是由单一机构进行管理和维护,单一机构对所有数据拥有绝对的控制权,其它机构无法完整了解数据更新过程,因而无法完全信任数据库中的数据.所以,在多个机构协作模式下,中心化的数据库管理系统始终存在信任问题.以金融行业的清算和结算业务为例,传统中心化的数据库因无法解决多方互信问题,使得每个参与方都需要独立维护一套承载自己业务数据的数据库,这些数据库实际上是一座座信息孤岛,造成清结算过程耗费大量人工进行对账的情况,目前的清结算时间最快也需按天来计.如果存在一个多方参与者一致信任的数据库系统,则可显著减少人工成本及缩短结算周期.区块链(blockchain)是一种去中心化、不可篡改、可追溯、多方共同维护的分布式数据库,能够将传统单方维护的仅涉及自己业务的多个孤立数据库整合在一起,分布式地存储在多方共同维护的多个节点上,任何一方都无法完全控制这些数据,只能按照严格的规则和共识进行更新,从而实现了可信的多方间的信息共享和监督,避免了繁琐的人工对账,提高了业务处理效率,降低了交易成本.区块链通过集成p2p协议、非对称加密、共识机制、块链结构等多种技术,解决了数据的可信问题.通过应用区块链技术,无需借助任何第三方可信机构,互不了解、互不信任的多方可实现可信、对等的价值传输,正是由于区块链具有的数据不可篡改,去中心化,可追溯且多方共同维护等特点,因对于许多应用领域而言,将应用的关键隐私数据存储在区块链之上能极大的提高数据的安全性,减低数据篡改等风险,目前,区块链技术已被应用到医疗、供应链、金融、物联网、资产发行与交易后清洁及数字存证等多个领域。区块链网络主要有三种类型:公共区块链、联盟或联合区块链,以及私有区块链,其中hyperledgerfabric是区块链领域最为热门的区块链开源项目之一,hyperledgerfabric是一个区块链框架,旨在帮助公司建立私人或联盟许可的区块链网络,在该网络中,多个组织可以共享控制和操作网络内节点的权限,目前,该框架已在多个领域得到应用。相对于区块链而言,传统的关系型数据库在信息
技术领域
:得到的应用更为广泛,其中结构化查询语言(sql)在关系把数据库的发展和普及中起到了非常重要的作用。sql具有很多的特点,其最突出的几个如下:sql不是像c语言一样的过程编程语言,不需要了解其下层结构就可以使用;数据库能够自动地选择数据的存储方式,因此数据库操作人员无需精通数据的存储细节;sql语句可以进行嵌套,这样可以使其在使用上得到很大的简化但同样能完成各类复杂的操作;sql语句在数据库行业得到了普遍的使用,它己经成为数据库标准化的重要组成部分。增、删、改、查、连接和合并等一系列sql语句使得对数据库的操作非常方便。不同于关系型数据库,现有的区块链技术底层大多采用nosql数据库,其主要支持的是key-value查询,以太坊、hyperledgerfabric都采用的nosql数据库,其次应用对区块链数据的访问操作必须通过智能合约(smartcontract),并且应用需要通过特定的中间件与区块链平台交互,用户若想基于区块链开发应用。还需花费大量时间学习智能合约等相关技术.相比于区块链而言,现有的相关技术技术人员更熟悉传统的关系数据库及sql语句的相关操作方法,现有的开发工具也基本都基于sql构建,如同hadoop编程由mapreduce转向sqlonhadoop、spark编程由rdd转向sparksql,其次,开发基于区块链平台的应用所需的工程量十分之大,技术难度较高,且区块链目前还是一门较为新兴的技术,熟悉区块链的开发者并不多,通过现有的区块链开发技术针对应用进行重开发将花费巨大的开发及学习精力。因此主流区块链平台在非常需要sqlonblockchain的访问工具,从而使现有技术人员能够快速上手,使现有数据分析工具能够无缝接入,因此为区块链提供有效的sql访问框架可以很大程度上提高基于区块链的应用的开发效率,减少开发成本以及开发者的学习成本。技术实现要素:有鉴于此,本发明的目的是提出一种基于面向面向hyperledgerfabric的sql访问方法,提高基于区块链的应用的开发效率,减少开发成本以及开发者的学习成本。本发明采用以下方案实现:一种基于面向面向hyperledgerfabric的sql访问方法,具体包括以下步骤:定义关系模型至键值对模型的转换规则,将关系模型向键值对模型进行转换;基于智能合约技术,开发sql执行合约,sql执行合约通过接受应用层传入的sql语句作为参数,并结合转换后的存储结构,实现sql语句向底层状态数据库操作的转换;应用自动重构工具,实现应用层相关逻辑代码的自动化映射转换。进一步地,所述定义关系模型至键值对模型的转换规则,将关系模型向键值对模型进行转换具体为:步骤s11:在关系模型的有向图结构上添加两种类型的描述标签,分别为表间关系的描述标签和表类型的描述标签;步骤s12:根据嵌入原则与引用原则实现关系模型向键值对模型的转换;所述嵌入原则为:将拥有一对一或者一对多关系的数据内嵌到一个单一的文档中,使用内嵌功能对数据所有者及其包含的概念进行建模;所述引用原则为:若表为ti兴趣表,即该表存在频繁的修改和插入,则使用引用来建模而不是嵌入的方式。进一步地,所述基于智能合约技术,开发sql执行合约,sql执行合约通过接受应用层传入的sql语句作为参数,并结合转换后的存储结构,实现sql语句向底层状态数据库操作的转换具体为:步骤s21:当从上层的应用接收了sql语句后,使用jsqlparser完成词法和语法的解析,生成sql语句对应的java对象;步骤s22:通过遍历的方式对java对象表达式进行语法的正确性分析,检查sql语句的语法结构和语义逻辑是否正确;步骤s23:进行sql向fabric中智能合约相应操作的转换;步骤s24:通过增加调用层,将底层的fabricapi和访问接口的代码进行隔离,对fabricapi中的常用类进行封装。进一步地,步骤s23具体包括数据插入、数据查询、数据修改以及数据删除的操作;所述数据插入具体为:对于insert类型语句,在插入数据时,找到插入表所嵌入的目标表的对应文档,通过putstateapi插入数据;在数据插入的过程中,需要根据转换后的表结构判断数据插入的位置,如果目标表是以嵌入方式存储,则遍历目标表所嵌入的所有父表的文档中,然后更新父表的相关字段;若不是以内嵌方式存储,首先判断该表是否包含外键,若是,则进一步判断当前条行记录对应的所有外键值所关联的目标文档是否存在,若不存在关联的目标文档,则忽略本条操作;所述数据查询具体为:建立select子句与mongoquery语法子句的映射关系,建立where子句运算符与selector子句运算符的映射关系,并通过getqueryresultforstringapi进行查询操作;所述数据修改具体为:通过getqueryresultforstring与putstate两个api组合使用进行修改,首先读取目标文档,然后更新字段后再重新写入新的数据;当表以文档引用的方式存储时,直接修改所有满足条件的目标文档;当表以嵌入方式存储时,修改时需要同时遍历查询多个目标文档并修改;所述数据删除具体为:首先查询到需删除的文档的key值,再调用deletestate删除数据;首先,判断该表是否以嵌入方式存储,若是内嵌存储,则需要遍历所有嵌入的目标表并存在关联字段的文档,然后更新文档数据,删除数据后还需要读取该表的所有子表,若存在子表,则数据删除首先需要删除所有引用了该条数据的所有子表对应的数据文档,若是独立存储的话,则直接删除目标表的所有文档,并且删除所有子表的关联数据。进一步地,所述应用自动重构工具,实现应用层相关逻辑代码的自动化映射转换具体包括以下步骤:步骤s31:确定源码中需要转换的代码位置,并用标签来标注;步骤s32:制定相应的代码转换规则,扫描源码包下的所有java文件,通过标注的标签识别需要转换的代码段,并将其转换为目标代码字符串;步骤s33:导入fabric-driver的jar包以及相关配置文件及身份证书文件;步骤s34:进一步封装api,设计应用层交互中间件fabric-driver;fabric-driver包括网络配置模块、证书管理模块、合约管理模块以及通道管理模块。本发明还提供了一种基于面向面向hyperledgerfabric的sql访问系统,包括存储器、处理器以及存储于存储器上并能够在处理器上运行的计算机程序指令,当处理器运行该计算机程序指令时,能够实现如上文所述的方法步骤。本发明还提供了一种计算机可读存储介质,其上存储有能够被处理器运行的计算机程序指令,当处理器运行该计算机程序指令时,能够实现如上文所述的方法步骤。与现有技术相比,本发明有以下有益效果:本发明定义关系模型至键值对模型的转换规则,实现模型转换算法,同时实现sql语言在fabric上的解释执行,实现sql语句向底层状态数据库操作的转换。另外,本发明应用自动重构工具,提升应用层的开发效率,实现应用层相关逻辑代码的自动化映射转换。附图说明图1为本发明实施例的关系模型与键值对模型存储结构。图2为本发明实施例的关系模型的有向图结构。图3为本发明实施例的couchdb文档引用。图4为本发明实施例的couchdb文档嵌入。图5为本发明实施例的添加标签后的有向图结构。图6为本发明实施例的模型转换示意图。图7为本发明实施例的转换合约的总体结构。图8为本发明实施例的sql语句对于于智能合约对couchdb的查询语句。图9为本发明实施例的jsqlparser词法解析。图10为本发明实施例的tokens词法。图11为本发明实施例的化简后的select语法类结构图。图12为本发明实施例的多层嵌入图。图13为本发明实施例的数据删除时表a与表b的三种关系示意图。图14为本发明实施例的应用结构对比。图15为本发明实施例的应用重构流程。图16为本发明实施例的源代码样例。图17为本发明实施例的目标代码样例。图18为本发明实施例的交互中间件-fabric-driver。具体实施方式下面结合附图及实施例对本发明做进一步说明。应该指出,以下详细说明都是示例性的,旨在对本申请提供进一步的说明。除非另有指明,本文使用的所有技术和科学术语具有与本申请所属
技术领域
:的普通技术人员通常理解的相同含义。需要注意的是,这里所使用的术语仅是为了描述具体实施方式,而非意图限制根据本申请的示例性实施方式。如在这里所使用的,除非上下文另外明确指出,否则单数形式也意图包括复数形式,此外,还应当理解的是,当在本说明书中使用术语“包含”和/或“包括”时,其指明存在特征、步骤、操作、器件、组件和/或它们的组合。本实施例提供了一种基于面向面向hyperledgerfabric的sql访问方法,具体包括以下步骤:定义关系模型至键值对模型的转换规则,将关系模型向键值对模型进行转换;基于智能合约技术,开发sql执行合约,sql执行合约通过接受应用层传入的sql语句作为参数,并结合转换后的存储结构,实现sql语句向底层状态数据库操作的转换;应用自动重构工具,实现应用层相关逻辑代码的自动化映射转换。在本实施例中,所述定义关系模型至键值对模型的转换规则,将关系模型向键值对模型进行转换具体为:步骤s11:在关系模型的有向图结构上添加两种类型的描述标签,分别为表间关系的描述标签和表类型的描述标签;步骤s12:根据嵌入原则与引用原则实现关系模型向键值对模型的转换;所述嵌入原则为:将拥有一对一或者一对多关系的数据内嵌到一个单一的文档中,使用内嵌功能对数据所有者及其包含的概念进行建模;所述引用原则为:若表为ti兴趣表,即该表存在频繁的修改和插入,则使用引用来建模而不是嵌入的方式。在本实施例中,所述基于智能合约技术,开发sql执行合约,sql执行合约通过接受应用层传入的sql语句作为参数,并结合转换后的存储结构,实现sql语句向底层状态数据库操作的转换具体为:步骤s21:当从上层的应用接收了sql语句后,使用jsqlparser完成词法和语法的解析,生成sql语句对应的java对象;步骤s22:通过遍历的方式对java对象表达式进行语法的正确性分析,检查sql语句的语法结构和语义逻辑是否正确;步骤s23:进行sql向fabric中智能合约相应操作的转换;步骤s24:通过增加调用层,将底层的fabricapi和访问接口的代码进行隔离,对fabricapi中的常用类进行封装。在本实施例中,步骤s23具体包括数据插入、数据查询、数据修改以及数据删除的操作;所述数据插入具体为:对于insert类型语句,在插入数据时,找到插入表所嵌入的目标表的对应文档,通过putstateapi插入数据;在数据插入的过程中,需要根据转换后的表结构判断数据插入的位置,如果目标表是以嵌入方式存储,则遍历目标表所嵌入的所有父表的文档中,然后更新父表的相关字段;若不是以内嵌方式存储,首先判断该表是否包含外键,若是,则进一步判断当前条行记录对应的所有外键值所关联的目标文档是否存在,若不存在关联的目标文档,则忽略本条操作;所述数据查询具体为:建立select子句与mongoquery语法子句的映射关系,建立where子句运算符与selector子句运算符的映射关系,并通过getqueryresultforstringapi进行查询操作;所述数据修改具体为:通过getqueryresultforstring与putstate两个api组合使用进行修改,首先读取目标文档,然后更新字段后再重新写入新的数据;当表以文档引用的方式存储时,直接修改所有满足条件的目标文档;当表以嵌入方式存储时,修改时需要同时遍历查询多个目标文档并修改;所述数据删除具体为:首先查询到需删除的文档的key值,再调用deletestate删除数据;首先,判断该表是否以嵌入方式存储,若是内嵌存储,则需要遍历所有嵌入的目标表并存在关联字段的文档,然后更新文档数据,删除数据后还需要读取该表的所有子表,若存在子表,则数据删除首先需要删除所有引用了该条数据的所有子表对应的数据文档,若是独立存储的话,则直接删除目标表的所有文档,并且删除所有子表的关联数据。在本实施例中,所述应用自动重构工具,实现应用层相关逻辑代码的自动化映射转换具体包括以下步骤:步骤s31:确定源码中需要转换的代码位置,并用标签来标注;步骤s32:制定相应的代码转换规则,扫描源码包下的所有java文件,通过标注的标签识别需要转换的代码段,并将其转换为目标代码字符串;步骤s33:导入fabric-driver的jar包以及相关配置文件及身份证书文件;步骤s34:进一步封装api,设计应用层交互中间件fabric-driver;fabric-driver包括网络配置模块、证书管理模块、合约管理模块以及通道管理模块。本实施例还提供了一种基于面向面向hyperledgerfabric的sql访问系统,包括存储器、处理器以及存储于存储器上并能够在处理器上运行的计算机程序指令,当处理器运行该计算机程序指令时,能够实现如上文所述的方法步骤。本实施例还提供了一种计算机可读存储介质,其上存储有能够被处理器运行的计算机程序指令,当处理器运行该计算机程序指令时,能够实现如上文所述的方法步骤。下面对本实施例的原理进行具体的说明。本实施例从以下三个方面实现框架:首先,fabric底层采用键值对模型数据库(couchdb),而sql语言基于关系模型,因此,本实施例先对比分析了关系模型与键值对模型之间的区别,定义关系模型至键值对模型的转换规则,实现模型转换算法,并开发了模型转换工具。其次,为了实现sql语言在fabric上的解释执行,基于智能合约技术,开发了sql执行合约,sql执行合约通过接受应用层传入的sql语句作为参数,并结合转换后的存储结构,实现sql语句向底层状态数据库操作的转换。最后,由于传统应用主要通过jdbcapi(java应用为例)调用jdbc-driver访问数据库,而基于fabric的区块链应用需要通过fabric-sdk访问fabric网络,因此为了提升应用层的开发效率,本框架提供了应用自动重构工具,实现应用层相关逻辑代码的自动化映射转换。为便于分析,关系模型和couchdb的键值对模型形式化地定义如下:定义1:关系型数据库中的一个表ti是一个二维表,可用二元组其中表示表中定义的属性的集合,pk(ti)表示表定义的主键属性,为数据的唯一标识。定义2:用di={′_id′:′…′,k1:v1,k2:v2,...kn:vn}表示couchdb中的每个文档,每个文档为一条数据记录,其中′_id′为每个文档的唯一标识,k1:v1,k2:v2,...kn:vn表示每个文档中包含的所有属性键值对。couchdb是键值对形式数据库,value部分为json格式的文档数据,在couchdb中′_id′可以是实体id,是在插入数据时数据库自动生成的数据标识,也可以是自定义的唯一标识,同样,couchdb文档中既可以用实体id标识,也可以有pk(ti)来标识,在本实施例中则采用pk(ti)标识,因为实体id是在插入couchdb时由数据库实时产生的,其作用仅仅是标识唯一性,而无法像主外键一样表示数据之间的引用关系。本实施例将一张关系表映射成一个couchdb中的某一类型文档所组成的集合,对于任意一条表t中的记录ti=(v1,v2,...vn)转换成couchdb中的一个文档,映射关系如图1所示。图1的实例将user表转换到couchdb的相应文档组成的集合tuser→cuser,couchdb中另外,′_rev′字段为couchdb自动生成的文档版本号。左图的表结构中用′user_no′作为主键,右图中的两个文档中的′_id′虽然也可以做主键,是在插入数据库后产生的,具有唯一性,但无法表示引用关系。同时user_no由于是主键的缘故在rdbms数据库中同样具有唯一性,在向couchdb数据库转换的时候也具有唯一性。所以可用于标识一个文档,本实施例将采用和关系型数据库原有的主键作为唯一标识,两个文档不通过′_id′来表示引用关系,由于couchdb中不存在表的概念,因此在每条文档数据中加入′table_name′字段用以表示文档所属的表类型,对应′table_name′所组的文档集合对应关系模型中的数据库表,其次为了保证key值的唯一性,通过表名组合主键方式用以标识。为了进一步描述关系模型中表间的关联关系,引入外键约束,外键约束描述了表与表之前的关联关系,引入关系后可以将关系模型表示成一个有向图结构,本实施例对主外键进行形式化定义如下:定义3:引入外键后,每张表ti可用三元组其中表示表ti的外键约束集,fk(ti→tj)表示表ti和表tj的外键约束关系。定义4:g=<t,v>为一张有向图,相当于一个数据库。t={t1,t2,...tn}:图中点的集合,相当于数据库中的所有的表,其中t的定义如定义1的描述。e=<vi,vj>:一条由vi指向vj的边,相当关系型数据库中的一条外键关系,或者是用户指定的两张表在今后的使用中需要连接的必要。e={e1,e2,...en};图中边的集合,相当于数据库中所有的外键关系,加上用户指定的所有存在连接必要的两张表构成的边。显然g是一个有向图,本实施例选取了开源数据库sakila的部分用例,该部分包含8张数据表,并且包含5条外键关联关系,该部分用例的有向图结构如图2所示,图中节点与节点之间通过有向边进行连接,对于一些关系模型中出现自引用的模型,即表节点有一条指向自己的边,以及可能存在循环引用的情况,本实施例不讨论包含以上两种情况的数据库模型,也就是假定关系模型所组成的图为有向无环图。couchdb是键值对模型的数据库,数据可以模式自由,而不像关系型数据库中在插入数据前要预先定义表的结构。并且couchdb不强制文档结构,这种数据模式可以很好地表示一个实体或对象。couchdb建模的关键是综合考虑应用的需求、数据库引擎的特点和性能、以及数据处理的方法(包括查询和更新)。在决定如何表示集合的数据结构以及实体与实体间的关系时,couchdb提供了两种表示联系的方式,分别为引用和嵌入:(1)引用:引用和关系型数据库中的外键作用几乎一样,文档a通过一个标识pk(ti)来表示,这个标识指向另外一个文档b,从而关联另一个文档b,好处是显然的,这样另外一个文档的修改,不需要影响文档a的修改;缺点也是明显的,查询效率降低了,因为,当想获取两条文档的信息时不得不查询两次来获取数据。这种建模方式一般用于一对多以及多对多的关系模式中,如图3所示,左图中的order文档中通过′user_no′字段引用了user文档。(2)嵌入:通过将一些相关的数据嵌入到一个文档的方式成为嵌入,couchdb允许以单独的json或者数组的形式嵌入到文档中,这种反范式的设计可以在获取一个文档的时候获取其关联的文档,因而可以提高查询的速度,比如可以将一对多中的多变成一个数组,数组的每一个元素均为一个子文档,这样可以从某种意义上说,一对多的模式退化成了一对一的模式。将图3中的两个文档间的联系用嵌入方式可表示为图4所示,和上面的建模方式不同,在嵌入式方法中,order文档嵌入user文档。couchdb具有灵活的结构,尽管couchdb不支持联接操作,但使用嵌套结构将大大提高读取操作的速度。可以将相关数据聚合到一个文档中,以避免合并操作,问题是如何确定关系数据库的哪些表需要嵌入在一起,同时,嵌套意味着数据冗余。设计模型转换算法时,必须在性能和数据冗余之间进行权衡。首先,本实施例为了有效提高转换后的数据读写效率,在关系模型的有向图结构上添加了两种类型的描述标签,分为为表间关系的描述标签以及表类型的描述标签,该描述信息都是开发者基于对业务的了解所给予的相关定义,因此,在定义2与3的基础上,添加描述信息后关系表结构的相关定义如下:定义:5:其中fk(ti)表示表ti的外键集合,tj表示关联的表名,气表示该关联是否在应用的业务中需要进行频繁的join查询,该字段是基于开发者对于业务的理解所给予定义;定义6:其中,新添加的属性typei表示表类型,本实施例将表类型划分成三种,分别为类型ti、tr以及tn。其中ti代表为兴趣表,表示该表在应用的业务中需要频繁的修改,插入等操作,tr则表示该表属于关联表,关联表是用以表示两表之前的多对多关系的中间表,其余表则归类为tn类型,表示普通实体表,以上三种不同的表类型是基于开发者对业务的理解及分析并给予的定义。图5为在图2基础上添加描述标签后的有向图结构,通过添加相关描述标签后,本实施例结合标签信息将关系型数据库模型向couchdb模型转换时,综合考虑以下两点来针对不同关系中数据的特点,并兼顾数据写入与查询的效率。(1)嵌入原则:考虑将拥有一对一或者一对多关系的数据内嵌到一个单一的文档中,同时也可以使用内嵌功能对数据所有者及其包含的概念进行建模。例如两张表之间进行频繁的join查询,这类操作采用嵌入方式将有效减少查询次数,因此这类关系将优先将子表嵌入到父表中,其次对于tr关系表,这类表用于是用于表示多对多关系的中间表,优先以嵌入方式存储也可以较少查询次数。(2)引用原则:关系型数据库范式设计的一个好处是对修改是方便的,通过主外键关联的两张表,当一张表的非主属性数据修改了,并不需要修改另一张表,在couchdb的模式设计时,也应该遵守这种约定,若表为ti兴趣表(频繁的修改和插入)这种情况下应该使用引用来建模而不是嵌入的方式,如果嵌入到其他实体中,则每次修改都需要对父实体进行读取并重写,并且,单个表的修改可能会导致多张表所在文档的频繁需改,而对性能产生一定的影响。通过读取用户定义的关系表结构信息,描述标签等转换为有向图结构,然后根据模型转换规则实现关系模型向键值对模型的转换算法,算法基本流程为:首先本实施例将构建后的有向无环图作为输入,然后定义一个可以嵌入到其他实体的函数can_emded,这个函数的参数是一个文档实体,如果此实体可以嵌入到其他实体中,则此函数返回真,否则返回假。如果该实体有“ti”标签,则本实施例算法不建议该文档嵌入到其他文档中例如,一个实体包含“ti”的标签的节点,表明该文档进行频繁的修改和插入操作,如果嵌入到其他实体中,则每次修改都需要对父实体进行读取并重写,对语句的执行性能是很大的影响。构建有向的无环图结构后,每个图节点包含表的类型,图中的表包含边的描述类型,分为一对一以及一对多,对于多对多的关系,实际上是通过通过组合两个一对多关系建立的,除此之外,有的边还包含frequentjoin标签。之后依次对遍历图中的边的类型,如果为一对一关系,则首先判断通过can_embeded函数,参数为该关系中的子表实体,如果该关系的子表能否嵌入到父表中,则将子表嵌入到父表中,如若不能,则通过引用的方式存储关系。若表间关系为一对多,则首先判断该关系的子表是否为tr关联表,若为tr关联表,则不保留关联表,而是将tr关联表的信息嵌入到父表中,这是由于关联表是用于表示所关联的两张表之间的多对多关系,通过将关联表嵌入到两个父表档中,从而使得两个父表建立多对多的引用关系。若不是tr关联表,则通过can_embeded函数该关系的子表能否嵌入到父表中并且判断该关系是否存在frequentjoin标签,若可以,则以数组的形式嵌入到父表中,若不能,则同样保留表间的引用,因为,在couchdb中,表的嵌入是为了减少join查询的损耗,但是同时会增加单个文档的维护成本,如果该关系不存在频繁join操作,则采用引用保留表间关系。如图6为图5的用例转换后的键值对结构,其中film_actor,film_category是tr关联表,address是ti兴趣表,其余为tn表,然后根据上述方法实现了关系结构向键值对建构的转换,转换后的样例如图6中b所示。转换后film_category由于是关联表,被同时嵌入film表与category表中,store表嵌入address表,address由于是ti类型表,和city表保持引用关系,city表嵌入到country表中。接下来描述sql语句在fabric上的解释及执行过程,本框架提供了sql执行合约,sql执行合约通过接受应用层发送的sql语句,实现sql语句至fabric中相关操作的转换。sql转换合约的总体结构如图7所示,sql语句只能在关系型数据库中解释执行,而在fabric中,智能合约提供了相应的api用以对couchdb数据库实现curd操作,因此将sql执行合约从结构上划分为sql解析层,sql校验层,sql转换层以及fabricapi调度层,通过对sql的解析及转换操作,映射为合约支持的相关操作,在转换过程中,需要重点解决两点问题:(1)按照前文所述,由于表的模型已经发生了转变,有些表已经嵌入到其他表中,形成了文档嵌入的关系,因此,在针对增删改查的操作过程中,需要针对转换后的结构做相应的操作处理。(2)需要实现语法的转换,获取sql解析分析后的对象,需要转换成智能合约所支撑的相应操作,以select语句查询为例子,如图8所示sql语句对于于智能合约对couchdb的查询语句。当从上层的应用接收了sql语句的输入后,接下来需要使用jsqlparser完成词法和语法的解析。sql解析层的设计参考了相关开源组件的实现原理和架构,并且由于jsqlparser是一个开源的项目,可以基于该组件进行二次的开发。这里从三个方面对解析层进行介绍,分别是词法解析、语法解析以及分析与检验。词法解析用于实现词法化函数,该函数将输入层中的sql字符流转换为词流,然后由下一个解析器使用。简而言之,词法分析器就是为了实现定义单词,再由jsqlparser按照单词做匹配。词法分析也是jsqlparser中比较简单的一个过程,因为要实现一个一语法词法分析器,该部分只需要按照sql-92标准并整合sql关键字和相关的词语就能够完成。在jsqlparser语法中,一词法分析器部分以大写字符开头。例如,要为单词“letter′’定义大写和小写字母。图9是jsqlparser的词法示例。由图9可知,jsqlparser的语法规则是给出了每一个字符的范围,也就是每个字符都必须是大写或者小写英文字母。在进行字符的匹配时,就是使用″letter′′的相关词法规则完成的。值得一提的是,jsqlparser也支持批量定义的功能,这是通过takens语法实现的。当运算逻辑比较复杂时,sql中的关键字就可以用批量定义来完成,如图10所示。在解析层中,jsqlparser会对sql语句进行词法的匹配。对于“select*fromtablename”这样的一条sql语句,jsqlparser会先做词法匹配,依据的原则是前文介绍的tokens语法,然后把剩余的词语当做语句的参数提取出来。这样,sql语句就会和词法匹配形成一个映射关系,从而对sql语句进行解析。词法分析器用于分割字符串,将输入的sql字符串拆分成有序的单词组,然后语法分析器会根据相应的规则对一单词组进行分析。通常,在编译器或者解析器中都会包含这样一个语法分析器组件,语法分析器除了能用于生成单词组的层次化数据结构,还可以用于对语法的检查。这种词法分析和语法分析分层的思想既有利于系统的设计,也能提高系统的执行透明度。在该接口的设计一中,语法分析器在输出端会返回一个sql语句的java对象,并对不同的sql语句操作通过语法分析器会生成对应的不同对象。例如,增加(insert)语句会生成insertstatement的一个对一象,这样,sql的大部分语句都可以通过jsqlparse工具进行实现。sql语言具备简洁、方便使用的特点,但是由于它的可嵌套性和数据库表的可连接性,有时候为了完成一个复杂的逻辑功能,sql语句会变得非常复杂。因此,本实施例的接口在设计上包含了一个sql分析逻辑层。输入的sql语句经过jsqlparser的解析后会生成一个sql语句对应的java对象,并且把该对象传递给sql分析层作为输入参数。由于sql语句中可能会包含where、limit、groupby等逻辑关键词,所以生成的java对象中也会包含这些关键词对应的成员变量。例如,操作语句通过解析层的解析后就会返回selectstatement类的一个对象,其中包含了一些成员变量,如图11所示。当分析层接收了java对象以后,需要通过遍历的方式对sql语句进行语法的正确性分析,检查sql语句的语法结构和语义逻辑是否正确,保证传递到下一层数据的正确性和可执行性。sql语句的正确性检查是个非常复杂的过程,事实上,解析层在解析sql语句之前,可以使用正则表达式的方式匹配sql语句,从而验证语句的正确性。但是,正则表达式的局限性很大,当逻辑关键词交叉使用时,正则表达式就很难完成这样的匹配。因此,在本实施例中采用将解析层返回的java对象转化成表达式,通过遍历表达式进行分析。在完成解析操作后,需要实现sql向fabric中智能合约相应操作的转换。包括数据插入、数据查询、数据修改与数据删除的过程。数据插入对应insert语句,在智能合约中需要通过putstateapi插入数据,insert语句相对简洁,主要包含into、value块,不包含where等条件子句,因此在处理insert语句时,只需要插入目标数据即可,但是根据第四章所述,模型转换后的结构已发生变化,有的表已被内嵌至其他表中,因此,在插入数据时,需要找到插入表所嵌入的目标表的对应文档。对于insert类型语句,相当于关系表中表t的一条行数据插入到couchdb中,插入时会存在以下几种情况:(1)情况一:表t转换为了一个文档,直接插入新的文档即可。(2)情况二:表t被嵌入到了另一张表t1中,因此需要将数据插入到父表的数据文档中。(3)情况三:表t被嵌入到t1中,ti被嵌入到t2中,这样t表最终被嵌入到t2表节点对应的文档集合中中。图12给出了表节点多层嵌入转换的示意图,例如图中的rental表将不再独立存储,而是同时嵌入到store表以及order表中,因此插入的过程中需要获取rental表所嵌入的路径store.inventory.rental以及order.rental。sql插入语句相对简洁,不包含where等条件子句,但是在数据插入的过程中需要根据转换后的表结构判断数据插入的位置,因为couchdb文档的嵌入方式,可能有的表已经被嵌入到其他表中,因此首先需要判断插入数据的目标表的存储方式。如果目标表是以嵌入方式存储,则遍历目标表所嵌入的所有父表的文档中,然后更新父表的相关字段,若不是以内嵌方式存储,对于这种情况,首先要判断该表是否包含外键,若存在,则判断该条行记录对应的所有外键值所关联的目标文档是否存在,因为对于存在外键关联字段,若该字段的数据所对应的数据并不存在,是无法完成插入操作的,因此需要忽略本条操作,table_name表示要插入数据对应的表明,docment为table的一条行记录转换为key-value结构后的json对象。关于数据查询,select语句是sql语句中最为复杂的类型,包含select、from、where、orderby、groupby、limit等子句块。couchdb支持通过mongoquery语句进行富查询,智能合约合约中,利用通过getqueryresultforstringapi进行查询操作,其中传入参数为mongoquery语句,如表l展示了本框架所支持的select各字句块与mongoquery的子句对比,在转换的过程中select中出现的返回字段则转换为field子句,where子句转化为selector子句;对于limit子句,虽然mongoquery支持limit,但是由于fabric中对其进行了限制,无法直接使用,但是,对于分页查询fabric提供了getqueryresultwithpagination分页查询接口,而对于orderby,having子句,由于couchdb并不支持本类操作,因此本框架暂不支持。表l其中最为复杂的是where条件选择子句,对应于mongoquery语法的selector子句,where字句包含许多复杂的运算符,例如:在where语句中使用的and运算符,变为$and语句;or运算符,变为$or语句;>运算符,变为$gt语句,如表2所示展示了本框架支持的常用的核心运算符映射对比。表2where子句运算符selector子句运算符说明and$and与条件运算符or$or或条件运算符in$in存在条件运算符notin$nin不存在条件运算符>$gt大于运算符<$lt小于运算符=/is$eq等于运算符>=$gte大于或者等于运算符<=$lte小于或者等于运算符!=/isnot$ne不等于运算符between$gt&$lt区间运算符其次需要重点关注的是,select查询可能包含表间的join联查,对于join查询,原生的查询语法并不支持,但是如前文所述,对于部分存在关系的表,已经按照主外键关联的字段值嵌入成一个文档,因此,在查询时,如果join连接的字段是主外键关联字段,可直接查询目标文档,但是join查询所关联的两张表,可能并未设置主外键约束,对于这类情况,与采用文档引用的场景下查询的方式一致,需要通过netstatedloopjoin方法实现join查询,首先判断join查询关联的两张表是否有内嵌关系,若存在,则说明两张表的数据在couchdb是存储在相同的文档中,则直接查询目标数据即可,若不是内嵌关系,则两张表的数据在couchdb中以引用方式关联,则首先查询出a表中的所有数据,然后循环遍历结果集,依次从b表中查询满足join条件的结果,最后聚合数据。数据修改对应sql语言中的update语句,在智能合约中没有原生的api,因此对于update操作,实际上是将查询与插入两种操作组合而成,可通过getqueryresultforstring与putstate两个api组合使用,首先读取目标文档,然后更新字段后再重新写入新的数据。update语句主要包含set子句以及where子句,对于where内容,支持andor运算符连接表达式,支持等于、大于、小于、大于等于、小于等于和不等于运算符,具体的where子句映射如表2所示,在转换的过程中需要将语句中的选择条件转换为修改的目标文档对应的选择条件,对于update类型操作主要包含以下两种情况:(1)情况一:对于文档引用,因为文档引用的修改并不影响存在关联关系的其他文档,因此直接修改所有满足条件的目标文档即可。(2)情况二:当嵌入方式存储时,表t可能被嵌入到了诺干个表中,因此在修改时需要同时遍历查询多个目标文档,并修改,操作过程与插入操作类似,例如a表与b表为一对多模型,a表的主键为b表的外键,转换后b表的数据以数组的方式嵌入到a对应的文档下,因此需要找到嵌入到的a表包含b表中目标数据的文档,然后进行更新操作。对于文档的修改,当修改某个表的一条行记录时,对应到couchdb中,可能需要修改一个文档,也可能需要修改多个文档。显然,当修改的表以嵌入的方式存储时,则可能要同时修改多个文档,因为,对于一个包含多个外键的表而言,根据模型转换规则,该表对应的文档可能同时嵌入到了多个父表的文档中,因此,需要遍历所有父表的文档,找到所有符合条件的文档数据并修改,而当修改的表对应的以引用的方式存储时,说明该表是用独立文档的形式存储的,则只需修改满足条件的文档即可。关于数据删除,对于delete语句,在fabric的智能合约中,通过deletestate接口实现删除操作,但是deletestate只支持按照key值删除,因此,每次删除操作需要首先查询到需删除的文档的key值,再调用deletestate删除数据,delete类型语句包含delete、from,where子句,from指定表名,where为选择条件,支持and、or运算符连接表达式,支持等于、大于、小于、大于等于、小于等于和不等于运算符,where子句选择运算符的映射关系具体如表2所示所述,对于detele操作相对较为复杂,主要存在以下几种情况。(1)情况一:如图13中a所示,假设a表与b表是父子表的关系,同时表a与表b之间通过引用关联,那么删除a表的一条行记录a,由于存在主外键的关系约束,因此先删除表b引用a的所有行记录,然后再删除a记录,因此转换后的文档集合,对应于couchdb中的ca,先删除cb文档集合中对应的文档然后再删除ca文档集合中的文档。(2)情况二:如图13中b所示,对于嵌入的情况,存在父子表关系的表a与表b,b中的所有数据嵌入到a表所在的文档中了,因此,删除a表对应的文档数据即可,所有的tr节点都是采用这种情况。(3)情况三:如图13中c所示,对于存在多级关联的关系,可能同时包含以上两种情况,例如表a嵌入到表b中,而表b又是表c的的子表,但表b通过引用关联表c,因此,删除表a的记录时,a表中可能包含多条以数组形式存储的表b的相关记录,还需要删除表c中有引用到表a中包含的表b相关记录的所有文档。对于删除操作,当删除某条数据时,由于表结构的转变,对应到couchdb中,可能需要删除一个数据文档,也可能需要删除多个数据文档。首先,判断该表是否以嵌入方式存储,若是内嵌存储,则需要遍历所有嵌入的目标表并存在关联字段的文档,然后更新文档数据,删除数据后还需要读取该表的所有子表,若存在子表,则数据删除首先需要删除所有引用了该条数据的所有子表对应的数据文档。若是独立存储的话,则直接删除目标表的所有文档即可,并且删除所有子表的关联数据。从应用层开发的角度,为了简化繁琐的业务代码及相关配置代码的开发,本框架设计了应用自动化重构工具,工具包含两个部分,分别是应用重构工具以及应用与区块链网络的交互中间件。重构的原则是重构给定的代码而不改变业务功能,对于本框架而言实际上是重构了对外部数据的访问操作,一般来说,重构具有三个方面的特征:(1)原始代码的结构,即源结构;(2)目标代码的结构,即目标结构;(3)一系列代码重构步骤,能将源代码结构转换为目标代码的结构,以便目标代码最终能够呈现所需的程序结构,如图14为源应用与重构后的目标结构对比。本实施例首先具体来介绍源结构和目标结构:1、源结构应用的原结构如图14中左图所示,应用程序从逻辑结构上可以划分为控制层、业务层、数据访问层,控制层及业务层为内部功能,对数据库的相关操作代码集中在数据访问层,在基于关系型数据库的应用中,数据访问层通过jdbc接口实现相关的逻辑操作,包括数据库连接,请求,数据处理等,其次,应用需要通过对应的交互中间件访问数据库,例如mysql的mysql-driver,oracle的oracle-driver等。2、目标结构应用重构后目标结构如图14的右图所示,目标结构的上层业务功能需要保持与原有应用相同,而对外部数据的请求需要转变,目标应用由对数据库的访问转换为了对区块链的访问。基于fabric的应用需要通过本框架提供的fabric-driver中间件与区块链网络交互,并且,在数据访问层中,对jdbc-api的相关操作代码需要转换为对fabric-driverapi的调用。根据上述描述,本实施例需要将源应用程序重构成符合上述提出的目标应用结构,实现将应用由对数据库的操作的相关代码转换为对区块链的访问操作,其重构步骤如图15所示,输入是应用源代码以及配置文件及身份证书文件,输出是目标应用代码,大致描述如下:第一步:确定需要转换的代码位置,java应用以类为基本单位,每个类中包含了若干个方法,因此,本实施例代码转换的目标代码也是以方法为单位,对于一个给定的应用源代码,通过对特定的方法添加<sql-fabric></sql-fabric>标签用以标注,标注的目标方法的核心业务逻辑为对数据库的相关操作代码,例如图16所示,图中第3行及第21行用<sql-fabric>标签用以标注,该方法中通过jdbc的相关接口实现数据的插入操作,例如图中第7行是获取数据库连接对象,第13行是执行数据库调用,第15行是对数据结果集进行处理。第二步:完成对源应用代码的标注后,需要实现源应用代码向目标应用代码的转换,转换前需要制定基本的代码转换规则,对于对外部数据请求及操作无关的逻辑代码需要保持不变,对于jdbc相关的逻辑代码则需要转换为对fabric-driver的操作,如表3为代码的映射规则,左边是jdbcapi,右边为转换后的fabric-driverapi。表3重构工具会扫描源码包下的所有java文件,然后逐行读取代码字符串,查询是否包含指定的标签<sql-fabric>,如果遇到指定标签则说明从该行开始到</sql-fabric>标签结束,该部分代码需要转换,然后根据扫描结果逐行提取代码块中的每一行代码,匹配需要映射转换的目标关键字,关键字为api类名称及方法名字,对象名则保持不遍,然后据表3定义的映射规则,将该行代码替换成目标代码字符串,例如对于图16的第7行代码connectionconn=drivermanager.getconnection(url,usr,pwd)可根据表3的第二条规则映射为图17的第6行代码fabricconnectionconn=fabricmanager.getconnection(),对需要修改的java文件,需重新创建新的同名文件,然后将修改后的代码写入新的文件中。第三步:在经过步骤2后,已经实现了代码的转换,完成代码转换后还需要导入fabric-driver的jar包以及相关配置文件及身份证书文件,首先用户提供的配置文件及身份证书文件添加至应用的resource文件目录下,路径命名为resource/fabric。网络信息配置文件为yaml格式,包含了fabric网络的相关信息,例如各节点的通信ip及端口、通道名称、合约名称等,而身份证书包含了fabric网络的通信密钥。fabric-driver为本框架提供的交互中间件,重构后的应用包含了fabric-driver的相关库,工具通过对应用的pom文件添加库的描述信息使得应用在运行时可加载外部库文件。虽然hyperledgerfabric框架提供了fabric-sdk-java,但是仍然需要自行编写复杂的配置模块,包括合约调用,证书加载等,并且操作及使用方法繁琐,缺乏有效的统一。其次,为了实现应用层jdbc代码的有效映射,需要进一步封装api,因此本框架提供了应用层交互中间件,如图18为交互中间就的总体结构设计。fabric-driver底层基于fabric-sdk-java1.3版本,并集成了合约管理,通道管理,证书配置以及网络配置四大模块,对应用业务层封装了fabric-driverapi,api的具体描述如表4所示,中间件的核心模块相关核心代码文件及说明如表4所示,相关描述如下:(1)网络配置模块:应用首先需要配置所有网络节点的信息包括ip地址、端口号等,通过fabricmanager.init方法加网络配置文件,读取有关各节点的相关信息。(2)证书管理模块:fabric是联盟区块链,访问请求需要获得权限认证,因此需要配置客户端的身份信息,通过fabricuser.init方法作为配置入口,加载resource/fabric下的证书文件。(3)合约管理模块:提供合约调用,结果集处理功能,合约调用包括invoke和query两种命令,对于状态数据插入,删除,修改操作通过inovk命令执行,对于查询操作则通过query命令执行,返回的json格式数据格式化后用fabricresult对象进一步包装。(4)通道管理模块:提供通道创建,通道信息查询等功能,fabric支持多通道,不同通道间的数据账本相互隔离,通道管理模块加载通道的配置信息以及创世区块。表4本领域内的技术人员应明白,本申请的实施例可提供为方法、系统、或计算机程序产品。因此,本申请可采用完全硬件实施例、完全软件实施例、或结合软件和硬件方面的实施例的形式。而且,本申请可采用在一个或多个其中包含有计算机可用程序代码的计算机可用存储介质(包括但不限于磁盘存储器、cd-rom、光学存储器等)上实施的计算机程序产品的形式。本申请是参照根据本申请实施例的方法、设备(系统)、和计算机程序产品的流程图和/或方框图来描述的。应理解可由计算机程序指令实现流程图和/或方框图中的每一流程和/或方框、以及流程图和/或方框图中的流程和/或方框的结合。可提供这些计算机程序指令到通用计算机、专用计算机、嵌入式处理机或其他可编程数据处理设备的处理器以产生一个机器,使得通过计算机或其他可编程数据处理设备的处理器执行的指令产生用于实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能的装置。这些计算机程序指令也可存储在能引导计算机或其他可编程数据处理设备以特定方式工作的计算机可读存储器中,使得存储在该计算机可读存储器中的指令产生包括指令装置的制造品,该指令装置实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能。这些计算机程序指令也可装载到计算机或其他可编程数据处理设备上,使得在计算机或其他可编程设备上执行一系列操作步骤以产生计算机实现的处理,从而在计算机或其他可编程设备上执行的指令提供用于实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能的步骤。以上所述,仅是本发明的较佳实施例而已,并非是对本发明作其它形式的限制,任何熟悉本专业的技术人员可能利用上述揭示的技术内容加以变更或改型为等同变化的等效实施例。但是凡是未脱离本发明技术方案内容,依据本发明的技术实质对以上实施例所作的任何简单修改、等同变化与改型,仍属于本发明技术方案的保护范围。当前第1页12当前第1页12
当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1