LINUX.ORG.RU

Вставка порции данных в таблицу

 , ,


0

3

Есть, значит, база данных. PostgreSQL (если имеет значение).

Обращаюсь туда посредством JDBC, для удобства использую commons-dbutils (никаких ORM, для меня в данной задаче просто ни к чему).

Есть задача вставлять данные порциями в таблицу. Для определённости предположим, что таблица users с полями name и password. Есть массив или список users длины пусть 100. Самый банальный способ вставки:

QueryRunner run = new QueryRunner(dataSource);
for (User user: users) {
    run.update("INSERT INTO users (name,password) VALUES (?,?)",user.getName(), user.getPassword())
}

Но ведь этот вариант неэффективен?? выполняется 100 запросов. А ведь можно в один:

INSERT INTO users (name,password) VALUES ('name1',pass1'), ('name2','pass2'), ....

Как реализовать такой запрос средствами JDBC и java?

Ответ на: комментарий от anonymous

была такая мысль, но это же неправильно... опасность sql инъекций. либо самому все символы экранировать вручную

BattleCoder ★★★★★
() автор топика
Ответ на: комментарий от BattleCoder

ты можешь 200 вопросиков впилить, а потом заполнить их

maxcom ★★★★★
()
Ответ на: комментарий от maxcom

1) просто склеить я пробовал :) склеить строку вроде

INSERT INTO table (field1, field2) VALUES (?,?), (?,?),(?,?),(?,?)

через StringBuilder в общем-то не составляет никакого труда. Но проблема в том, чтобы потом передать заранее неизвестное число аргументов в метод QueryRunner.update(sqlstring, param1, param2, ..., paramN), средства java это вообще позволяют? :) например, массив передать. просто раньше я так не делал (в python такое довольно легко делается.

2) batch api тоже штука интересная, наверное хороший вариант, я так понял, вместо метода update вызвать метод batch, а ему передать массив Object[][] параметров вместо списка через запятую. честно говоря, с этим способом немного запутался сначала,но попробую ещё раз. :)

3) наверное завязываться на postgreSQL сейчас для меня не вариант. Хотя в будущем такую фичу обязательно учту. =) когда что-то серьёзное соберусь разрабатывать. спасибо.

BattleCoder ★★★★★
() автор топика
Ответ на: комментарий от BattleCoder

Вопросики и хинт ниже:

PreparedStatement Connection.prepareStatement(String query)
PreparedStatement.setX(int paramNumber, X paramValue)
PreparedStatement.executeUpdate()

Это если хочешь сразу values (a, b), (c, d), ...

Или можно препаред с одним values, а в цикле уже расставлять только от конкретного инстанса значения, сэкономишь на спичкахразборе запроса, но много инсертов /thread

GateKeeper ★★
()
Ответ на: комментарий от BattleCoder

Может как-то так (могут быть ошибки, писал на коленке, главное, суть понять)?

DBOperation dbOperation = new DBOperation("INSERT INTO users (`name`, `password`) VALUES (?, ?);");
try {
    dbOperation.getConnection().setAutoCommit(false);
    for (int i = 0; i < usersList.size(); i++) {
        User u = usersList.get(i);
        dbOperation.getPst().setString(1, u.getName());
        dbOperation.getPst().setString(2, u.getPassword());

        if (((i+1) % 100 == 0) || (i == usersList.size() - 1)) { // на каждые 100 юзеров делаем коммит
            dbOperation.getPst().executeBatch();
            dbOperation.getConnection().commit();
            dbOperation.getPst().clearBatch();
        }
    }
} catch (SQLException ex) {
    TRACER.error("Failed adding users (batch) to the database !", ex);
} finally {
    dbOperation.close();
}

kovrik ★★★★★
()
Последнее исправление: kovrik (всего исправлений: 1)
Ответ на: комментарий от kovrik

Это не тоже самое, что я хотел, но суть понял =) думаю, подойдёт вполне. спс.

BattleCoder ★★★★★
() автор топика
Ответ на: комментарий от BattleCoder

Но проблема в том, чтобы потом передать заранее неизвестное число аргументов в метод QueryRunner.update(sqlstring, param1, param2, ..., paramN), средства java это вообще позволяют? :)

Запросто. Java's varargs просто синтаксический сахар вокруг arrays со всеми вытекающими:


public int fakeUpdate(String sql, Object ... parameters) {
    return parameters.length;
}

@Test
public void fewParametersTest() {
    // given

    // when
    int actualValue = fakeUpdate("", 1, "24234", 1.0F);

    // then
    Assert.assertEquals(actualValue, 3);
}

@Test
public void parameterAsArrayTest() {
    // given
    Object parameters[] = new Object[] {1 , "24234", 1.0F};

    // when
    int actualValue = fakeUpdate("", parameters);

    // then
    Assert.assertEquals(actualValue, 3);
}
Slavaz ★★★★★
()
Ответ на: комментарий от Slavaz

О. Спасибо. На будущее будет, и для общего развития. :)

Над заданием свои сейчас получше подумал, и пришёл к выводу, что мне вставлять 100 строк одним запросом всё равно не вариант. И дело не в оптимизации. Почему?

А причина в том, что данные могут повторяться... например в таблице уже храниться данные с тем же первичным ключом, что уже были... и insert вылетит с ошибкой. в таком случае новые данные надо или игнорировать, или делать update (пока не решил, что лучше - наверное update).

BattleCoder ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.