问题:
我有一个api端点,可以处理多个查询参数。它是使用Spring实现的,查询参数用于从postgres数据库查询数据,我使用JDBC模板进行查询。
我正在寻找一种成熟的查询生成器技术来解决我的问题。
示例:
一个简单的查询可能看起来像这样:
api/book?name=LOTR&cover=hardback
将查询参数添加到地图,并根据地图数据构建查询字符串:
String sqlQuery += (String) map.entrySet().stream()
.map(entry -> entry.getKey() + "='" + entry.getValue() + "' AND ")
.collect(Collectors.joining());
它不是最有效的,因为我必须始终从字符串中删除尾随的"AND"
子句,但它可以工作。
但是,如果查询在哪里看起来像
api/book?name=LOTR&name=Ulysses&cover=hardback
现在添加了"OR"
子句,上述代码无法处理。我可以看到自己很快进入了繁琐的字符串解析来创建SQL语句的领域。
因此,既然我已经提出了我的问题,我想知道是否有一种我可以使用的技术可以很好地处理此类问题?
我想避免在该项目中使用任何ORM,因此Hibernate和MyBatis完全没有问题。我看了一些JOOQ示例,但它们看起来与JDBC模板不兼容。
对于像第一种情况这样的琐碎实现,您必须删除最后一个AND并在构建查询之后就有一个简单的技巧-在WHERE之后立即添加1 = 1,然后为每个WHERE谓词添加AND [COLUMN] = [VALUE ]。
注意:大多数数据库在执行之前都会优化WHERE子句中常量的使用,因此性能不会成为问题
/*
select <columns> from <tables> where 1 = 1
[dynamically built Where predicates will come here from following code]
*/
String sqlQuery += (String) map.entrySet().stream()
.map(entry -> "AND " + entry.getKey() + "='" + entry.getValue() + "'")
.collect(Collectors.joining());
但是对于严肃的生产实现,您可能希望使用myBatis之类的框架,该框架使您可以对查询进行模板化,然后在运行时传递参数以构建最终查询。
您可以找到一个很好的教程here。
/* An example */
<select id = "getName_Id_phone" parameterType = "Student" resultType = "Student">
SELECT * FROM STUDENT
<where>
<if test = "id != null">
id = #{id}
</if>
<if test = "name != null">
AND name LIKE #{name}
</if>
</where>
</select>