Jdao v2.1.0: 如何构建更高效灵活的动态SQL


在复杂应用环境中,数据访问层的设计不仅需要高效,还需要具备灵活性。为了满足对数据库操作的高性能与灵活需求,Jdao提供了全面的持久层解决方案。

Jdao 是Java持久层框架,它结合了Hibernate的抽象性和MyBatis的灵活性。它解决Hibernate和MyBatis的痛点,如过度封装、复杂配置以及SQL维护的挑战。它既适合小型项目,也能胜任大型企业应用,帮助团队在不同场景中灵活应对持久层开发需求

在版本 v2.1.0 中,Jdao特别加强了动态SQL的构建功能,使其更加高效和灵活。本文重点介绍Jdao v2.1.0中动态SQL功能,并展示如何利用这些新特性来构建更高效的数据库操作。


动态SQL的重要性及Jdao的动态SQL简介

动态SQL允许根据不同的条件或需求动态地生成SQL语句,这对于处理那些条件时有时无、逻辑复杂多变的场景尤为重要。在Jdao中,动态SQL的实现不仅提高了代码的灵活性和可维护性,还增强了SQL语句的安全性,避免了SQL注入等风险。

jdao提供了3种方式构建动态SQL

  • 实体类构建动态 SQL
  • XML 映射动态SQL标签
  • SqlBuilder 基于java程序构建动态SQL


Jdao提供了构建动态SQL的 if,where,choose,foreach,set,trim等系列标签。

