2 Star 5 Fork 1

Blinkfox / zealot

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
Apache-2.0

Zealot

Build Status codecov Maven Central Javadoc

一个轻量级的SQL和参数动态生成工具库。文档地址

My life for Auir!

Zealot

一、创建初衷

SQL对开发人员来说是核心的资产之一,在开发中经常需要书写冗长、动态的SQL,许多项目中仅仅采用Java来书写动态SQL,会导致SQL分散、不易调试和阅读。所谓易于维护的SQL应该兼有动态性和可调试性的双重特性。在Java中书写冗长的SQL,虽然能很好的做到动态性,缺大大降低了SQL本身的可调试性,开发人员必须运行项目调试打印出SQL才能知道最终的SQL长什么样。所以为了做到可调试性,开发人员开始将SQL单独提取出来存放到配置文件中来维护,这样方便开发人员复制出来粘贴到SQL工具中来直接运行,但无逻辑功能的配置文件虽然解决了可调试性的问题,却又丧失了动态SQL的能力。所以,才不得不诞生出类似于mybatis这样灵活的半ORM工具来解决这两个的问题,但众多的项目却并未集成mybaits这样的工具。

Zealot是基于Java语言开发的SQL及对应参数动态拼接生成的工具包,其核心设计目标是帮助开发人员书写和生成出具有动态的、可复用逻辑的且易于维护的SQL及其对应的参数。为了做到可调试性,就必须将SQL提取到配置文件中来单独维护;为了保证SQL根据某些条件,某些逻辑来动态生成,就必须引入表达式语法或者标签语法来达到动态生成SQL的能力。因此,两者结合才诞生了Zealot。

:zealot即狂热者,是游戏星际争霸中的单位。

二、主要特性

  • 轻量级,jar包仅仅47k大小,集成和使用简单
  • 提供了纯Java代码或XML两种方式书写维护SQL
  • Java的方式采用流式API的方式书写动态SQL,易于书写阅读
  • XML的方式让SQL和Java代码解耦和,易于维护
  • 具有动态性、可复用逻辑和可半调试性的优点
  • 具有可扩展性,可自定义标签和处理器来完成自定义逻辑的SQL和参数生成

三、集成使用

1. 支持场景

适用于Java web项目,JDK1.6及以上

2. 安装集成

这里以Maven为例,Maven的引入方式如下:

<dependency>
    <groupId>com.blinkfox</groupId>
    <artifactId>zealot</artifactId>
    <version>1.1.2</version>
</dependency>

四、Java链式式之ZealotKhala

在Java中书写中等长度的SQL,用"+"连接的字符串尤其是动态字符串,会导致SQL的可读性极差且拼接性能较低,在Zealot v1.0.4版本中提供了一个额外高效的SQL字符串链式拼接工具Khala,但Khala只提供拼接字符串的功能,并不具有返回动态SQL和参数的特性,便决定在v1.1.0版本中新增了ZealotKhala,ZealotKhala也采用流式API的方式可以书写出更流畅的动态SQL,且会得到动态SQL的有序参数。其使用示例如下:

public class ZealotKhalaTest {

    /**
     * 测试使用ZealotKhala书写的sql.
     */
    @Test
    public void testSql() {
        String userName = "zhang";
        String startBirthday = "1990-03-25";
        String endBirthday = "2010-08-28";
        Integer[] sexs = new Integer[]{0, 1};

        SqlInfo sqlInfo = ZealotKhala.start()
                .select("u.id, u.name, u.email, d.birthday, d.address")
                .from("user AS u")
                .leftJoin("user_detail AS d").on("u.id = d.user_id")
                .where("u.id != ''")
                .andLike("u.name", userName)
                .doAnything(true, new ICustomAction() {
                    @Override
                    public void execute(final StringBuilder join, final List<Object> params) {
                        join.append("abc111");
                        params.add(5);
                        log.info("执行了自定义操作,可任意拼接字符串和有序参数...");
                    }
                })
                .andMoreThan("u.age", 21)
                .andLessThan("u.age", 13)
                .andMoreEqual("d.birthday", startBirthday)
                .andLessEqual("d.birthday", endBirthday)
                .andBetween("d.birthday", startBirthday, endBirthday)
                .andIn("u.sex", sexs)
                .orderBy("d.birthday").desc()
                .end();
        String sql = sqlInfo.getSql();
        Object[] arr = sqlInfo.getParamsArr();

        // 断言并输出sql信息
        assertEquals("SELECT u.id, u.name, u.email, d.birthday, d.address FROM user AS u "
                + "LEFT JOIN user_detail AS d ON u.id = d.user_id WHERE u.id != '' AND u.name LIKE ? "
                + "abc111 AND u.age > ? AND u.age < ? AND d.birthday >= ? AND d.birthday <= ? "
                + "AND d.birthday BETWEEN ? AND ? AND u.sex in (?, ?) ORDER BY d.birthday DESC", sql);
        assertArrayEquals(new Object[]{"%zhang%", 5, 21, 13, "1990-03-25", "2010-08-28",
                "1990-03-25", "2010-08-28", 0, 1} ,arr);
        log.info("testSql()方法生成的sql信息:" + sql + "\n参数为:" + Arrays.toString(arr));
    }

}

