JDBC PreparedStatement — возможно ли это с использованием того же аргумента?

#java #mysql #jdbc

#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, поскольку они требуют одного и того же значения в приведенном выше запросе.

Могу ли я что-нибудь изменить либо в запросе, либо в методах заполнения аргументов, чтобы избежать такого «уродливого» синтаксиса?

Комментарии:

1. Одной из функций, которая позволяет избежать такого дублирования, будут именованные параметры. К сожалению, они не поддерживаются 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);
  

Комментарии:

1. На самом деле нет. Он хотел не указывать один и тот же параметр более одного раза. К сожалению PreparedStatement , не поддерживается NamedParameters .

2. Но это решает основную проблему с повторением — вы можете изменить одно, забыв изменить другое.

3. Несколько, но у вас все еще есть несколько setParameter строк, что не самый лучший код. Когда вы хотите указать что-то один раз, вы должны сообщить это JDBC один раз. К сожалению, сейчас такой опции нет.

4. Кроме того, он сказал, чтобы это выглядело менее уродливо, я устранил 2 набора круглых скобок и 2 точки и сделал код (немного) более удобным для обслуживания.

5. @Octopi — Я немного отредактировал ваш пост, чтобы заслужить одобрение, и поддержал вас 🙂

Ответ №2:

Разве вы не можете изменить синтаксис SQL, чтобы вернуться к «ЗНАЧЕНИЯМ», которые вы уже объявили? Что-то вроде следующего:

 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=:personIdn"  
            "unionn"  
            "select 'TEXTURA_ADMIN' from Administrator where PersonId=:personId) as Tn"  
            "inner join Role R on R.RoleId=T.RoleIdn";


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

Вы можете увидеть, как в этом методе происходит волшебство весны, если вам интересно, что он делает под прикрытием:

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

Ответ №5:

Невозможно.

Но вместо этого вы могли бы в своем запросе объявить переменную и использовать ее несколько раз. Смотрите: http://msdn.microsoft.com/en-IN/library/ms188927.aspx для MSSQL

Комментарии:

1. Как насчет дополнительных затрат на производительность при переключении контекста T-SQL?