【发布时间】:2010-09-27 02:46:20
【问题描述】:
我想构建一个 SQL 字符串来执行数据库操作(更新、删除、插入、选择等)——而不是使用数百万个“+”和引号的糟糕的字符串 concat 方法在最好的——必须有更好的方法。
我确实想过使用 MessageFormat - 但它应该用于用户消息,虽然我认为它会做一个合理的工作 - 但我想应该有一些更符合 java sql 库中的 SQL 类型操作的东西。
Groovy 有用吗?
【问题讨论】:
我想构建一个 SQL 字符串来执行数据库操作(更新、删除、插入、选择等)——而不是使用数百万个“+”和引号的糟糕的字符串 concat 方法在最好的——必须有更好的方法。
我确实想过使用 MessageFormat - 但它应该用于用户消息,虽然我认为它会做一个合理的工作 - 但我想应该有一些更符合 java sql 库中的 SQL 类型操作的东西。
Groovy 有用吗?
【问题讨论】:
首先考虑在准备好的语句中使用查询参数:
PreparedStatement stm = c.prepareStatement("UPDATE user_table SET name=? WHERE id=?");
stm.setString(1, "the name");
stm.setInt(2, 345);
stm.executeUpdate();
可以做的另一件事是将所有查询保存在属性文件中。例如 在 queries.properties 文件中可以放置上述查询:
update_query=UPDATE user_table SET name=? WHERE id=?
然后借助一个简单的实用程序类:
public class Queries {
private static final String propFileName = "queries.properties";
private static Properties props;
public static Properties getQueries() throws SQLException {
InputStream is =
Queries.class.getResourceAsStream("/" + propFileName);
if (is == null){
throw new SQLException("Unable to load property file: " + propFileName);
}
//singleton
if(props == null){
props = new Properties();
try {
props.load(is);
} catch (IOException e) {
throw new SQLException("Unable to load property file: " + propFileName + "\n" + e.getMessage());
}
}
return props;
}
public static String getQuery(String query) throws SQLException{
return getQueries().getProperty(query);
}
}
您可以按如下方式使用您的查询:
PreparedStatement stm = c.prepareStatement(Queries.getQuery("update_query"));
这是一个相当简单的解决方案,但效果很好。
【讨论】:
InputStream 放在if (props == null) 语句中,这样您就不会在不需要时实例化它。
c是什么?
对于任意 SQL,请使用 jOOQ。 jOOQ目前支持SELECT、INSERT、UPDATE、DELETE、TRUNCATE和MERGE。您可以像这样创建 SQL:
String sql1 = DSL.using(SQLDialect.MYSQL)
.select(A, B, C)
.from(MY_TABLE)
.where(A.equal(5))
.and(B.greaterThan(8))
.getSQL();
String sql2 = DSL.using(SQLDialect.MYSQL)
.insertInto(MY_TABLE)
.values(A, 1)
.values(B, 2)
.getSQL();
String sql3 = DSL.using(SQLDialect.MYSQL)
.update(MY_TABLE)
.set(A, 1)
.set(B, 2)
.where(C.greaterThan(5))
.getSQL();
您也可以使用 jOOQ 来执行它,而不是获取 SQL 字符串。见
(免责声明:我为 jOOQ 背后的公司工作)
【讨论】:
"?"还是内联绑定值。
您应该考虑的一项技术是SQLJ - 一种直接在 Java 中嵌入 SQL 语句的方法。举个简单的例子,您可能在名为 TestQueries.sqlj 的文件中有以下内容:
public class TestQueries
{
public String getUsername(int id)
{
String username;
#sql
{
select username into :username
from users
where pkey = :id
};
return username;
}
}
还有一个额外的预编译步骤,它会获取您的 .sqlj 文件并将它们转换为纯 Java - 简而言之,它会查找以
分隔的特殊块#sql
{
...
}
并将它们转换为 JDBC 调用。使用 SQLJ 有几个主要好处:
大多数主要数据库供应商都有翻译器的实现,因此您应该能够轻松找到所需的一切。
【讨论】:
我会看看Spring JDBC。每当我需要以编程方式执行 SQL 时,我都会使用它。示例:
int countOfActorsNamedJoe
= jdbcTemplate.queryForInt("select count(0) from t_actors where first_name = ?", new Object[]{"Joe"});
对于任何类型的 sql 执行都非常棒,尤其是查询;它将帮助您将结果集映射到对象,而不会增加完整 ORM 的复杂性。
【讨论】:
我倾向于使用 Spring 的命名 JDBC 参数,因此我可以编写一个标准字符串,例如 "select * from blah where colX=':someValue'";我认为这很容易阅读。
另一种方法是在单独的 .sql 文件中提供字符串,并使用实用程序方法读取内容。
哦,Squill 也值得一看:https://squill.dev.java.net/docs/tutorial.html
【讨论】:
我一直致力于开发一个 Java servlet 应用程序,该应用程序需要构建非常动态的 SQL 语句以用于临时报告目的。该应用程序的基本功能是将一堆命名的 HTTP 请求参数提供给预编码的查询,并生成格式良好的输出表。我使用 Spring MVC 和依赖注入框架将我的所有 SQL 查询存储在 XML 文件中,并将它们与表格格式信息一起加载到报告应用程序中。最终,报告需求变得比现有参数映射框架的功能更复杂,我不得不自己编写。这是一个有趣的开发练习,并为参数映射生成了一个比我能找到的任何其他东西都更强大的框架。
新的参数映射如下所示:
select app.name as "App",
${optional(" app.owner as "Owner", "):showOwner}
sv.name as "Server", sum(act.trans_ct) as "Trans"
from activity_records act, servers sv, applications app
where act.server_id = sv.id
and act.app_id = app.id
and sv.id = ${integer(0,50):serverId}
and app.id in ${integerList(50):appId}
group by app.name, ${optional(" app.owner, "):showOwner} sv.name
order by app.name, sv.name
最终框架的美妙之处在于它可以通过适当的类型检查和限制检查将 HTTP 请求参数直接处理到查询中。输入验证不需要额外的映射。在上面的示例查询中,名为 serverId 的参数 将被检查以确保它可以转换为整数并且在 0-50 的范围内。参数 appId 将被处理为整数数组,长度限制为 50。如果字段 showOwner 存在并设置为“true”,则 SQL 的位引号中的内容将添加到生成的查询中,以获取可选字段映射。字段 还有几个参数类型映射可用,包括带有更多参数映射的可选 SQL 段。它允许开发人员想出的尽可能复杂的查询映射。它甚至可以在报告配置中进行控制,以确定给定查询是通过 PreparedStatement 进行最终映射,还是简单地作为预构建查询运行。
对于示例 Http 请求值:
showOwner: true
serverId: 20
appId: 1,2,3,5,7,11,13
它将产生以下 SQL:
select app.name as "App",
app.owner as "Owner",
sv.name as "Server", sum(act.trans_ct) as "Trans"
from activity_records act, servers sv, applications app
where act.server_id = sv.id
and act.app_id = app.id
and sv.id = 20
and app.id in (1,2,3,5,7,11,13)
group by app.name, app.owner, sv.name
order by app.name, sv.name
我真的认为 Spring 或 Hibernate 或其中一个框架应该提供更强大的映射机制来验证类型,允许复杂的数据类型,如数组和其他此类功能。我写我的引擎只是为了我的目的,它不是很适合一般发布。它目前仅适用于 Oracle 查询,并且所有代码都属于一家大公司。有一天我可能会采纳我的想法并构建一个新的开源框架,但我希望现有的大玩家之一能够接受挑战。
【讨论】:
我支持使用像 Hibernate 这样的 ORM 的建议。然而,在某些情况下,这肯定是行不通的,所以我将借此机会宣传一些我帮助编写的东西:SqlBuilder 是一个使用“builder”样式动态构建 sql 语句的 java 库.它相当强大且相当灵活。
【讨论】:
为什么要手动生成所有的sql?你有没有看过像 Hibernate 这样的 ORM 根据你的项目,它可能会完成你需要的至少 95% 的工作,以比原始 SQL 更简洁的方式完成,如果你需要获得最后一点性能,你可以创建需要手动调整的 SQL 查询。
【讨论】:
你也可以看看 MyBatis (www.mybatis.org) 。它可以帮助您在 java 代码之外编写 SQL 语句,并将 sql 结果映射到您的 java 对象中。
【讨论】:
Google 提供了一个名为Room Persitence Library 的库,该库提供了一种非常简洁的Android 应用程序 编写SQL 方法,基本上是底层SQLite 数据库 之上的抽象层。 Bellow是来自官网的短代码sn-p:
@Dao
public interface UserDao {
@Query("SELECT * FROM user")
List<User> getAll();
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
List<User> loadAllByIds(int[] userIds);
@Query("SELECT * FROM user WHERE first_name LIKE :first AND "
+ "last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
@Insert
void insertAll(User... users);
@Delete
void delete(User user);
}
该库的官方文档中有更多示例和更好的文档。
还有一个叫做 MentaBean,它是 Java ORM。它具有很好的功能,并且似乎是一种非常简单的 SQL 编写方式。
【讨论】:
Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite。因此,它不是 RDBMS 的通用 ORM 库。它主要用于 Android 应用程序。
您可以从 XML 文件中读取它。它易于维护和使用。 有标准的 STaX、DOM、SAX 解析器可用,使其在 java 中只需几行代码。
您可以在标签上包含一些带有属性的语义信息,以帮助使用 SQL 做更多事情。这可以是方法名称或查询类型或任何有助于减少代码的内容。
您可以将xml放在jar之外并轻松维护它。与属性文件相同的好处。
XML 是可扩展的,并且可以轻松转换为其他格式。
【讨论】:
I don't see a reason to make use of XML. ,因为我无法编辑它。
如果将 SQL 字符串放在属性文件中,然后将其读入,则可以将 SQL 字符串保存在纯文本文件中。
这并不能解决 SQL 类型的问题,但至少它使从 TOAD 或 sqlplus 的复制和粘贴变得更加容易。
【讨论】:
您如何获得字符串连接,除了 PreparedStatements 中的长 SQL 字符串(您可以轻松地在文本文件中提供并作为资源加载)之外,您会中断多行?
您不是直接创建 SQL 字符串吗?这是编程中最大的禁忌。请使用 PreparedStatements,并将数据作为参数提供。它大大减少了 SQL 注入的机会。
【讨论】: