在Java中构建SQL字符串的最简洁方法

时间:2008-12-16 09:44:38

标签: java sql oracle string

我想构建一个SQL字符串来进行数据库操作(更新,删除,插入,选择,那种事情) - 而不是使用数以百万计的“+”和引号的糟糕的字符串连接方法,这是不可读的最好的 - 必须有更好的方法。

我确实想过使用MessageFormat - 但它应该用于用户消息,虽然我认为它会做一个合理的工作 - 但我想在java sql库中应该有一些更符合SQL类型操作的东西。 / p>

Groovy会不会有任何好处?

14 个答案:

答案 0 :(得分:67)

首先考虑在预准备语句中使用查询参数:

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"));

这是一个相当简单的解决方案,但效果很好。

答案 1 :(得分:58)

对于任意SQL,请使用jOOQ。 jOOQ目前支持SELECTINSERTUPDATEDELETETRUNCATEMERGE。您可以像这样创建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字符串。参见

http://www.jooq.org

(免责声明:我为jOOQ背后的公司工作)

答案 2 :(得分:14)

您应该考虑的一种技术是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有几个主要好处:

  • 完全抽象出JDBC层 - 程序员只需要考虑Java和SQL
  • 可以使编译器在编译时针对数据库检查查询语法等
  • 使用“:”前缀
  • 直接绑定查询中的Java变量的能力

大多数主要数据库供应商都有翻译器的实现,因此您应该能够轻松找到所需的一切。

答案 3 :(得分:12)

我想知道你是不是喜欢Squiggle之类的东西。另外一些非常有用的东西是jDBI。但它对查询没有帮助。

答案 4 :(得分:9)

我会看看Spring JDBC。每当我需要以编程方式执行SQL时,我就会使用它。例如:

int countOfActorsNamedJoe
    = jdbcTemplate.queryForInt("select count(0) from t_actors where first_name = ?", new Object[]{"Joe"});

对于任何类型的sql执行来说都非常棒,特别是查询;它可以帮助您将结果集映射到对象,而不会增加完整ORM的复杂性。

答案 5 :(得分:5)

我倾向于使用Spring的命名JDBC参数,因此我可以编写一个标准字符串,例如“select * from blah where colX =':someValue'”;我认为这很可读。

另一种方法是在单独的.sql文件中提供字符串,并使用实用程序方法读取内容。

哦,也值得一看Squill:https://squill.dev.java.net/docs/tutorial.html

答案 6 :(得分:4)

为什么要手动生成所有sql?您是否看过像Hibernate这样的ORM根据您的项目,它可能至少可以完成您需要的95%,以更简洁的方式执行原始SQL,如果您需要获得最后一点性能,您可以创建需要手动调整的SQL查询。

答案 7 :(得分:4)

我推荐使用像Hibernate这样的ORM的建议。但是,当然有些情况不起作用,所以我将借此机会宣传一些我帮忙写的东西:SqlBuilder是一个用于使用“构建器”动态构建sql语句的java库“风格。它相当强大且相当灵活。

答案 8 :(得分:3)

您还可以查看MyBatis(www.mybatis.org)。它可以帮助您在Java代码之外编写SQL语句,并将sql结果映射到您的java对象中。

答案 9 :(得分:3)

Google提供了一个名为Room Persitence Library的库,它提供了一种非常简洁的sql编写方式。 Bellow是官方网站的简短代码片段:

@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的非常简单的方法。

答案 10 :(得分:2)

我一直在研究一个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查询,并且所有代码都属于大公司。有一天,我可能会采纳我的想法并建立一个新的开源框架,但我希望现有的大型企业之一能够接受挑战。

答案 11 :(得分:2)

读取XML文件。

您可以从XML文件中读取它。它易于维护和使用。 有标准的STaX,DOM,SAX解析器可以在java中使用几行代码。

使用属性

做更多事情

您可以在标记上包含一些带有属性的语义信息,以帮助您更好地使用SQL。这可以是方法名称或查询类型,也可以是帮助您减少编码的任何内容。

Maintaince

您可以将xml放在jar外面并轻松维护它。与属性文件相同的好处。

转换

XML是可扩展的,可以轻松转换为其他格式。

用例

Metamug uses xml to configure REST resource files with sql.

答案 12 :(得分:1)

如果将SQL字符串放在属性文件中然后读取,则可以将SQL字符串保存在纯文本文件中。

这并不能解决SQL类型问题,但至少它会使从TOAD或sqlplus复制和粘贴变得更加容易。

答案 13 :(得分:0)

除了PreparedStatements中的长SQL字符串(您可以轻松地在文本文件中提供并作为资源加载)之外,您是如何获得字符串连接的?你打破了几行?

你不是直接创建SQL字符串吗?这是编程中最大的禁忌。请使用PreparedStatements,并提供数据作为参数。它大大降低了SQL注入的几率。