简体   繁体   English

JDBC PreparedStatement - 使用相同的参数,是否可能?

[英]JDBC PreparedStatement - Using the same argument, is it possible?

I'm using an "insert or update" query such as the one below: 我正在使用“插入或更新”查询,如下所示:

        String sql = 
            "INSERT INTO servlets (path, applicationId, startTime, numOfRequests, totalResponseTime, totalBytes)" +
            "VALUES (?, ?, NOW(), 1, ?, ?)" +
            "ON DUPLICATE KEY UPDATE numOfRequests = numOfRequests + 1, " +
            "totalResponseTime = totalResponseTime + ?, totalBytes = totalBytes + ?";

I'm using prepared statements and fill it with the relevant arguments in the following manner: 我正在使用预准备语句,并以下列方式填充相关参数:

        statement = connection.prepareStatement(sql);
        statement.setString(1, i_ServletModel.GetPath());
        statement.setInt(2, i_ServletModel.GetApplicationId());
        statement.setLong(3, i_RequestStats.GetResponseTime());
        statement.setLong(4, i_RequestStats.GetBytes());
        statement.setLong(5, i_RequestStats.GetResponseTime());
        statement.setLong(6, i_RequestStats.GetBytes());

Notice that argument 3 is exactly the same as argument 5 and argument 4 is exactly the same as argument 6 since they require the same value in the query above. 请注意,参数3与参数5完全相同,参数4与参数6完全相同,因为它们在上面的查询中需要相同的值。

Is there anything I can change, either in the query or in the arguments filling methods to avoid such an "ugly" syntax? 有什么我可以改变,无论是在查询中还是在参数填充方法中,以避免这种“丑陋”的语法?

Using a local variable, you can make the code less ugly and error-prone. 使用局部变量,可以使代码不那么难看且容易出错。 But the shortcoming of JDBC that it does not support named parameters still holds. 但是JDBC不支持命名参数的缺点仍然存在。 There will be again multiple lines for the same parameter. 同一参数将再次出现多行。

    statement = connection.prepareStatement(sql);

    long time = i_RequestStats.GetResponseTime();
    long bytes = i_RequestStats.GetBytes();

    statement.setString(1, i_ServletModel.GetPath());
    statement.setInt(2, i_ServletModel.GetApplicationId());
    statement.setLong(3,time);
    statement.setLong(4, bytes);
    statement.setLong(5, time);
    statement.setLong(6, bytes);

For developers facing same issue I recommend you to create a stored procedure or function, it is a very good practice since you will return a scalar value or list, but avoid to put a lot of logical stuff there. 对于面临同样问题的开发人员,我建议你创建一个存储过程或函数,这是一个非常好的做法,因为你将返回一个标量值或列表,但避免在那里放置很多逻辑的东西。 if you adopt this solution you should use "Callable Statement" Hope this will help . 如果您采用此解决方案,您应该使用“可调用语句”希望这会有所帮助。

As others have already reported while it is not technically possible with straight JDBC to use named parameters with prepared statements Spring does have some sugar to make it more palatable if that's in your stack already. 正如其他人已经报道的那样,直接JDBC在技术上不可能使用带有预处理语句的命名参数Spring确实有一些糖,如果它已经在你的堆栈中,它会更加可口。

Here's an example using the same "named parameter" :personId twice in the query. 下面是使用相同“命名参数”的示例查询中的personId两次。 Under the covers Spring will convert the SQL :personId to ?'s but it will apply the the same values multiple times as desired. 在封面下,Spring会将SQL :personId转换为?,但它会根据需要多次应用相同的值。

@Repository
public class RoleRepositoryImpl implements RoleRepository {

    private static final String ROLE_ASSIGNMENT_QUERY = "select T.RoleId from \n" +
            "(select RoleId from RoleAssignment where OrganizationId=:orgId and PersonId=:personId\n" +
            "union\n" +
            "select 'TEXTURA_ADMIN' from Administrator where PersonId=:personId) as T\n" +
            "inner join Role R on R.RoleId=T.RoleId\n";


    public static class RoleQuery extends MappingSqlQuery<Role> {

        public RoleQuery(final DataSource ds) {
            super(ds, ROLE_ASSIGNMENT_QUERY);
            declareParameter(new SqlParameter("orgId", Types.INTEGER));
            declareParameter(new SqlParameter("personId", Types.INTEGER));
            compile();
        }

        @Override
        protected Role mapRow(final ResultSet rs, final int rowNum) throws SQLException {
            try {
                return Role.valueOf(rs.getString(1));
            } catch (final IllegalArgumentException ex) {
                return null;
            }
        }
    }
}

And then example usage 然后是示例用法

    private final RoleQuery roleQuery;

    public RoleRepositoryImpl(final DataSource dataSource) {
        this.roleQuery = new RoleQuery(dataSource);
    }

    @Override
    public List<Role> findRoles(final Long orgId,
                                final Long userId) {

final List<Role> roles = roleQuery.executeByNamedParam(ImmutableMap.of(
                "orgId", orgId, 
                "personId", userId));

You can see the Spring magic happen in this method if you are curious what it does under the covers: 如果你好奇它在幕后做了什么,你可以看到Spring魔法在这种方法中发生:

org.springframework.jdbc.object.SqlQuery#executeByNamedParam(java.util.Map, java.util.Map) org.springframework.jdbc.object.SqlQuery #cuteByNamedParam(java.util.Map,java.util.Map)

Can't you change the SQL syntax to refer back to the 'VALUES' you've already declared? 你不能改变SQL语法来引用你已经声明的'VALUES'吗? Something like the following: 类似于以下内容:

String sql = 
        "INSERT INTO servlets (path, applicationId, startTime, numOfRequests, totalResponseTime, totalBytes)" +
        "VALUES (?, ?, NOW(), 1, ?, ?)" +
        "ON DUPLICATE KEY UPDATE numOfRequests = numOfRequests + 1, " +
        "totalResponseTime = totalResponseTime + VALUES(5), totalBytes = totalBytes + VALUES(6)";

...that way you need only add each parameter once. ...这样你只需要添加一次参数。

Not possible. 不可能。

But you could instead, in your query, declare a variable and use the same multiple times. 但是,您可以在查询中声明一个变量并多次使用相同的变量。 See: http://msdn.microsoft.com/en-IN/library/ms188927.aspx for MSSQL 请参阅: http//msdn.microsoft.com/en-IN/library/ms188927.aspx for MSSQL

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM