6.4 组合SQL服务标签
之前6.1节简单的介绍了组合SQL服务的概念和定义,这节将详细的介绍组合SQL服务的功能和使用。
1. sql-service标签的使用
图片1Schema设计图
sql-service节点属性说明
属性名 | 用途及说明 | 必填 | 取值 |
---|---|---|---|
id | 服务标识,需要唯一;作为辅助标签此项无意义,可省略 | Y | 用户定义 |
dsKey | 所使用的数据源标识,这里有以下几种情况:<br />1.此处设置数据源(A),内部服务未设置数据源,内部服务使用数据源(A)。<br />2.此处设置数据源(A),内部服务设置数据源(B),内部服务使用数据源(B)。<br />3.此处未设置数据源,内部服务设置数据源(B),内部服务使用数据源(B)。<br />4.在分库分表的情况下,此处未和内部服务均可不设置数据源,后面章节将会详细介绍此种设置。 | N | 用户定义 |
txRef | 所使用的事务定义标识,如果用户未指定,则根据setDefaultTransaction所定义的规则进行默认匹配,如果还未匹配上,系统则会跑出异常。 N 用户定义 | ||
resultType | 返回类型:默认xco | N | xco/map |
cacheUse | 缓存使用,此项将在缓存一节详细说明 | N | 用户定义 |
cacheClear | 缓存使用,此项将在缓存一节详细说明 | N | 用户定义 |
说明:
组合SQL服务是一个服务集合,他可以包含selectSet
、selectOne
、selectVar
、update
、delete
、insert
这6中基本服务标签一个或者多个,但不能包含自身sql-service
标签。
2. 内部selectSet标签的使用
示例1:
<sql-service id="myService" dsKey="ds" txRef="tx_02">
<selectSet resultKey="{users}">
select * from user
</selectSet>
<return>
<property value="{users}"/>
</return>
</sql-service>
说明:
上述示例中我们定义了一个组合SQL服务myService,其内部包含了了一个selectSet
基本服务,当程序执行完selectSet
服务后会将其结果List<XCO>
以users为key,放入上下文中,最后通过return标签返回。具体的返回结果是一个XCO
对象,其中包含一个List<XCO>
元素,关于return
标签和返回结果的设定我们将在其他章节讲述,这里不再细说,下面给出返回结果的XML格式。
返回结果
<?xml version="1.0" encoding="UTF-8"?>
<X>
<XL K="users">
<X>
<L K="user_id" V="1"/>
<S K="user_name" V="李四"/>
<B K="user_age" V="18"/>
<A K="create_time" V="2016-10-20 21:30:58"/>
</X>
<X>
<L K="user_id" V="2"/>
<S K="user_name" V="张三"/>
<B K="user_age" V="18"/>
<A K="create_time" V="2016-10-20 21:31:58"/>
</X>
......
</XL>
</X>
上述示例中selectSet
作为内部服务,或者说内部标签,其属性的使用发生了一下变化:
selectSet节点属性和变化说明:
属性名 | 用途及说明 | 必填 | 取值 |
---|---|---|---|
id | 无意义 | N | 用户定义 |
dsKey | 所使用的数据源标识,在有如下几种情况(非分库分表):<br />1.如果用户没有设置此属性,则使用sql-service节点的数据源;<br />2.如果用户自行设置了此属性,则使用用户所设置的数据源。<br />关于在分库分表的应用场景下,此项的设置我们将在具体的章节来说明。 | N | 用户定义 |
txRef | 无意义 | N | 用户定义 |
resultKey | 当前查询操作返回结果的在上下文参数中的存放key | N | 用户定义 |
resultType | 无意义 | N | 用户定义 |
resultMap | 无意义 | N | 用户定义 |
fetchSize | 每次查询的最大获取条数,默认255 | N | 用户定义 |
cacheUse | 缓存使用,此项将在缓存一节详细说明 | N | 用户定义 |
看到这里大家可能会疑问,和示例1中,为什么不直接用selectSet
标签呢?因为之前已经给已经给大家介绍过sql-service
所定义的服务是一个组合服务,其内部可以包含selectSet
、selectOne
、selectVar
、update
、delete
、insert
这些基本服务,在后面的教程中大家就可以感受到组合服务的强大功能了。
2. 内部selectOne标签的使用
示例2:
<sql-service id="myService2" dsKey="ds" txRef="tx_02">
<selectOne resultKey="{user}">
select * from user where user_id = #{user_id}
</selectOne>
<return value="{user}"/>
</sql-service>
说明:
示例2中我们定义了一个组合SQL服务myService2
,其内部包含了了一个selectOne
基本服务,执行后返回结果是一个XCO
对象,XML格式如下:
返回结果
<X>
<L K="user_id" V="1"/>
<S K="user_name" V="李四"/>
<B K="user_age" V="18"/>
<A K="create_time" V="2016-10-20 21:30:58"/>
</X>
selectOne节点属性和变化说明:
属性名 | 用途及说明 | 必填 | 取值 |
---|---|---|---|
id | 无意义 | N | 用户定义 |
dsKey | 同selectSet.dsKey | N | 用户定义 |
txRef | 无意义 | N | 用户定义 |
resultKey | 当前查询操作返回结果的在上下文参数中的存放key | N | 用户定义 |
resultType | 无意义 | N | 用户定义 |
resultMap | 无意义 | N | 用户定义 |
fetchSize | 每次查询的最大获取条数,默认255 | N | 用户定义 |
cacheUse | 缓存使用,此项将在缓存一节详细说明 | N | 用户定义 |
3. 内部selectVar标签的使用
示例3:
<sql-service id="myService3" dsKey="ds" txRef="tx_02">
<selectVar resultKey="{userName}">
select user_name from user where user_id = #{user_id}
</selectVar>
<return>
<property name="{userName}" value="{userName}"/>
</return>
</sql-service>
说明:
示例3中我们定义了一个组合SQL服务myService3
,其内部包含了了一个selectVar
基本服务,执行后返回结果是一个XCO
对象,其中包含了一个userName元素,XML格式如下:
返回结果
<?xml version="1.0" encoding="UTF-8"?>
<X>
<S K="userName" V="李四"/>
</X>
selectVar节点属性和变化说明:
属性名 | 用途及说明 | 必填 | 取值 |
---|---|---|---|
id | 无意义 | N | 用户定义 |
dsKey | 同selectSet.dsKey | N | 用户定义 |
txRef | 无意义 | N | 用户定义 |
resultKey | 当前查询操作返回结果的在上下文参数中的存放key | N | 用户定义 |
cacheUse | 缓存使用,此项将在缓存一节详细说明 | N | 用户定义 |
4. 内部update标签的使用
示例4:
<sql-service id="myService4" dsKey="ds" txRef="tx_02">
<update rowCount="{nCount}">
update user set user_name = '张三' where user_id = #{user_id}
</update>
<exception test="{nCount} != 1" code="-1" message="用户更新失败"/>
</sql-service>
说明:
示例4中我们定义了一个组合SQL服务myService4
,其内部包含了了一个update
基本服务,当程序执行完update
服务后会将影响行数以nCount为key,放入上下文中,然后通过exception
标签判断nCount的有效性,如果nCount不满足条件,则无服务将抛出服务异常,并回滚之前的操作。
返回结果: 无
update节点属性和变化说明:
属性名 | 用途及说明 | 必填 | 取值 |
---|---|---|---|
id | 无意义 | N | 用户定义 |
dsKey | 同selectSet.dsKey | N | 用户定义 |
txRef | 无意义 | N | 用户定义 |
rowCount | 当前更新操作的影响行数的在上下文参数中的存放key | N | 用户定义 |
cacheClear | 缓存使用,此项将在缓存一节详细说明 | N | 用户定义 |
5. 内部delete标签的使用
示例5:
<sql-service id="myService5" dsKey="ds" txRef="tx_02">
<delete rowCount="{nCount}">
delete from user where user_id = #{delete_user_id}
</delete>
<exception test="{nCount} != 1" code="-1" message="用户删除失败"/>
</sql-service>
说明
示例5中我们定义了一个组合SQL服务myService5
,其内部包含了了一个delete
基本服务,当程序执行完delete
服务后会将影响行数以nCount为key,放入上下文中,然后通过exception
标签判断nCount的有效性,如果nCount不满足条件,则无服务将抛出服务异常,并回滚之前的操作。
返回结果: 无
delete节点属性和变化说明:
属性名 | 用途及说明 | 必填 | 取值 |
---|---|---|---|
id | 无意义 | N | 用户定义 |
dsKey | 同selectSet.dsKey | N | 用户定义 |
txRef | 无意义 | N | 用户定义 |
rowCount | 当前更新操作的影响行数的在上下文参数中的存放key | N | 用户定义 |
cacheClear | 缓存使用,此项将在缓存一节详细说明 | N | 用户定义 |
6. 内部insert标签的使用
示例6:
<sql-service id="myService6" dsKey="ds" txRef="tx_02">
<insert incrementKey="{user_id}" rowCount="{nCount}">
insert into user(user_name, user_age, create_time) values('李四', 26, #{create_time|now()});
</insert>
<return>
<property value="{user_id}"/>
<property value="{nCount}"/>
</return>
</sql-service>
说明:
示例6中我们定义了一个组合SQL服务myService6,其内部包含了了一个insert
基本服务,当程序执行完insert
服务后会作2个操作:第一,将insert语句自动生成的主键以user_id为key,放入上下文中;第二将影响行数以nCount为key,放入上下文中,然后通过return
标签将其返回,XML格式如下:
返回结果
<?xml version="1.0" encoding="UTF-8"?>
<X>
<L K="user_id" V="10"/>
<I K="nCount" V="1"/>
</X>
insert节点属性和变化说明:
属性名 | 用途及说明 | 必填 | 取值 |
---|---|---|---|
id | 无意义 | N | 用户定义 |
dsKey | 同selectSet.dsKey | N | 用户定义 |
txRef | 无意义 | N | 用户定义 |
resultType | 无意义 | N | 用户定义 |
rowCount | 当前更新操作的影响行数的在上下文参数中的存放key | N | 用户定义 |
incrementKey | 当前插入操作返回插入后的主键(数据库自动生成的)的在上下文参数中的存放key。<br />关于返回主键,有以下几种情况:<br />3.插入一条记录,返回单个主键,其结果类型视主键的数据库数据类型而定。<br />2. 插入多条记录,返回多个主键数组,数组元素类型视主键的数据库数据类型而定。 | N | 用户定义 |
cacheClear | 缓存使用,此项将在缓存一节详细说明 | N | 用户定义 |
之前我们看到的示例都是一些很简单的组合SQL服务,下面我们看一个复杂的组合SQL服务示例。
7. 组合SQL的使用
示例7
<sql-service id="myService7" dsKey="ds" txRef="tx_02">
<selectSet resultKey="{users}">
select * from user
</selectSet>
<selectOne resultKey="{user}">
select * from user where user_id = #{user_id}
</selectOne>
<selectVar resultKey="{userName}">
select user_name from user where user_id = #{user_id}
</selectVar>
<update rowCount="{nCount}">
update user set user_name = '张三' where user_id = #{user_id}
</update>
<exception test="{nCount} != 1" code="-1" message="用户更新失败"/>
<delete rowCount="{nCount}">
delete from user where user_id = #{delete_user_id}
</delete>
<exception test="{nCount} != 1" code="-1" message="用户删除失败"/>
<insert incrementKey="{user_id}" rowCount="{nCount}">
insert into user(user_name, user_age, create_time) values('李四', 26, #{create_time|now()});
</insert>
<return>
<property value="{users}"/>
<property value="{user}"/>
<property value="{userName}"/>
<property value="{user_id}"/>
</return>
</sql-service>
返回结果:
<?xml version="1.0" encoding="UTF-8"?>
<X>
<XL K="users">
<X>
<L K="user_id" V="1"/>
<S K="user_name" V="张三"/>
<B K="user_age" V="18"/>
<A K="create_time" V="2016-10-20 21:30:58"/>
</X>
<X>
<L K="user_id" V="2"/>
<S K="user_name" V="张三"/>
<B K="user_age" V="18"/>
<A K="create_time" V="2016-10-20 21:31:58"/>
</X>
...
</XL>
<X K="user">
<L K="user_id" V="1"/>
<S K="user_name" V="张三"/>
<B K="user_age" V="18"/>
<A K="create_time" V="2016-10-20 21:30:58"/>
</X>
<S K="userName" V="张三"/>
<L K="user_id" V="11"/>
</X>
说明:
示例7其实就是整合之前的示例1到示例6,在一个服务中完成6个操作,并根据需要返回结果,就如同SQL中的存储过程,Java中的函数一般,这才是组合SQL服务的优势所在,通过一些基本服务的组合,和一些辅助标签,实现复杂的业务逻辑。使其开发人员即使不懂的JAVA也可完成大部分的服务开发工作。
8. 自定义返回结果
在组合SQL服务中可以通过return
标签来定义返回内容。return
标签的使用一般有下面两种情况:
示例8:
<sql-service id="myService" dsKey="ds" txRef="tx_02">
<selectSet resultKey="{users}">
select * from user
</selectSet>
<return>
<property value="{users}"/>
</return>
</sql-service>
说明:
示例8中return
标签表示返回一个封装后的对象(默认为XCO
类型),此对象中包含一个名为users的属性,值为List<XCO>
。
示例9:
<sql-service id="myService" dsKey="ds" txRef="tx_02">
<selectVar resultKey="{userName}">
select user_name from user where user_id = #{user_id}
</selectVar>
<return value="{userName}" />
</sql-service>
说明:
示例9中return
标签表示直接返回userName
所代表的值,此处为userName
的类型为String
。
上述两个示例说明了return
标签的两种使用方式,一种是返回一个封装对象,然后通过property子标签定义其内部具体属性和属性值。另一种是直接通过return
标签的value属性定义返回值,在这种情况下的返回类型由value属性中的变量所代表的值的类型来决定。注意,这两种方式不能混合使用,当然如果某个服务不需要返回类型,也可以不使用return
标签,但是如果使用return
标签,则只能有一个。
图片2Schema设计图
return节点属性说明:
属性名 | 用途及说明 | 必填 | 取值 |
---|---|---|---|
value | 需要直接返回的变量名称, 如value="{user}" | N | 用户定义 |
property节点属性说明:
属性名 | 用途及说明 | 必填 | 取值 |
---|---|---|---|
name | 返回对象中的属性名称,可省略,默认为value中的变量名称 | N | 用户定义 |
value | 代表属性值的变量名称 | Y | 用户定义 |
10. log标签的使用
日志打印标签,用于在服务执行过程中的检测和日志的输出。
示例10:
<sql-service>
<log message="更新用户:开始" level="info"/>
<update rowCount="{nCount}">...</update>
<log message="更新用户:结束"/>
<log message="更新班级:开始"/>
<update rowCount="{nCount}">...</update>
<log message="更新班级:结束"/>
</sql-service>
说明:
上述示例中使用4个log
标签,分别在update
操作前后,这样开发的时候可以方便的从日志中观察到服务的执行情况。
图片3Schema设计图
log节点属性说明:
属性名 | 用途及说明 | 必填 | 取值 |
---|---|---|---|
message | 日志内容,其中可使用变量;如:<log message="学生ID: {user_id}, 班级ID: {class_id}"/>
|
Y | 用户定义 |
level | 日志等级默认info,可参考log4j的日志等级 | N | error/warn/info/debug |
11. setvar标签的使用
用途:变量设置标签,在XML中给一个变量赋值。
示例11:
<sql-service>
<setvar key="{x}" value="0"/>
<if test="{type} == 1">
<selectOne resultKey="{x}">....</selectOne>
</if>
<return>
<property value="{x}"/>
</return>
</sql-service>
说明:
上述示例中先将比变量x
赋值为0,然后判断条件,如果type==1,则执行selectOne
操作,将执行结果重新赋值给变量x
,最后返回x
.
图片4Schema设计图
setvar节点属性说明:
属性名 | 用途及说明 | 必填 | 取值 |
---|---|---|---|
key | 变量名称,如:key="{x}" | Y | 用户定义 |
value | 变量值 | Y | 用户定义 |
type | 变量的数据类型,默认将会根据根据用户输入变量值自动分析其类型;比如:<br />value="0",类型为int<br />value="1.3",类型为float<br />value="true",类型为boolean<br />value="xxx",类型为String<br />value="yyyy-MM-dd HH:mm:ss",类型为dateTime<br />value="yyyy-MM-dd",类型为date<br />value="HH:mm:ss",类型为time | N | int<br />long<br />float<br />double<br />short<br />boolean<br />byte<br />char<br />dateTime<br />date<br />time<br /> |
12. transGroup标签的使用
transGroup
的作用就是启动一个新的事务,如果transGroup
中的服务执行失败将不会影响之前的事务。
示例12:
<sql-service id="myService" dsKey="ds" txRef="tx_02">
<update rowCount="{nCount}">
update user set user_name = '张三' where user_id = #{user_id}
</update>
<transGroup txRef="tx_03"> <!-- (1) -->
<insert incrementKey="{user_id}" rowCount="{nCount}">
insert into user(user_name, user_age, create_time) values('李四', 26, #{create_time|now()});
</insert>
</transGroup> <!-- (2) -->
<update rowCount="{nCount}"> <!-- (3) -->
update user set user_name = '李四' where user_id = #{user_id}
</update>
<return>...</return>
</sql-service>
说明:
上述示例中服务执行到(1)的时候,根据transGroup
所使用事务定义tx_03
,启动一个新事物Y,并将之前的事务X挂起,开始执行其内部的insert
服务,如果顺利的执行到(2)的位置,则提交事物Y,并恢复之前的X,继续执行后面的操作;一旦在此期间发生异常,则回滚事物Y,恢复之前的事物X,从(3)的位置继续执行。
注意:transGroup
所使用的事务定义的传播属性必须是requires_new
或者not_supported
。
图片5Schema设计图
transGroup节点属性说明:
属性名 | 用途及说明 | 必填 | 取值 |
---|---|---|---|
txRef | 所引用事务定义的id | Y | 用户定义 |
13. call标签的使用
服务内部调用标签,就是在一个组合服务内部调用其他服务,可以是基本服务,也可以是另一个组合服务。
示例13A:
<selectOne id="getUserById">
SELECT * from user WHERE user_id = #{user_id}
</selectOne>
<sql-service id="myService" dsKey="ds" txRef="tx_02">
<update rowCount="{nCount}">
update user set user_name = '张三' where user_id = #{user_id}
</update>
<call service="demo2.getUserById" resultKey="{user}"/>
<return>
<property value="{user}"/>
</return>
</sql-service>
说明:
上述示例中在myService
内部调用了getUserById
服务,调用指的是像JAVA函数之间的调用一样,有入参,有返回。当执行完call
标签后,会将其执行结果以user为key放入参数上下文中。细心的同学可能会有疑问,getUserById
服务是需要一个user_id
参数的,如何获取其值呢?其实在上述示例中myService
隐示的将自己的上下文和参数传递给getUserById
,myService
的参数中是存在user_id
,所以getUserById
服务可以顺利取到值。
通过下面这种方式,我们还可以显示的给getUserById
服务传递参数:
示例13B:
<call service="demo2.getUserById" resultKey="{user}">
<property value="{user_id}"/>
</call>
在这中方式下,调用getUserById
服务的时候仅仅给其传递了user_id
一个参数。
示例13C:
<call service="demo2.getUserById" resultKey="{user}" mode="EXTEND" exResultKey="{ex}"/>
说明:
示例13C中call标签出现了mode
属性和exResultKey
属性,mode
属性表示的是调用的模式,exResultKey
属性表示的是当调用发生异常后,将异常信息的code
和message
封装成一个新的对象(默认为XCO
对象),以ex为key放入参数上下文中,供后续程序使用。
注意: 要避免循环或递归调用。
图片6Schema设计图
call节点属性说明:
属性名 | 用途及说明 | 必填 | 取值 |
---|---|---|---|
service | 被调用的服务名称。格式为:命名空间+"."+服务id | Y | 用户定义 |
resultKey | 返回结果的key | N | 用户定义 |
mode | 调用模式,默认EXTEND<br />EXTEND:被调用方将继承调用方的上下文。<br />ALONE:被调用方有独立的上下文。<br />ASYNC:异步调用,被调用方有独立的上下文。 | N | EXTEND/ALONE/ASYNC |
exResultKey | 调用过程中发生异常后,将异常信息封装成对象code 和message 所存放的key |
N | 用户定义 |
property节点属性说明:
属性名 | 用途及说明 | 必填 | 取值 |
---|---|---|---|
name | 参数名,默认为value的变量名 | N | 用户定义 |
value | 参数值 | Y | 用户定义 |
14. exception标签的使用
异常标签,当满足其检测条件的时候会抛出服务异常ServiceException
示例14:
<sql-service id="myService4" dsKey="ds" txRef="tx_02">
<update rowCount="{nCount}">......</update>
<exception test="{nCount} != 1" code="-1" message="用户更新失败"/>
......
</sql-service>
说明:
对于exception标签的使用之前已经有过很多示例,这里我们着重介绍一些服务异常抛出后相关的处理,一般有以下几种情况:
- 当前上下文中运行这一个事务,对于这种情况tangyuan框架捕捉到异常后会先回滚当前事务,然后在将此异常继续上抛给调用方。对于调用方捕获到服务异常后可通过
ServiceException
对象的getErrorCode和getErrorMessage拿到错误码和错误描述。 - 当前上下文中存在挂起的事务,也就是说当前操作运行在一个独立的事务中,在这种情况下框架捕捉到异常后会先回滚当前事务,然后恢复最近挂起的服务,并继续执行。
图片7Schema设计图
exception节点属性说明:
属性名 | 用途及说明 | 必填 | 取值 |
---|---|---|---|
test | 逻辑表达式,同if标签的test属性。 | Y | 用户定义 |
code | 当抛出服务异常时所携带的错误码。 | N | 用户定义 |
message | 当抛出服务异常时所携带的错误描述。 | N | 用户定义 |
到此,第六章节的内容就结束了,感兴趣的朋友可以关注TangYuan项目。
- QQ群:518522232 *请备注关注的项目
- 邮箱:xson_org@126.com
- 项目地址: https://github.com/xsonorg/tangyuan
网友评论