注意:Jdao 没有提供bind标签和$变量替换。原因是实践中,bind标签与$ 会带来一些问题

  • bind标签
    • 不常用:在实际开发中,bind标签并不常用,因为它提供的功能可以通过其他方式(比如使用<if>标签结合#{}参数化方式)来实现,而且这种方式更加安全和直观。
    • 维护问题:bind绑定通常将java编码转移到xml文件上,导致更加难以维护,和更容易导致错误。
  • $符号
    • 安全性问题:进行变量替换是SQL注入攻击的源头,如果参数包含恶意构造的SQL语句,则可能会被数据库引擎当作合法的SQL命令执行
    • 可以代替: 通常$变量替换可以通过其他方式实现。它的存在并不是必需的。即使无法通过其他配置的方式实现,也可以通过SqlBuilder实现动态sql构建。


Jdao动态sql性能

   动态sql构建效率通常受到构建条件的影响,复杂多条件,多分支的构建条件,程序需要执行更多的判断与基础数据查询,可能导致动态Sql总体执行更慢,这是客观的因素,但是这情况通常不是总体性能决定因素。性能的关键在于底层的实现,条件表达式的分析等;Jdao底层实现了非常高效的sql动态构建性能,它的性能几乎接近直接原生Sql查询的性能。具体的压测数据,请查询Jdao使用文档的jdao性能压测 章节。


Jdao动态SQL快速入门示例


安装 jdao依赖包
# 使用 Maven 安装
<dependency>
	<groupId>io.github.donnie4w</groupId>
	<artifactId>jdao</artifactId>
	<version>2.1.0</version>
</dependency>

Xml配置动态sql构建,兼容mybatis动态Sql标签

  • 示例程序1
    <!-- where if -->
    <select id="demo1" resultType="io.github.donnie4w.jdao.dao.Hstest">
        SELECT * FROM hstest
        <where>
            <if test="rowname!= 'hello'">
                and rowname = #{rowname}
            </if>
            <if test="id >0">
                AND id = #{id}
            </if>
        </where>
    </select>
    //测试 where if标签
    @Test
    public void demo1() throws JdaoException, JdaoClassException, SQLException {
        Hstest hs = new Hstest();
        hs.setId(31);
        hs.setRowname("hello");
        List<Hstest> list = jdaoMapper.selectList("io.github.donnie4w.jdao.action.Dynamic.demo1", hs);
    }

执行日志

SELECTLIST SQL[SELECT * FROM hstest WHERE id = ?]ARGS[31]


  • 示例程序2
    <!-- trim -->
    <select id="demo2" resultType="io.github.donnie4w.jdao.dao.Hstest">
        SELECT * FROM hstest
        <trim prefix="WHERE" prefixOverrides="AND|OR">
            <if test="rowname != null">
                AND rowname = #{rowname}
            </if>
            <if test="id != null">
                AND id = #{id}
            </if>
        </trim>
    </select>
    //测试 trim
    @Test
    public void demo2() throws JdaoException, JdaoClassException, SQLException {
        Hstest hs = new Hstest();
        hs.setId(31);
        hs.setRowname("hello");
        List<Hstest> list = jdaoMapper.selectList("io.github.donnie4w.jdao.action.Dynamic.demo2", hs);
    }

执行日志

SELECTLIST SQL[SELECT * FROM hstest WHERE rowname = ? AND id = ?]ARGS[hello, 31]

SqlBuilder 动态SQL构建

示例1
@Test
public void testAppendIf() throws JdaoException, SQLException {
   Map<String, Object> context = new HashMap<>();
   context.put("id", 31);

   SqlBuilder builder = SqlBuilder.newInstance();
   builder.append("SELECT * FROM HSTEST where 1=1")
          .appendIf("id>0", context, "and id=?", context.get("id"))  //若表达式id>0成立, 添加SQL : and id=?
          .append("ORDER BY id ASC");

   List<DataBean> list = builder.selectList();

}
  • 说明:
    • append  添加sql语句,没有条件表达式。
    • appendIf 通过条件表达式判断是否添加sql语句,if 条件 功能等同 if标签

执行日志

信息: [SqlBuilder SQL] SELECT * FROM HSTEST where 1=1  and id=? ORDER BY id ASC [ARGS][31]


示例2
@Test
public void testAppendChoose() throws JdaoException, SQLException {
    Hstest hstest = new Hstest();
    hstest.setRowname("www>>>>2");
    hstest.setId(31);
    hstest.setValue("2421209491375900");
    hstest.setAge(31);

    SqlBuilder builder = SqlBuilder.newInstance();
    builder.append("SELECT * FROM HSTEST where 1=1")
            .appendChoose(hstest, choose -> choose
                    .when(" age <30 && age>10", "AND age =?", hstest.getAge())
                    .when("rowname!=null", "AND rowname like ?", "%" + hstest.getRowname() + "%")
                    .otherwise("AND id >0")).append("limit 2");

    List<DataBean> list = builder.selectList();

}
  • 说明
    • appendChoose 通过条件表达式判断是否添加sql语句,switch条件  功能等同  choose标签

执行日志

信息: [SqlBuilder SQL] SELECT * FROM HSTEST where 1=1  AND rowname like ?  limit 2 [ARGS][%www>>>>2%]
示例3
//appendForeach
@Test
public void testAppendForeach() throws JdaoException, SQLException {
    SqlBuilder builder = SqlBuilder.newInstance();
    builder.append("SELECT * FROM hstest where id in")
            .appendForeach(null, new int[]{31, 32, 33}, "item", ",", "(", ")", foreachBuilder -> foreachBuilder
                    .body("#{item}")
            );
    List<DataBean> list = builder.selectList();
    for (DataBean bean : list) {
        System.out.println(bean);
    }
}
  • 说明
    • appendForeach 可循环拼接 appendForeach中的内容,如in(?,?,?,?)   .  foreach条件 功能等同  foreach标签

执行日志

信息: [SqlBuilder SQL] SELECT * FROM hstest where id in (?,?,?)[ARGS][31, 32, 33]



JDAO 简介

Jdao是一种创新的持久层解决方案。主要目的在于 减少编程量,提高生产力,提高性能,支持多数据源整合操作,支持数据读写分离,制定持久层编程规范。 灵活运用Jdao,可以在持久层设计上,减少30%甚至50%以上的编程量,同时形成持久层的统一编程规范,减少持久层错误,同时易于维护和扩展。
Jdao的映射模块实现了SQL与程序分离的特性,映射模块与myBatis的核心功能相同。是除了mybatis和基于myBatis系列的orm外,唯一实现该特性的orm框架.


GitHub :  Jdao Repository
示例程序: Jdaodemo
使用文档: Jdaodoc

主要特点
  1. 轻量:Jdao没有任何依赖,所有特性均为Jdao本身实现,不会由于其他项目的更新或功能修改而受到影响,可以轻松融入各类项目中。
  2. 高效:Jdao功能均自身实现最重要目的就是实现高效性,没有多余的包袱。性能与直接JDBC调用极为接近,ORM封装的性能损耗非常小。
  3. 灵活:Jdao支持丰富的动态SQL构建功能,包括原生动态SQL,映射动态SQL,实体类动态SQL。
  4. 安全:Jdao没有SQL注入的风险。Jdao提供了Mybatis相同的映射功能,并去掉了sql注入的潜在隐患。
  5. 全面:Jdao结合了Hibernate的抽象性和MyBatis的灵活性,提供规范且高效的ORM运用方案。


主要功能
  1. 生成代码:运行jdao代码生成工具,创建数据库表的标准化实体类。类似thrift/protobuf
  2. 高效序列化:表的标准化实体类实现了高效的序列化与反序列化。比标准库序列化方法高3-12倍,而序列化体积只有其20%左右。
  3. 支持数据读写分离:jdao支持绑定多数据源,并支持数据源绑定表,类,映射接口等属性。并支持数据读写分离
  4. 支持数据缓存:jdao支持数据缓存,并支持对缓存数据时效,与数据回收等特性进行细致控制
  5. 广泛兼容性:jdao理论上支持所有实现JDBC接口的数据库
  6. 高级特性:支持事务,存储过程,批处理等数据库操作
  7. 支持动态SQL:jdao实现了mybatis的动态sql功能,去掉了安全隐患及无实用特性。同时为原生SQL提供了编写动态SQL的特性。
  8. 兼容myBatis:jdao在映射模块中,采用了mybatis的标签定义,并实现了相同的功能特性。

更多Jdao的详细信息,请查阅 Jdao使用文档

更多Jdao使用示例,请参考 jdaodemo