打印结果如下:

testSql()方法生成的sql信息:SELECT u.id, u.name, u.email, d.birthday, d.address FROM user AS u LEFT JOIN user_detail AS d ON u.id = d.user_id WHERE u.id != '' AND u.name LIKE ? abc111 AND u.age > ? AND u.age < ? AND d.birthday >= ? AND d.birthday <= ? AND d.birthday BETWEEN ? AND ? AND u.sex in (?, ?) ORDER BY d.birthday DESC
参数为:[%zhang%, 5, 21, 13, 1990-03-25, 2010-08-28, 1990-03-25, 2010-08-28, 0, 1]

五、XML方式之Zealot

对于很长的动态或统计性的SQL采用Java书写会不易于维护和调试,因此更推荐你通过xml文件来书写sql,使得SQL和Java代码解耦,易于维护和阅读。

配置使用

在你的Java web项目项目中,创建一个继承自AbstractZealotConfig的核心配置类,如以下示例:

package com.blinkfox.config;

import com.blinkfox.zealot.bean.XmlContext;
import com.blinkfox.zealot.config.AbstractZealotConfig;

/**
 * 我继承的zealotConfig配置类
 * Created by blinkfox on 2016/11/4.
 */
public class MyZealotConfig extends AbstractZealotConfig {

    @Override
    public void configXml(XmlContext ctx) {
        
    }

    @Override
    public void configTagHandler() {

    }

}

代码解释

(1). configXml()方法主要配置你自己SQL所在XML文件的命名标识和对应的路径,这样好让zealot能够读取到你的XML配置的SQL文件;

(2). configTagHandler()方法主要是配置你自定义的标签和对应标签的处理类,当你需要自定义SQL标签时才配置。

然后,在你的web.xml中来引入zealot,这样容器启动时才会去加载和缓存对应的xml文档,示例配置如下:

<!-- zealot相关配置的配置 -->
<context-param>
   <!-- paramName必须为zealotConfigClass名称,param-value对应刚创建的Java配置的类路径 -->
   <param-name>zealotConfigClass</param-name>
   <param-value>com.blinkfox.config.MyZealotConfig</param-value>
</context-param>
<!-- listener-class必须配置,JavaEE容器启动时才会执行 -->
<listener>
   <listener-class>com.blinkfox.zealot.loader.ZealotConfigLoader</listener-class>
</listener>

如果你不是Java web项目,或者你就想通过Java代码来初始化加载zealot的配置信息,可以这样来做:

ZealotConfigManager.getInstance().initLoad(MyZealotConfig.class);

接下来,就开始创建我们业务中的SQL及存放的XML文件了,在你项目的资源文件目录中,不妨创建一个管理SQL的文件夹,我这里取名为zealotxml,然后在zealotxml文件夹下创建一个名为zealot-user.xml的XML文件,用来表示用户操作相关SQL的管理文件。在XML中你就可以创建自己的SQL啦,这里对user表的两种查询,示例如下:

<?xml version="1.0" encoding="UTF-8"?>
<zealots>

    <!-- 根据Id查询用户信息 -->
    <zealot id="queryUserById">
        select * from user where
        <equal field="id" value="id"/>
    </zealot>
    
    <!-- 根据动态条件查询用户信息 -->
    <zealot id="queryUserInfo">
        select * from user where
        <like field="nickname" value="nickName"/>
        <andLike match="?email != empty" field="email" value="email"/>
        <andBetween match="?startAge > 0 || ?endAge > 0" field="age" start="startAge" end="endAge"/>
        <andBetween match="?startBirthday != empty || ?endBirthday != empty" field="birthday" start="startBirthday" end="endBirthday"/>
        <andIn match="?sexs != empty" field="sex" value="sexs"/>
        order by id desc 
    </zealot>
    
