JDBC PreparedStatement - возможно ли использовать тот же аргумент? - PullRequest
8 голосов
/ 06 октября 2011

Я использую запрос «вставить или обновить», такой как приведенный ниже:

        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 ]

6 голосов
/ 06 октября 2011

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

Как уже сообщали другие, в то время как технически невозможно с прямой 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)

1 голос
/ 18 апреля 2015

Для разработчиков, сталкивающихся с той же проблемой, я рекомендую вам создать хранимую процедуру или функцию, это очень хорошая практика, так как вы будете возвращать скалярное значение или список, но избегайте размещения там множества логических вещей. если вы принимаете это решение, вы должны использовать «Callable Statement» Надеюсь, это поможет.

0 голосов
/ 30 мая 2014

Не возможно.

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

0 голосов
/ 18 марта 2013

Не можете ли вы изменить синтаксис 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)";

... таким образом, вам нужно добавить каждый параметр только один раз.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...