【问题标题】:JDBC PreparedStatement - Using the same argument, is it possible?JDBC PreparedStatement - 使用相同的参数,可能吗?
【发布时间】:2011-12-02 04:22:43
【问题描述】:

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

        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 完全相同,因为它们在上面的查询中需要相同的值。

有什么我可以改变的,无论是在查询中还是在参数填充方法中,以避免这种“丑陋”的语法?

【问题讨论】:

  • 一个可以避免这种重复的特性是命名参数。不幸的是,JDBC 不支持这些。

标签: java mysql jdbc


【解决方案1】:

使用局部变量,您可以使代码不那么难看和容易出错。但是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);

【讨论】:

  • 并非如此。他不想多次指定相同的参数。遗憾的是PreparedStatement 不支持NamedParameters
  • 但这解决了重复的主要问题——你可能会改变一个而忘记改变另一个。
  • 有点,但你仍然有多个 setParameter 行,这不是最好的代码。当你想一次性指定一些东西时,你应该把它告诉 JDBC 一次。遗憾的是现在没有这样的选择。
  • 另外他说让它看起来不那么难看,我去掉了2组括号和2个句点,并使代码(一点点)更易于维护。
  • @Octopi - 我稍微编辑了你的帖子,值得你点赞并点赞:)
【解决方案2】:

您不能更改 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)";

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      正如其他人已经报告的那样,虽然直接 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#executeByNamedParam(java.util.Map, java.util.Map)

      【讨论】:

        【解决方案5】:

        不可能。

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

        【讨论】:

        • T-SQL 上下文切换的额外性能开销如何?
        猜你喜欢
        • 1970-01-01
        • 2021-11-12
        • 2012-09-26
        • 1970-01-01
        • 2019-11-14
        • 2023-04-09
        • 1970-01-01
        • 2021-08-20
        • 1970-01-01
        相关资源
        最近更新 更多