83

Are there named parameters in JDBC instead of positional ones, like the @name, @city in the ADO.NET query below?

select * from customers where name=@name and city = @city
Jonas
  • 121,568
  • 97
  • 310
  • 388
Fakrudeen
  • 5,778
  • 7
  • 44
  • 70
  • This article here offers a quick implementation of such a class: http://www.javaworld.com/article/2077706/core-java/named-parameters-for-preparedstatement.html – Sorin Postelnicu Feb 03 '17 at 18:08
  • `I think Oracle JDBC driver supports calling regular SQL statements with named parameters when using CallableStatement`. – Alex78191 Jul 15 '19 at 16:07

5 Answers5

86

JDBC does not support named parameters. Unless you are bound to using plain JDBC (which causes pain, let me tell you that) I would suggest to use Springs Excellent JDBCTemplate which can be used without the whole IoC Container.

NamedParameterJDBCTemplate supports named parameters, you can use them like that:

 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);
Roland
  • 7,525
  • 13
  • 61
  • 124
Malax
  • 9,436
  • 9
  • 48
  • 64
  • Thanks - but I can't use springs, because I can't make that much change to existing codebase :( – Fakrudeen Feb 22 '10 at 10:19
  • 2
    the point that @Malax is making is that you can use the NamedParameterJdbcTemplate from spring standalone. You wouldn't have to change any other parts of the code base. – Gareth Davis Feb 22 '10 at 10:24
  • 12
    But you have to include many Spring JARs into your project, just to use a few classes related to the NamedParameterJdbcTemplate. It's pitty the org.springframework.jdbc.jar cannot be used standalone. – xmedeko Mar 24 '11 at 13:20
  • 5
    UnZip the jar file - and copy the class files that you want into your project. Voila.. – Lars Juel Jensen Aug 20 '15 at 12:35
  • 6
    the `spring-jdbc` module depends on `spring-core`, `spring-framework`, and `spring-tx`. `NamedParameterJdbcTemplate` would require some rewriting to use standalone. https://repo1.maven.org/maven2/org/springframework/spring-jdbc/4.3.8.RELEASE/spring-jdbc-4.3.8.RELEASE.pom – jordanpg May 09 '17 at 15:44
  • Apparently it "does" support them? https://stackoverflow.com/a/2310045/32453 – rogerdpack Apr 07 '22 at 20:21
37

To avoid including a large framework, I think a simple homemade class can do the trick.

Example of class to handle named parameters:

public class NamedParamStatement implements AutoClosable {

    private final PreparedStatement prepStmt;
    private final List<String> fields = new ArrayList<>();

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

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

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

    private int getIndex(final String name) {
        return fields.indexOf(name) + 1;
    }
}

Example of calling the class:

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

Please note that the above simple example does not handle using named parameter twice. Nor does it handle using the : sign inside quotes.

XelaNimed
  • 301
  • 5
  • 18
InvulgoSoft
  • 3,231
  • 1
  • 16
  • 6
  • 3
    I used yours with a few small modifications. ` 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(), "?")); – William Feb 24 '14 at 20:45
  • think I'm going to do something similar. I reckon it'd be nicer to override the indexer for this though – JonnyRaa May 12 '14 at 12:52
  • @WillieT Pattern `(?!\\B'[^']*)(:\\w+)(?![^']*'\\B)` works better for me. – Furgas Aug 17 '16 at 11:49
  • 3
    gist for code of @WillieT https://gist.github.com/ruseel/e10bd3fee3c2b165044317f5378c7446 – ruseel Dec 16 '16 at 06:37
  • 1
    that gist could well improve. indexof for getting the index ? – mjs Oct 01 '20 at 18:38
  • 2
    I would also recommend adding `implements AutoClosable` (req java7+) – Gary Oct 21 '20 at 14:47
  • Using `(?<!')(:[\w]+)(?!')` makes you able to use format functions like `%H:%i:%s`. Using zero or many can generate empty fields. – Jonas Pedersen Feb 15 '21 at 15:21
26

Vanilla JDBC only supports named parameters in a CallableStatement (e.g. setString("name", name)), and even then, I suspect the underlying stored procedure implementation has to support it.

An example of how to use named parameters:

//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));
}
Hotel
  • 1,361
  • 11
  • 13
skaffman
  • 398,947
  • 96
  • 818
  • 769
  • 6
    Yes it is right, but not all db's support this feature. I tested on postgresql it does not work. – den bardadym Sep 30 '13 at 14:23
  • 2
    Yes, obviously the database has to support named parameters first...and it appears postgres does not. The question implies their DB does support this, and wants to understand how to use the feature in JDBC. – Hotel Jan 13 '14 at 18:33
1

You can't use named parameters in JDBC itself. You could try using Spring framework, as it has some extensions that allow the use of named parameters in queries.

Ivan Vrtarić
  • 385
  • 3
  • 11
1

Plain vanilla JDBC does not support named parameters.

If you are using DB2 then using DB2 classes directly:

  1. Using named parameter markers with PreparedStatement objects
  2. Using named parameter markers with CallableStatement objects

EDIT: New links for Db2 v11.5:

  1. Using named parameter markers with PreparedStatement objects
  2. Using named parameter markers with CallableStatement objects

EDIT 2: New links for Db2 on z/OS (v13)

  1. Using named parameter markers with PreparedStatement objects
  2. Using named parameter markers with Callable Statement objects
rpenny125
  • 3
  • 1
kovica
  • 2,443
  • 3
  • 21
  • 25
  • 3
    `NamedParameterStatement` is not a class in the Java API. – Jonas Nov 09 '11 at 21:00
  • 2
    True. Here is link to it: http://www.javaworld.com/javaworld/jw-04-2007/jw-04-jdbc.html – kovica Nov 21 '11 at 14:12
  • Link provided by @kovica is now dead. You can find its content there: https://www.infoworld.com/article/2077706/named-parameters-for-preparedstatement.html – air-dex Nov 10 '22 at 09:26