在复杂应用环境中,数据访问层的设计不仅需要高效,还需要具备灵活性。为了满足对数据库操作的高性能与灵活需求,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
主要特点
- 轻量:Jdao没有任何依赖,所有特性均为Jdao本身实现,不会由于其他项目的更新或功能修改而受到影响,可以轻松融入各类项目中。
- 高效:Jdao功能均自身实现最重要目的就是实现高效性,没有多余的包袱。性能与直接JDBC调用极为接近,ORM封装的性能损耗非常小。
- 灵活:Jdao支持丰富的动态SQL构建功能,包括原生动态SQL,映射动态SQL,实体类动态SQL。
- 安全:Jdao没有SQL注入的风险。Jdao提供了Mybatis相同的映射功能,并去掉了sql注入的潜在隐患。
- 全面:Jdao结合了Hibernate的抽象性和MyBatis的灵活性,提供规范且高效的ORM运用方案。
主要功能
- 生成代码:运行jdao代码生成工具,创建数据库表的标准化实体类。类似thrift/protobuf。
- 高效序列化:表的标准化实体类实现了高效的序列化与反序列化。比标准库序列化方法高3-12倍,而序列化体积只有其20%左右。
- 支持数据读写分离:jdao支持绑定多数据源,并支持数据源绑定表,类,映射接口等属性。并支持数据读写分离
- 支持数据缓存:jdao支持数据缓存,并支持对缓存数据时效,与数据回收等特性进行细致控制
- 广泛兼容性:jdao理论上支持所有实现JDBC接口的数据库
- 高级特性:支持事务,存储过程,批处理等数据库操作
- 支持动态SQL:jdao实现了mybatis的动态sql功能,去掉了安全隐患及无实用特性。同时为原生SQL提供了编写动态SQL的特性。
- 兼容myBatis:jdao在映射模块中,采用了mybatis的标签定义,并实现了相同的功能特性。
更多Jdao的详细信息,请查阅 Jdao使用文档
更多Jdao使用示例,请参考 jdaodemo
如果github无法访问,可以访问gitee,两处均有同步。donnie4w/jdaodemo