JDBC中的命名参数


75

是否有一个名为JDBC中,而不是那些位置参数,比如@name@city在下面的ADO.NET查询?

select * from customers where name=@name and city = @city

:本文在这里提供了一个快速的实现这种类的javaworld.com/article/2077706/core-java/...
索林·波斯特尔尼卡

I think Oracle JDBC driver supports calling regular SQL statements with named parameters when using CallableStatement
Alex78191 '19

Answers:


75

JDBC不支持命名参数。除非您必须使用普通的JDBC(这会造成麻烦,让我告诉你),否则我建议使用Springs Excellent JDBCTemplate,该模板无需整个IoC容器即可使用。

NamedParameterJDBCTemplate支持命名参数,您可以像这样使用它们:

 NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);

 MapSqlParameterSource paramSource = new MapSqlParameterSource();
 paramSource.addValue("name", name);
 paramSource.addValue("city", city);
 jdbcTemplate.queryForRowSet("SELECT * FROM customers WHERE name = :name AND city = :city", paramSource);

谢谢-但是我不能使用springs,因为我不能对现有代码库进行太多更改:(
Fakrudeen 2010年

2
@Malax提出的要点是,您可以从Spring独立版本使用NamedParameterJdbcTemplate。您无需更改代码库的任何其他部分。
加雷斯·戴维斯

11
但是您必须在项目中包括许多Spring JAR,仅使用一些与NamedParameterJdbcTemplate相关的类。org.springframework.jdbc.jar不能单独使用很可惜。
xmedeko 2011年

3
解压缩jar文件-将所需的类文件复制到项目中。Voila ..
Lars Juel Jensen 2015年

6
spring-jdbc模块依赖于spring-corespring-frameworkspring-txNamedParameterJdbcTemplate需要进行一些重写才能使用独立版本。 repo1.maven.org/maven2/org/springframework/spring-jdbc/…–
jordanpg

34

为了避免包含大型框架,我认为一个简单的自制类可以解决问题。

处理命名参数的类的示例:

public class NamedParamStatement {
    public NamedParamStatement(Connection conn, String sql) throws SQLException {
        int pos;
        while((pos = sql.indexOf(":")) != -1) {
            int end = sql.substring(pos).indexOf(" ");
            if (end == -1)
                end = sql.length();
            else
                end += pos;
            fields.add(sql.substring(pos+1,end));
            sql = sql.substring(0, pos) + "?" + sql.substring(end);
        }       
        prepStmt = conn.prepareStatement(sql);
    }

    public PreparedStatement getPreparedStatement() {
        return prepStmt;
    }
    public ResultSet executeQuery() throws SQLException {
        return prepStmt.executeQuery();
    }
    public void close() throws SQLException {
        prepStmt.close();
    }

    public void setInt(String name, int value) throws SQLException {        
        prepStmt.setInt(getIndex(name), value);
    }

    private int getIndex(String name) {
        return fields.indexOf(name)+1;
    }
    private PreparedStatement prepStmt;
    private List<String> fields = new ArrayList<String>();
}

调用类的示例:

String sql;
sql = "SELECT id, Name, Age, TS FROM TestTable WHERE Age < :age OR id = :id";
NamedParamStatement stmt = new NamedParamStatement(conn, sql);
stmt.setInt("age", 35);
stmt.setInt("id", 2);
ResultSet rs = stmt.executeQuery();

请注意,上面的简单示例不会两次使用命名参数。它也不使用引号内的:符号处理。


1
我使用了您的一些小修改。`Pattern findParametersPattern = Pattern.compile(“(?<!')(:[\\ w] *)(?!')”); Matcher matcher = findParametersPattern.matcher(statementWithNames); while(matcher.find()){fields.add(matcher.group()。substring(1)); } prepStmt = conn.prepareStatement(statementWithNames.replaceAll(findParametersPattern.pattern(),“?”));
WillieT 2014年

以为我会做类似的事情。我认为最好为此覆盖索引器
JonnyRaa 2014年

@WillieT模式(?!\\B'[^']*)(:\\w+)(?![^']*'\\B)对我来说更好。
Furgas


1
要点可以很好地改善。indexof获取索引?
毫米

23

Vanilla JDBC仅支持CallableStatement(例如setString("name", name))中的命名参数,即使如此,我怀疑底层存储过程实现也必须支持它。

有关如何使用命名参数的示例:

//uss Sybase ASE sysobjects table...adjust for your RDBMS
stmt = conn.prepareCall("create procedure p1 (@id int = null, @name varchar(255) = null) as begin "
        + "if @id is not null "
        + "select * from sysobjects where id = @id "
        + "else if @name is not null "
        + "select * from sysobjects where name = @name "
        + " end");
stmt.execute();

//call the proc using one of the 2 optional params
stmt = conn.prepareCall("{call p1 ?}");
stmt.setInt("@id", 10);
ResultSet rs = stmt.executeQuery();
while (rs.next())
{
    System.out.println(rs.getString(1));
}


//use the other optional param
stmt = conn.prepareCall("{call p1 ?}");
stmt.setString("@name", "sysprocedures");
rs = stmt.executeQuery();
while (rs.next())
{
    System.out.println(rs.getString(1));
}

5
是的,这是正确的,但并非所有数据库都支持此功能。我在postgresql上测试了它不起作用。
den bardadym 2013年

1
是的,显然数据库必须首先支持命名参数...而似乎Postgres不支持。问题是他们的数据库确实支持此功能,并想了解如何在JDBC中使用该功能。
酒店

1

您不能在JDBC本身中使用命名参数。您可以尝试使用Spring框架,因为它具有一些扩展名,这些扩展名允许在查询中使用命名参数。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.