Я использую Java 8, JDB C и MySql. Я хочу вставить большой объем данных (2000 строк) в 2 таблицы. Таблицы имеют отношение 1 к 1. Первая таблица: order_items
:
| id | amount |
|:--------|----------------:|
| 1 | 20 |
| 2 | 25 |
| 3 | 30 |
Вторая таблица: delivery_details
:
| orderItemId | message |
|----------------:|:-----------|
| 1 | hello. |
| 2 | salut. |
| 3 | ciao. |
orderItemId
- это внешний ключ для order_items
.
Данные представлены в этом классе:
public class OrderItemDelivery {
@SerializedName("amount")
private BigDecimal amount = null;
@SerializedName("message")
private String message = null;
// getters and setters below
...
...
}
Мне нужно выполнить вставки как пакет, чтобы сократить время выполнения. List<OrderItemDelivery> orderItemDeliveries
содержит 2000 предметов. Мой текущий код:
Connection connection = this.hikariDataSource.getConnection();
connection.setAutoCommit(false);
Statement statement = connection.createStatement();
for (int x = 0; x < orderItemDeliveries.size(); x++) {
sql = String.format("INSERT INTO order_items (amount) VALUES ('%s')", orderItemDelivery.getAmount());
statement.addBatch(sql);
sql = String.format("INSERT INTO `delivery_details` (`orderItemId`, `message`) VALUES (LAST_INSERT_ID(), '%s')", orderItemDelivery.getMessage());
statement.addBatch(sql);
}
statement.executeBatch();
statement.close();
connection.setAutoCommit(true);
connection.close();
Это действительно эффективно, но здесь есть ограничение: SQL Инъекция. Если бы я использовал PreparedStatement
, мне бы понадобился один для партии order_items
и один для партии delivery_details
. И тогда LAST_INSERT_ID()
не будет работать.
Есть ли способ обойти это? Из того, что я видел, нет. И мне нужно предотвратить инъекцию SQL путем очистки message
и amount
с помощью Java, что, по-видимому, имеет ограничения. Например, message
может содержать апострофы и эмодзи. Кто-нибудь может придумать другое решение?
РЕДАКТИРОВАТЬ
Вот действительно эффективное решение, которое я придумал:
String orderItemSql = "INSERT INTO order_items (amount) VALUES (?) ";
for (int x = 1; x < orderItemDeliveries.size(); x++) {
orderItemSql += ", (?)";
}
PreparedStatement preparedStatement = connection.prepareStatement(orderItemSql, Statement.RETURN_GENERATED_KEYS);
int i = 1;
for (int x = 0; x < orderItemDeliveries.size(); x++) {
preparedStatement.setDouble(i++, orderItemDelivery.getAmount().doubleValue());
}
preparedStatement.executeUpdate();
Long ids[] = new Long[orderItemDeliveries.size()];
ResultSet rs = preparedStatement.getGeneratedKeys();
int x = 0;
while (rs.next()) {
ids[x] = rs.getLong(1);
x++;
}
String deliveryDetails = "INSERT INTO `delivery_details` (`orderItemId`, `message`) VALUES (?, ?)";
for (x = 1; x < orderItemDeliveries.size(); x++) {
deliveryDetails += ", (?)";
}
preparedStatement = connection.prepareStatement(deliveryDetails);
i = 1;
for (x = 0; x < orderItemDeliveries.size(); x++) {
orderItemDelivery = orderItemDeliveries.get(x);
preparedStatement.setLong(i++, ids[x]);
preparedStatement.setString(i++, orderItemDelivery.getMessage());
}
preparedStatement.executeUpdate();
Так что для этого для работы порядок ids
должен быть последовательным, а порядок orderItemDeliveries
не должен изменяться между первым l oop в списке и вторым.
Это немного странно, но работает. Я что-то упустил?