</zealots>

代码解释

(1). zealots代表根节点,其下每个zealot表示你业务中需要书写的一个完整SQL。

(2). 其中的likeandLikeandBetweenandIn等标签及属性表示要动态生成的sql类型,如:等值查询、模糊查询、In查询、区间查询等;

(3). 标签中的属性match表示匹配到的条件,如果满足条件就生成该类型的SQL,不匹配就不生成,从而达到了动态生成SQL的需求,如果不写match,则表示必然生成;

(4). field表示对应的数据库字段;

(5). value、start、end则表示对应的参数。

(6). ?email != empty前面的?表示属性的安全访问,即使email不存在的时候也不会抛出异常,仍会返回false。更详细的使用可以参考MVEL属性安全访问的表达式语法。

回到你的Zealot核心配置类中,配置你Java代码中需要识别这个XML的标识和XML路径,我这里的示例如下:

package com.blinkfox.config;

import com.blinkfox.zealot.bean.XmlContext;
import com.blinkfox.zealot.config.AbstractZealotConfig;

/**
 * 我继承的zealotConfig配置类
 * Created by blinkfox on 2016/11/4.
 */
public class MyZealotConfig extends AbstractZealotConfig {

    public static final String USER_ZEALOT = "user_zealot";

    @Override
    public void configXml(XmlContext ctx) {
        ctx.add(USER_ZEALOT, "/zealotxml/zealot-user.xml");
    }

    @Override
    public void configTagHandler() {

    }

}

代码解释

(1). ctx.add(USER_ZEALOT, "/zealotxml/zealot-user.xml");代码中第一个参数USER_ZEALOT表示的是对XML做唯一标识的自定义静态常量,第二个参数就是你创建的对应的XML的资源路径。

最后,就是一个UserController的调用测试类,这里的目的用来调用执行,测试我们前面配置书写的SQL和参数,参考代码如下:

/**
 * 用户信息相关的控制器
 * Created by blinkfox on 16/7/24.
 */
public class UserController extends Controller {

    public void queryUserById() {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("id", "2");
        
        SqlInfo sqlInfo = Zealot.getSqlInfo(MyZealotConfig.USER_ZEALOT, "queryUserById", paramMap);
        String sql = sqlInfo.getSql();
        Object[] params = sqlInfo.getParamsArr();

        List<User> users = User.userDao.find(sql, params);
        renderJson(users);
    }

    public void userZealot() {
        // 构造测试需要的动态查询的参数
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("nickName", "张");
        paramMap.put("email", "san");
        paramMap.put("startAge", 23);
        paramMap.put("endAge", 28);
        paramMap.put("startBirthday", "1990-01-01 00:00:00");
        paramMap.put("endBirthday", "1991-01-01 23:59:59");
        paramMap.put("sexs", new Integer[]{0, 1});
        
        // 执行Zealot方法,得到完整的SQL和对应的有序参数
        SqlInfo sqlInfo = Zealot.getSqlInfo(MyZealotConfig.USER_ZEALOT, "queryUserInfo", paramMap);
        String sql = sqlInfo.getSql();
        Object[] params = sqlInfo.getParamsArr();

        // 执行SQL查询并返回结果
        List<User> users = User.userDao.find(sql, params);
        renderJson(users);
    }

}

代码解释

(1). 说明一下,我测试项目中采用的框架是JFinal,你自己具体的项目中有自己的SQL调用方式,而Zealot的目的只是生成SQL和对应的有序参数而已。

(2). Zealot.getSqlInfo()方法有三个参数,第一个参数表示前面所写的XML的标识名称,第二个表示你XML中具体想生成的SQL的zealot id,第三个表示生成动态SQL的参数对象,该对象可以是一个普通的Java对象,也可以是Map等。

结果,第二个方法userZealot()中生成的SQL和参数打印如下:

生成SQL结果

----生成sql的为:select * from user where nickname LIKE ? AND email LIKE ? AND age BETWEEN ? AND ? AND birthday BETWEEN ? AND ? AND sex in (?, ?) order by id desc

----生成sql的参数为:[%张%, %san%, 23, 28, 1990-01-01 00:00:00, 1991-01-01 23:59:59, 0, 1]

六、Zealot SQL配置

Zealot的核心功能就在于它XML格式的 SQL配置文件。配置文件也仅仅是一个普通的XML文件,在XML中只需要少许的配置就可以动态生成自己所需要的查询条件。在XML中zealots标签作为根标签,其中的zealot则是一个独立SQL的元素标签,在zealot标签中才包含likeandLikeandBetweenandIn等条件标签,以下重点介绍各条件标签。

Zealot中默认自带了以下4类条件标签,分别是:equallikebetweenin,分别对应着SQL查询中的等值匹配条件、模糊匹配条件、区间匹配条件以及范围匹配条件;四类条件标签又各自额外附带了两个连接前缀,分别是:andor,用于表示逻辑的情形,这两者更为常用,目前还未加入的情形。所以,zealot中总共带有12个条件标签,各标签的属性和生成SQL的示例如下:

1. equal、andEqual、orEqual 标签介绍

(1). 属性介绍

  • match,表示匹配条件。非必要(填)属性,如果不写(填)此属性,则视为必然生成此条件SQL片段;否则匹配结果为true时才生成,匹配结果为false时,不生成。匹配规则使用MVEL2表达式,关于MVEL的语法文档参考这里。
  • field,表示对应数据库的字段,可以是数据库的表达式、函数等。必要(填)属性。
  • value,表示参数值,对应Java中的名称。必要(填)属性,值也是使用MVEL2做解析。

(2). 生成示例

标签:<equal field="nickname" value="nickName"></equal>
SQL片段的生成结果:nickname = ?
解释:必然生成此条SQL片段和参数

<andEqual match="?email != empty" field="email" value="email"></andEqual>
SQL片段的生成结果:AND email = ?
解释:如果email不等于空时,才生成此条SQL片段和参数

(3). 与Equal类似的标签

  • moreThan 大于
  • andMoreThan 带and关键字的大于
  • orMoreThan 带or关键字的大于
  • lessThan 小于
  • andLessThan 带and关键字的小于
  • orLessThan 带or关键字的小于
  • moreEqual 大于等于
  • andMoreEqual 带and关键字的大于等于
  • orMoreEqual 带or关键字的大于等于
  • lessEqual 小于等于
  • andLessEqual 带and关键字的小于等于
  • orLessEqual 带or关键字的小于等于

2. like、andLike、orLike 标签介绍

(1). 属性介绍

  • match,同上。
  • field,同上。
  • value,同上。

(2). 生成示例

<andLike match="?email != empty" field="email" value="email"></andLike>

SQL片段的生成结果:AND email LIKE ?

解释:如果email不等于空时,才生成此条SQL片段和参数

3. between、andBetween、orBetween 标签介绍

(1). 属性介绍

  • match,同上。
  • field,同上。
  • start,表示区间匹配条件的开始参数值,对应Java中的名称,条件必填。
  • end,表示区间匹配条件的结束参数值,对应Java中的名称,条件必填。

注意:Zealot中对start和end的空判断是检测是否是null,而不是空字符串,0等情况。所以,对start和end的空处理应该是null。

(2). 生成示例

<andBetween match="?startAge != null || ?endAge != null" field="age" start="startAge" end="endAge"></andBetween>

start为null,end不为null,则SQL片段的生成结果:AND age >= ?
start不为null,end为null,则SQL片段的生成结果:AND age <= ?
start不为null,end不为null,则SQL片段的生成结果:AND age BETWEEN ? AND ?
start为null,end为null,则不生成SQL片段

**解释**:match标签是非必填的,区间查询中,靠start和end两种条件也可以组成一个简单的动态情形。如果start为空,end不为空,则是大于等于查询;如果start为空,end不为空,则是小于等于查询;如果start、end均不为空,则是区间查询;两者会均为空则不生产此条sql。

4. in、andIn、orIn 标签介绍

(1). 属性介绍

  • match,同上。
  • field,同上。
  • value,表示参数的集合,值可以是数组,也可以是Collection集合,还可以是单个的值。必填

(2). 使用生成示例

<andIn match="?sexs != empty" field="sex" value="sexs"></andIn>

SQL片段的生成结果:AND sex in (?, ?)

解释:如果sexs不等于空时,才生成此条SQL片段和参数(这里的sexs假设有两个值)

5. text 标签介绍

