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

时间:2011-10-06 11:28:25

标签: java mysql jdbc

我正在使用“插入或更新”查询,如下所示:

        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 + ?";

我正在使用预备语句,并以下列方式填写相关参数:

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

请注意,参数3与参数5完全相同,参数4与参数6完全相同,因为它们在上面的查询中需要相同的值。

我是否可以在查询或参数填充方法中更改任何内容以避免这种“丑陋”的语法?

5 个答案:

答案 0 :(得分:6)

使用局部变量,可以使代码不那么难看且容易出错。但JDBC它不支持命名参数的缺点仍然存在。同一参数将再有多行。

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

答案 1 :(得分:1)

对于面临同样问题的开发人员,我建议你创建一个存储过程或函数,这是一个非常好的做法,因为你将返回一个标量值或列表,但避免在那里放入很多逻辑内容。 如果采用此解决方案,则应使用“可调用语句” 希望这会有所帮助。

答案 2 :(得分:1)

正如其他人已经报道的那样,虽然在技术上不可能使用直接的JDBC来使用带有预处理语句的命名参数Spring确实有一些糖,如果它已经在你的堆栈中,它会更加可口。

以下是在查询中使用相同“命名参数”:personId 两次的示例。在封面下,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;
            }
        }
    }
}

然后使用示例

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

如果你好奇它在幕后做了什么,你可以看到Spring魔法在这种方法中发生:

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

答案 3 :(得分:0)

你不能改变SQL语法来引用你已经声明的'VALUES'吗?如下所示:

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

......这样你只需要添加一次参数。

答案 4 :(得分:0)

不可能。

但您可以在查询中声明一个变量并多次使用相同的变量。请参阅:http://msdn.microsoft.com/en-IN/library/ms188927.aspx for MSSQL