Answers:
首先考虑在准备好的语句中使用查询参数:
PreparedStatement stm = c.prepareStatement("UPDATE user_table SET name=? WHERE id=?");
stm.setString(1, "the name");
stm.setInt(2, 345);
stm.executeUpdate();
可以做的另一件事是将所有查询保留在属性文件中。例如,在querys.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)
以便在不需要该语句时不要实例化它。
对于任意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();
除了获取SQL字符串,您还可以使用jOOQ执行它。看到
(免责声明:我为jOOQ背后的公司工作)
"?"
还是内联绑定值。
您应该考虑的一种技术是SQLJ-一种将SQL语句直接嵌入Java中的方法。作为一个简单的示例,您可能在名为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有几个主要优点:
大多数主要数据库供应商都提供了转换器的实现,因此您应该能够轻松找到所需的一切。
我想知道您是否正在追寻诸如Squiggle之类的东西。jDBI也非常有用。它不会帮助您查询。
我将看一下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
我对使用像Hibernate这样的ORM的建议表示赞同。但是,在某些情况下肯定无法解决问题,因此,我将借此机会介绍一些我曾经写过的东西:SqlBuilder是一个Java库,用于使用“构建器”样式动态构建sql语句。它既强大又灵活。
我一直在研究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查询,并且所有代码都属于一家大公司。总有一天,我可能会采纳我的想法并建立一个新的开源框架,但是我希望现有的大公司中的一位能够接受挑战。
您也可以查看MyBatis(www.mybatis.org)。它可以帮助您在Java代码之外编写SQL语句,并将SQL结果映射到Java对象中。
Google提供了一个名为Room Persitence Library的库,它提供了一种非常干净的方式为Android Apps编写SQL ,基本上是底层SQLite数据库的抽象层。贝娄是官方网站上的简短代码段:
@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 Java ORM。它具有不错的功能,并且似乎是编写SQL的非常简单的方法。
您可以从XML文件中读取它。它易于维护和使用。有标准的STaX,DOM,SAX解析器可供使用,以使其在Java中仅几行代码。
您可以在标记上具有一些带有属性的语义信息,以帮助使用SQL进行更多操作。这可以是方法名称或查询类型,也可以是可以减少编码的任何内容。
您可以将xml放在jar之外并轻松维护它。与属性文件具有相同的好处。
XML是可扩展的,可以轻松转换为其他格式。
I don't see a reason to make use of XML.
,因为无法编辑。
除了PreparedStatements中的长SQL字符串(您可以很容易地在文本文件中提供并无论如何将其加载为资源)之外,如何拆分多行代码,还如何获得字符串连接?
您不是直接创建SQL字符串吗?那是编程中最大的禁忌。请使用PreparedStatements,并提供数据作为参数。它极大地减少了SQL注入的机会。