text标签主要用于在标签内部自定义需要的文本和需要传递的各种参数,为SQL书写提供灵活性

(1). 属性介绍

  • match,同上。
  • value,表示参数的集合,值可以是数组,也可以是Collection集合,还可以是单个的值。必填

(2). 使用生成示例

<text match="" value="{name1, name2, email}">
    and name in (?, ?)
    and email = ?
</text>

SQL片段的生成结果:and name in (?, ?) and email = ?

解释:如果match为true、不填写或无match标签时,才生成此条SQL片段和自定义传递的参数,参数就是通过`name1`、`name2`和`email`组合成的数组或集合,或者直接传递集合或数组(此处组合而成的数组,如果是集合就把'{'换成'['即可)。

五、自定义标签和处理器

从前面所知,条件标签是生成动态SQL和参数的核心,但是项目开发的过程中往往有更多多复杂的逻辑来生成某些SQL,甚至那些逻辑还要被多处使用到,默认的一些标签不能够满足开发需求,那么自定义自己的动态条件标签来实现就显得很重要了。所谓自定义标签和处理器就是设置自定义的标签名称、匹配条件、参数和数据库字段等,再通过自定义的处理器来控制生成SQL的逻辑,这样就可以达到生成我们需要的SQL的功能,这样的标签重大的意义在于能够最大化简化sql的书写和功能的复用。

1. 假设查询需求

假设user表中有id、email两个字段,后台封装了一个User的参数,其中包含userId和usermail的属性。如果userId不为空时,则根据id来等值查询;如果userId为空,usermail不为空时,则根据email来做模糊查询;此处也隐含的说明了如果userId和usermail均不为空时,仍然以id来做等值查询。对此需求查询我们仍然可以用前面的标签组合来实现。假如很多地方都需要这种逻辑的查询,那我们可以使用自定义的标签来实现和复用这种查询逻辑。

2. 使用方式示例

(1). 在XML中定义标签及属性

根据上面的查询需求,可以分析出标签属性具有有idemail两个数据库字段,userId和userEmail的两个Java参数值,可设置其标签属性分别为idValueemailValue,因此标签为:

<zealot id="queryUserWithIdEmail">
    select * from user where
    <userIdEmail match="?userId != empty || ?userEmail != empty" idField="id" emailField="email" idValue="userId" emailValue="userEmail"></userIdEmail>
</zealot>

(2). 自定义标签处理器

在你项目的某个package中,新建一个UserIdEmailHandler.java的文件,并让它实现IConditHandler接口,细节的代码处理逻辑和注释说明如下:

package com.blinkfox.handler;

import com.blinkfox.zealot.bean.BuildSource;
import com.blinkfox.zealot.bean.SqlInfo;
import com.blinkfox.zealot.consts.ZealotConst;
import com.blinkfox.zealot.core.IConditHandler;
import com.blinkfox.zealot.helpers.ParseHelper;
import com.blinkfox.zealot.helpers.StringHelper;
import com.blinkfox.zealot.helpers.XmlNodeHelper;
import org.dom4j.Node;
import java.util.List;

/**
 * 自定义的ID和Email条件查询的SQL处理器
 * Created by blinkfox on 2016/11/11.
 */
public class UserIdEmailHandler implements IConditHandler {

    @Override
    public SqlInfo buildSqlInfo(BuildSource source) {
        /* 获取拼接的参数和Zealot节点 */
        SqlInfo sqlInfo = source.getSqlInfo();
        Node node = source.getNode();

        // 获取配置的属性值,getAndCheckNodeText()方法会检测属性值是否为空,如果为空,会抛出运行时异常
        String idField = XmlNodeHelper.getAndCheckNodeText(node, "attribute::idField");
        String emailField = XmlNodeHelper.getAndCheckNodeText(node, "attribute::emailField");
        // getAndCheckNodeText()方法仅仅只获取属性的值,即使未配置或书写值,也会返回空字符串
        String idValue = XmlNodeHelper.getNodeAttrText(node, "attribute::idValue");
        String emailValue = XmlNodeHelper.getNodeAttrText(node, "attribute::emailValue");

        /* 获取match属性值,如果匹配中 字符值没有,则认为是必然生成项 */
        String matchText = XmlNodeHelper.getNodeAttrText(node, ZealotConst.ATTR_MATCH);
        if (StringHelper.isBlank(matchText)) {
            sqlInfo = buildIdEmailSqlInfo(source, idField, emailField, idValue, emailValue);
        } else {
			/* 如果match匹配成功,则生成数据库sql条件和参数 */
            Boolean isTrue = (Boolean) ParseHelper.parseWithMvel(matchText, source.getParamObj());
            if (isTrue) {
                sqlInfo = buildIdEmailSqlInfo(source, idField, emailField, idValue, emailValue);
            }
        }

        return sqlInfo;
    }

    /**
     * 构建自定义的SqlInfo信息,区分是根据id做等值查询,还是根据email做模糊查询的情况
     * @param source source
     * @param idField idField
     * @param emailField emailField
     * @param idValue idValue
     * @param emailValue emailValue
     */
    private SqlInfo buildIdEmailSqlInfo(BuildSource source, String idField, String emailField,
            String idValue, String emailValue) {
        SqlInfo sqlInfo = source.getSqlInfo();
        StringBuilder join = sqlInfo.getJoin();
        List<Object> params = sqlInfo.getParams();

        // 如果userId不为空,则根据id来做等值查询
        Integer idText = (Integer) ParseHelper.parseWithMvel(idValue, source.getParamObj());
        if (idText != null) {
            // prefix是前缀,如"and","or"之类,没有则默认为空字符串""
            join.append(source.getPrefix()).append(idField).append(ZealotConst.EQUAL_SUFFIX);
            params.add(idText);
            return sqlInfo.setJoin(join).setParams(params);
        }

        // 获取userEmail的值,如果userEmail不为空,则根据email来做模糊查询
        String emailText = (String) ParseHelper.parseWithMvel(emailValue, source.getParamObj());
        if (StringHelper.isNotBlank(emailText)) {
            // prefix是前缀,如"and","or"之类,没有则默认为空字符串""
            join.append(source.getPrefix()).append(emailField).append(ZealotConst.LIEK_SUFFIX);
            params.add("%" + emailText + "%");
            return sqlInfo.setJoin(join).setParams(params);
        }

        return sqlInfo;
    }

}

(3). 配置自定义的标签和处理器

在你继承的Zealot Java配置文件方法中添加配置自定义的标签和处理器,重启即可,代码示例如下:

/**
 * 我继承的zealotConfig配置类
 * Created by blinkfox on 2016/11/4.
 */
public class MyZealotConfig extends AbstractZealotConfig {

    public static final String USER_ZEALOT = "user_zealot";

    @Override
    public void configXml(XmlContext ctx) {
        ctx.add(USER_ZEALOT, "/zealot/zealot-user.xml");
    }

    @Override
    public void configTagHandler() {
        // 自定义userIdEmail标签和处理器
        add("userIdEmail", UserIdEmailHandler.class);
        // 有and前缀的自定义标签
        add("andUserIdEmail", " and " ,UserIdEmailHandler.class);
    }

}

(4). 测试生成结果

测试代码和结果如下:

public void queryUserIdEmail() {
    Map<String, Object> user = new HashMap<String, Object>();
    user.put("userId", 3);
    user.put("userEmail", "san");

    SqlInfo sqlInfo = Zealot.getSqlInfo(MyZealotConfig.USER_ZEALOT, "queryUserWithIdEmail", user);
    String sql = sqlInfo.getSql();
    Object[] params = sqlInfo.getParamsArr();
    System.out.println("----生成sql的为:" + sql);
    System.out.println("----生成sql的参数为:" + Arrays.toString(params));

    List<User> users = User.userDao.find(sql, params);
    renderJson(users);
}

打印的sql结果:

----生成sql的为:select * from user where id = ?
----生成sql的参数为:[3]

当把userId的值设为null时,打印的sql结果:

----生成sql的为:select * from user where email LIKE ?
----生成sql的参数为:[%san%]

七、流程控制标签

由于Zealot中SQL片段生成的标签大多是工具库预设或自定义的,但是在实现更为灵活的逻辑控制时就显得力不从心了,如果都通过自定义标签去实现更灵活多变的逻辑会显得很麻烦。因此,决定在1.0.6的版本中增加更为强大灵活的流程逻辑控制标签。自由的流程逻辑控制方式就意味着难以实现绑定变量参数的方式来生成SQL,而是即时生成替换变量值后的SQL。

1. 使用示例

Zealot中SQL片段标签和流程控制标签是可以混合使用的,看下面的SQL书写方式即可容易理解:

