Java 更快地批量加载嵌套数据
我正在填充两个表,它们具有1-many关系 因此,我在Java 更快地批量加载嵌套数据,java,sql,spring,postgresql,Java,Sql,Spring,Postgresql,我正在填充两个表,它们具有1-many关系 因此,我在外部中插入一行,获取该行的(自动递增主键)id,然后在内部中插入100行(所有外键都指向外部.id) 然后我重复,50次。对于外部中的每个条目,我必须插入,读取id,然后插入到内部 这太慢了。大部分时间用于将100行代码加载到内部。我想如果我能在一次批处理操作中将所有50*100行插入内部,速度会快得多。但是我不知道怎么做-我怎么才能使外键工作 其他人是如何使其高效的 我正在使用Java/Spring。这100行插入了一个JdbcTempla
外部
中插入一行,获取该行的(自动递增主键)id
,然后在内部
中插入100行(所有外键都指向外部.id
)
然后我重复,50次。对于外部
中的每个条目,我必须插入,读取id
,然后插入到内部
这太慢了。大部分时间用于将100行代码加载到内部
。我想如果我能在一次批处理操作中将所有50*100行插入内部
,速度会快得多。但是我不知道怎么做-我怎么才能使外键工作
其他人是如何使其高效的
我正在使用Java/Spring。这100行插入了一个JdbcTemplate.batchUpdate()
更新-以下内容似乎适用于Spring 3.1.1和Postgres 9.2-1003.jdbc4
/**
* An alternative implementation that should be faster, since it inserts
* in just two batches (one for inner and one fo router).
*
* @param db A connection to the database.
* @param data The data to insert.
*/
public final void insertBatchier(final JdbcTemplate db,
final AllDataBlocks data) {
final List<Object[]> outers = data.getOuter();
List<Integer> ids = db.execute(
new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(
final Connection con) throws SQLException {
return con.prepareStatement(getInsertSql(),
Statement.RETURN_GENERATED_KEYS);
}
},
new PreparedStatementCallback<List<Integer>>() {
@Override
public List<Integer> doInPreparedStatement(final PreparedStatement ps)
throws SQLException {
for (Object[] outer: outers) {
for (int i = 0; i < outer.length; ++i) {
setParameterValue(ps, i + 1,
SqlTypeValue.TYPE_UNKNOWN, outer[i]);
}
ps.addBatch();
}
ps.executeBatch();
RowMapperResultSetExtractor<Integer> ids =
new RowMapperResultSetExtractor<Integer>(
new ItemRowMapper.IdRowMapper());
try (ResultSet keys = ps.getGeneratedKeys()) {
return ids.extractData(keys);
}
}
});
innerTable.insert(db, data.getInner(ids));
}
/**
*另一种应该更快的实现,因为它插入
*只需两批(一批用于内部路由器,一批用于fo路由器)。
*
*@param db连接到数据库。
*@param data要插入的数据。
*/
公共最终void insertBatchier(最终JdbcTemplate db,
最终所有数据块(数据){
最终列表outers=data.getOuter();
List id=db.execute(
新的PreparedStatementCreator(){
@凌驾
公共PreparedStatement createPreparedStatement(
最终连接(con)引发SQLException{
返回con.prepareStatement(getInsertSql(),
语句。返回\u生成的\u键);
}
},
新的PreparedStatementCallback(){
@凌驾
公共列表未编制报表(最终编制报表ps)
抛出SQLException{
对于(对象[]外部:外部){
对于(int i=0;i
我不太熟悉JdbcTemplate
,但假设它与JDBC类似,我会使用与以下代码类似的方法(我可能会将其分解为多个方法):
private static final int BATCH_SIZE = 50;
public void addBatch(Connection connection, List<Outer> outers) {
PreparedStatement outerInsertStatement = connection.prepareStatement("...", Statement.RETURN_GENERATED_KEYS);
PreparedStatement innerInsertStatement = connection.prepareStatement("...", Statement.RETURN_GENERATED_KEYS);
List<Integer> outerIds = new ArrayList<Integer>();
for(Outer outer : outers) {
outerInsertStatement.setParameter(...);
...
outerInsertStatement.setParameter(...);
outerInsertStatement.addBatch();
}
outerInsertStatement.executeBatch();
//Note, this line requires JDBC3
ResultSet primaryKeys = outerInsertStatement.getGeneratedKeys();
while(!primaryKeys.isAfterLast()) {
outerIds.add(primaryKeys.getInt(0));
}
for(int i = 0; i < outers.size(); i++) {
Outer outer = outers.get(i);
Integer outerId = outerIds.get(i);
for(Inner inner : outer.getInners()) {
//One of these setParameter calls would use outerId
innerInsertStatement.setParameter(...);
...
innerInsertStatement.setParameter(...);
innerInsertStatement.addBatch();
if( (i+1) % BATCH_SIZE == 0) {
innerInsertStatement.executeBatch();
}
}
innerInsertStatement.executeBatch();
}
}
private static final int BATCH\u SIZE=50;
public void addBatch(连接,列出outer){
PreparedStatement outerInsertStatement=connection.prepareStatement(“…”,Statement.RETURN\u生成的\u键);
PreparedStatement innerInsertStatement=connection.prepareStatement(“…”,Statement.RETURN\u生成的\u键);
List outerIds=new ArrayList();
用于(外部:外部){
setParameter(…);
...
setParameter(…);
outerInsertStatement.addBatch();
}
outerInsertStatement.executeBatch();
//注意,此行需要JDBC3
ResultSet primaryKeys=outerInsertStatement.getGeneratedKeys();
而(!primaryKeys.isAfterLast()){
add(primaryKeys.getInt(0));
}
对于(int i=0;i
我不明白。为什么不将外部
中的所有记录插入一批,然后将内部
中的所有记录插入一批?为什么不发布一些代码呢?如果我可以检索外部表中所有插入的索引,我就可以这样做。有没有办法在一系列值上检索自动递增键?我必须手动管理密钥生成吗?可能有多个线程正在向这些表中添加/删除数据。这与java代码不符,但从数据库的角度来看,您必须采用基于集合的方法,而不是逐行执行此操作,这听起来就像您当前正在执行的操作。能否将所有数据转储到postgre中的临时表中,并使用临时表作为源执行插入?这将一次性插入所有数据,这比许多微小的插入要便宜。@JustBob(谢谢)忽略java,如何设置使内部(或临时)表具有外部表的外键引用?这是我的根本问题。内部表的每100行(或多或少)引用一个不同的外部行。并且ID是自动递增的。我想我需要自己管理这些密钥。我添加了一些sql表以备不时之需。太棒了。我不知道GetGeneratedKey的存在。谢谢(环顾四周,它似乎需要一个非常新的jdbc和postgres库,但这不应该是一个问题)?我试过了(请参阅问题“更新”部分中的代码),但似乎postgres驱动程序不支持这个(在jdbc标准中是可选的)啊,不,对不起。我倾向于在MySQL中工作,没有在Postgres中工作过。在准备语句时,您可能需要执行con.prepareStatement(sql,Statement.RETURN\u GENERATED\u key)
。我读了,忘了。哑的但你更令人敬畏。看来是这样
/**
* An alternative implementation that should be faster, since it inserts
* in just two batches (one for inner and one fo router).
*
* @param db A connection to the database.
* @param data The data to insert.
*/
public final void insertBatchier(final JdbcTemplate db,
final AllDataBlocks data) {
final List<Object[]> outers = data.getOuter();
List<Integer> ids = db.execute(
new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(
final Connection con) throws SQLException {
return con.prepareStatement(getInsertSql(),
Statement.RETURN_GENERATED_KEYS);
}
},
new PreparedStatementCallback<List<Integer>>() {
@Override
public List<Integer> doInPreparedStatement(final PreparedStatement ps)
throws SQLException {
for (Object[] outer: outers) {
for (int i = 0; i < outer.length; ++i) {
setParameterValue(ps, i + 1,
SqlTypeValue.TYPE_UNKNOWN, outer[i]);
}
ps.addBatch();
}
ps.executeBatch();
RowMapperResultSetExtractor<Integer> ids =
new RowMapperResultSetExtractor<Integer>(
new ItemRowMapper.IdRowMapper());
try (ResultSet keys = ps.getGeneratedKeys()) {
return ids.extractData(keys);
}
}
});
innerTable.insert(db, data.getInner(ids));
}
private static final int BATCH_SIZE = 50;
public void addBatch(Connection connection, List<Outer> outers) {
PreparedStatement outerInsertStatement = connection.prepareStatement("...", Statement.RETURN_GENERATED_KEYS);
PreparedStatement innerInsertStatement = connection.prepareStatement("...", Statement.RETURN_GENERATED_KEYS);
List<Integer> outerIds = new ArrayList<Integer>();
for(Outer outer : outers) {
outerInsertStatement.setParameter(...);
...
outerInsertStatement.setParameter(...);
outerInsertStatement.addBatch();
}
outerInsertStatement.executeBatch();
//Note, this line requires JDBC3
ResultSet primaryKeys = outerInsertStatement.getGeneratedKeys();
while(!primaryKeys.isAfterLast()) {
outerIds.add(primaryKeys.getInt(0));
}
for(int i = 0; i < outers.size(); i++) {
Outer outer = outers.get(i);
Integer outerId = outerIds.get(i);
for(Inner inner : outer.getInners()) {
//One of these setParameter calls would use outerId
innerInsertStatement.setParameter(...);
...
innerInsertStatement.setParameter(...);
innerInsertStatement.addBatch();
if( (i+1) % BATCH_SIZE == 0) {
innerInsertStatement.executeBatch();
}
}
innerInsertStatement.executeBatch();
}
}