使用Spring JdbcTemplate向多个表中插入数据

6
我正在使用Java和Spring JDBC模板向两个表中插入n条记录。代码如下:
假设daos.xml已正确配置。
ApplicationContext ctxt = new ClassPathXmlApplicationContext("daos.xml");
JdbcTemplate template = (JdbcTemplate) ctxt.getBean("jdbcTemplate");

final List<Person> list = new ArrayList<>();
        final List<Role> roles = new ArrayList<>();
        for(int i =1; i<=100; i++){
            Person item = new Person();
            item.setFirstName("Naveen" + i);
            item.setLastName("kumar" + i);
            item.setDescription("D" + i);
            list.add(item);

            Role role = new Role();
            role.setName("Admin");
            role.setCode("c"  + i);
            roles.add(role);

        }

String sql = "insert into person(first_name, last_name, description) values(?,?,?)";

            int[] arr = template.batchUpdate(sql, new BatchPreparedStatementSetter() {

                        @Override
                        public void setValues(PreparedStatement ps, int i) throws SQLException                             {
                            Person person = list.get(i);
                            ps.setObject(1, person.getFirstName());
                            ps.setObject(2, person.getLastName());
                            ps.setObject(3, person.getDescription());
                        }

                        @Override
                        public int getBatchSize() {
                            return list.size()
                        }
                    });

我还配置了事务管理器。
我的问题是如何使用批处理将数据插入到person和role表中。因为一个人可以有多个角色,所以当我插入person时,需要一起插入角色ID。在这种情况下,person插入查询将如下所示。
String sql = "insert into person(first_name, last_name, description, role_id) values(?,?,?, ?)";

我想批量执行它,因为在我的情况下,我有至少10k人的列表需要使用文件进行解析。如果我先将角色插入表中,然后获取并再次插入人员,这可能会导致性能下降。

1
如果你有10K条记录,考虑使用项目Spring Batch。我只会在少量记录时使用JdbcTemplate的batch方法,例如发票的详细信息,其中每张发票可能只有1到20条记录。 - Manuel Jordan
我想要将记录插入到两个不同的表中,比如说Role表和Person表。我需要解析一个文件,这个文件可能包含一次性处理的10k条记录。所以在将数据从文件解析成Role对象和Person对象之后,我想要先插入Role对象,然后获取已插入的Role对象的ID,然后再插入具有角色的Person对象。所以我的问题是,我想要同时维护事务和批处理,以便减少与数据库的往返次数。现在,请告诉我正确的方法来实现这个任务。什么是Spring Batch,它如何使用?给我一个示例的链接。 - keepmoving
你好,Spring Batch可以满足你的需求,并且具有事务支持和重要的“块”功能,你可以使用10个1K的块并获得事务支持。请参考http://projects.spring.io/spring-batch/。 - Manuel Jordan
非常感谢。我也找到了它并正在处理。 - keepmoving
1个回答

7

你可以使用多行语句和 LAST_INSERT_ID() MySql 函数:

String sql = "insert into role(name, code) values(?,?);" +
    "insert into person(first_name, last_name, description, role_id) values(?,?,?,(SELECT LAST_INSERT_ID()));";

int[] arr = template.batchUpdate(sql, new BatchPreparedStatementSetter() {

    @Override
    public void setValues(PreparedStatement ps, int i) throws SQLException {
        Role role = roles.get(i);
        Person person = list.get(i);
        ps.setObject(1, role.getName());
        ps.setObject(2, role.getCode();
        ps.setObject(3, person.getFirstName());
        ps.setObject(4, person.getLastName());
        ps.setObject(5, person.getDescription());
    }

    @Override
    public int getBatchSize() {
        return list.size()
    }
});

亲爱的Nailgun,第一个问题 - 当多个线程同时运行时,插入这种数据是否是正确的方法。意思是获取最后一个id。第二个问题 - 我将如何插入角色。如果我使用jdbcTemplate.update()。那么又会对数据库进行数千次访问。这就是为什么我正在寻找一些解决方案,将这两个插入合并为单个批处理执行。 - keepmoving
  1. LAST_INSERT_ID() 返回当前连接中插入的最后一个 ID,所以没问题。
  2. 然后您需要稍微更改一下代码,使用 JdbcTemplate 中的 public <T> int[][] batchUpdate(String sql, final Collection<T> batchArgs, final int batchSize, final ParameterizedPreparedStatementSetter<T> pss) 方法。我建议将 batchSize 参数设置为 1000。
- Nailgun
谢谢,我希望它能够正常工作。我也按照你所说的做了同样的事情。 - keepmoving
在Postgres中,带有;的多行插入不起作用:PSQLException: Too many update results were returned. 参见:https://www.postgresql.org/message-id/373352.78463.qm@web32701.mail.mud.yahoo.com - gene b.
请使用CTE,参考以下链接:https://stackoverflow.com/questions/37567741/how-to-insert-batch-for-multiple-table-using-spring-namedparameterjdbctemplate/67380135#67380135 - 7er
嗨,我有一个类似的情况需要将数据插入到两个不同的表中,但是在第二个插入语句处出现了“PreparedStatementCallback; bad SQL grammar”错误。请问有什么建议吗?代码与上面完全相同。 - Waheed Khan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接