<!-- 根据流程控制标签查询用户信息 -->
<zealot id="queryUsersByFlowTag">
    select * from user where
    <like field="nickname" value="nickName"/>
    @if{?email != empty}
        AND email like '%@{email}%'
    @end{}
    order by id desc
</zealot>

当email不为空时就会生成类似如下的SQL了:

select * from user where nickname LIKE ? AND email like '%zhang%' order by id desc

2. 常用流程控制标签介绍

Zealot的流程控制标签使用的是MVEL模板标签,所以,支持所有MVEL2.0的模板标签,这也正体现了Zealot动态SQL的强大特性。关于MVEL2.x的模板更详细的介绍请参考这里

(1). @{} 表达式

@{}表达式是最基本的形式。它包含一个对字符串求值的值表达式,并附加到输出模板中。例如:

Hello, my name is @{person.name}

(2). @code{} 静默代码标签

静默代码标记允许您在模板中执行MVEL表达式代码。它不返回值,并且不以任何方式影响模板的格式。

@code{age = 23; name = 'John Doe'}
@{name} is @{age} years old

该模板将计算出:John Doe is 23 years old。

(3). @if{}@else{} 控制流标签

@if{}和@else{}标签在MVEL模板中提供了完全的if-then-else功能。 例如:

@if{foo != bar}
   Foo not a bar!
@else{bar != cat}
   Bar is not a cat!
@else{}
   Foo may be a Bar or a Cat!
@end{}

MVEL模板中的所有块必须用@end{}标签来终止,除非是if-then-else结构,其中@else{}标记表示前一个控制语句的终止。

(4). @foreach{} Foreach迭代

foreach标签允许您在模板中迭代集合或数组。 注意:foreach的语法已经在MVEL模板2.0中改变,以使用foreach符号来标记MVEL语言本身的符号。

@foreach{item : products} 
 - @{item.serialNumber}
@end{}

八、许可证

Zealot类库遵守Apache License 2.0 许可证

九、版本更新记录

  • v1.1.2(2017-04-22)
    • 新增了zealot加载完成时的banner显示
    • 新增或升级了一些pom文件中的插件,如:pmd、reports等
    • 其他代码小细节修改
  • v1.1.1(2017-04-16)
    • 新增了ZealotKhala和xml标签的常用API,如:大于、小于、大于等于、小于等于等功能。
    • 新增了Zealot中xml的text标签,使灵活性SQL拼接灵活性更强
    • 新增了ZealotKhala的ICustomAction接口,使自定义的逻辑也能够通过链式写法完成,使SQL拼接逻辑更紧凑
    • 标记Khala.java为推荐使用,即@Deprecated。推荐使用ZealotKhala.java`,使SQL的动态性、灵活性更强。
  • v1.1.0(2017-04-04)
    • 新增了ZealotKhala,使ZealotKhala用Java也可以链式的书写动态SQL,和Zealot的XML标签相互应
  • v1.0.7(2017-03-31)
    • 使用Google CheckStyle来规范Java代码风格,重构了部分代码,使代码更整洁
    • Khala字符串的链式拼接去掉了手动newInstance的方式,直接调用start()方法即可
  • v1.0.6(2016-12-31)
    • 新增灵活强大的流程逻辑控制标签
    • 新增自定义标签的示例和单元测试
  • v1.0.5(2016-12-29)
    • 新增Zealot基本功能的单元测试
    • 重构Zealot缓存加载的代码
    • 新增了Khala的获取实例的方法
  • v1.0.4(2016-11-12)
    • 新增了SQL字符串链式拼接工具类Khala.java
  • v1.0.3(2016-11-11)
    • 修复了区间查询大于或等于情况下的bug
    • XmlNodeHelper中新增getNodeAttrText()方法
  • v1.0.2(2016-11-10)
    • 将缓存文档改为缓存Zealot节点,使生成sql效率更快
    • 代码细节重构调整
  • v1.0.1(2016-11-08)
    • 新增日志功能,替换System.out
    • 新增自定义异常
    • 完善文档注释
  • v1.0.0(2016-11-04)
    • 核心功能完成
Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

简介

一个轻量级的SQL和参数动态生成工具库 展开 收起
Java
Apache-2.0
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/chenjiayin1990/zealot.git
git@gitee.com:chenjiayin1990/zealot.git
chenjiayin1990
zealot
zealot
master

搜索帮助