当前位置: 首页 > 知识库问答 >
问题:

在JPA中,有没有办法批量插入数据库中的数据,只有在数据库中不存在时才插入

那安宁
2023-03-14

在JPA中,是否有任何方法可以在DB中批量插入数据,并且只有在DB中不存在数据时才插入数据。当我尝试批量插入时,由于唯一键约束,它抛出异常。我想插入数据库中不存在的所有记录,其余的应该跳过。

组织。springframework。刀。DataIntegrityViolationException:无法执行语句;SQL[n/a];约束[reference_number_master_id_key];嵌套的异常是org。冬眠例外ConstraintViolationException:无法执行语句

共有1个答案

舒博雅
2023-03-14

我和我的团队最终创建了我们的JpaRepository扩展的实现来添加这样的行为。

主接口

public interface BatchInsertable <T> {
    enum Mode {NORMAL, IGNORE, REPLACE}

    void batchInsertUsingMode(Collection<T> items, final Mode mode);
}

这就是我们如何将其连接到现有JPA存储库的方法。

public interface UrlRepository extends JpaRepository<UrlEntity, Long>, BatchInsertable<UrlEntity> {
    // insert common methods
}

... 这是一种用法

urlRepository.batchInsertUsingMode(newUrlEntities, BatchInsertable.Mode.IGNORE);

批插入器实现

@Component
@Scope("prototype")
@Lazy
public class BatchInserter<T> extends AbstractBaseBatchJdbcMutation<T> implements BatchInsertable<T> {

    @Override
    public void batchInsertUsingMode(final Collection<T> items, final Mode mode) {
        final Map<String, String> parameters = new HashMap<>();
        parameters.put("MODE", mode == Mode.IGNORE ? "INSERT IGNORE" : mode == Mode.REPLACE ? "REPLACE" : "INSERT");
        parameters.put("STAGING_TABLE", getTableName());
        parameters.put("COLUMNS", buildColumnNameList());
        parameters.put("PLACEHOLDERS", buildBindVariablePlaceholderList());

        final StrSubstitutor strSubstitutor = new StrSubstitutor(parameters);
        final String sqlTemplate = "${MODE} INTO `${STAGING_TABLE}` (${COLUMNS}) VALUES (${PLACEHOLDERS})";
        getJdbcTemplate().batchUpdate(strSubstitutor.replace(sqlTemplate), buildBindVariables(items));
    }
}

批删除器实现

@Component
@Scope("prototype")
@Lazy
public class BatchDeleter<T> extends AbstractBaseBatchJdbcMutation<T> implements BatchDeletable<T> {

    @Override
    public int batchDelete(final List<T> items) {
        final Map<String, String> parameters = new HashMap<>();
        parameters.put("STAGING_TABLE", getTableName());
        parameters.put("COLUMNS", buildColumnNameList());
        parameters.put("PLACEHOLDERS", buildBindVariablePlaceholderList());

        final StrSubstitutor strSubstitutor = new StrSubstitutor(parameters);
        final String sqlTemplate = "DELETE FROM `${STAGING_TABLE}` WHERE (${COLUMNS}) = (${PLACEHOLDERS})";
        final int[] updateCounts = getJdbcTemplate().batchUpdate(strSubstitutor.replace(sqlTemplate), buildBindVariables(items));
        return sum(updateCounts);
    }

    private static int sum(final int[] updateCounts) {
        int sum = 0;

        for (final int updateCount : updateCounts) {
            sum += updateCount;
        }

        return sum;
    }
}

维护公共配置绑定逻辑的抽象类

public abstract class AbstractBaseBatchJdbcMutation<T> {
    private JdbcTemplate jdbcTemplate;

    private List<ColumnValueExtractors> columnExtractors;

    private String tableName;

    public void setColumnExtractors(final List<ColumnValueExtractors> columnExtractors) {
        this.columnExtractors = new ArrayList<>(columnExtractors);
    }

    public void setTableName(final String tableName) {
        this.tableName = tableName;
    }

    protected List<Object[]> buildBindVariables(final Collection<T> items) {
        return FluentIterable.from(items).transform(new BulkBindingTransform<T>(columnExtractors)).toList();
    }

    protected JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    protected String getTableName() {
        return tableName;
    }

    @Autowired
    public void setDataSource(final DataSource datasource) {
        this.jdbcTemplate = new JdbcTemplate(datasource);
    }

    protected String buildColumnNameList() {
        return join(extract(columnExtractors, on(ColumnValueExtractors.class).getColumnName()), ",");
    }

    protected String buildBindVariablePlaceholderList() {
        return join(nCopies(columnExtractors.size(), "?"), ",");
    }

    private static class BulkBindingTransform<T> implements Function<T, Object[]> {

        private final List<ColumnValueExtractors> columns;
        private BulkBindingTransform(final List<ColumnValueExtractors> columns) {
            this.columns = columns;
        }

        @Nullable
        @Override
        public Object[] apply(final T input) {
            final Object[] bindings = new Object[columns.size()];

            for (int i = 0; i < columns.size(); i++) {
                bindings[i] = columns.get(i).resolveValue(input);
            }

            return bindings;
        }

    }
}

这将允许您绕过直接与默认的save(Iterable)接口时可能遇到的一些缓慢情况

 类似资料:
  • 我想我在这段代码中遇到了一点问题:当我试图在数据库中插入值时,我遇到了一个错误。

  • root-context.xml文件是: servlet-context.xml文件是: 我的@Entity类是: 控制台输出为:

  • 问题内容: 我正在尝试将数据插入为主数据ID,该数据在MySQL数据库中具有一个字母数字值和两个数字值。该数据将自动递增生成数字,但字母数字值将是固定的。像D1,D2 .... D54,D55,D56等。这里,“ D”始终相同,但是数字将自动递增。有什么办法吗? 问题答案: 首先, 不建议这样做 ,就像其他评论一样,您可以动态生成此id值。 但是,尽管如此,至少有两种方法可以实现: 或多或少可靠的

  • 我正在inMemory数据库中插入数据,当插入数据时,我得到了一个问题, 使用boot、JPA、H2db在内存中插入数据的示例程序 > 创建Pojo并使用JPA注释进行注释 > 配置在app.prop:中 在data.sql文件中添加了给定表 为data.sql中提到的转换添加了名称。 在哪里配置;在Springboot中? 波乔 控制器 错误原因:对名为'in memorydatabaseShu

  • 我的数据库代码是这样的。 出现成功保存的消息,但在数据库中找不到该值。请告诉我为什么会这样?

  • 我创建了一个向MySql数据库插入数百万个值的程序。我读到过有关批插入的文章,它将优化我的程序并使其更快,但当我尝试这样做时,它以同样的方式工作。我没有将每个值插入数据库,而是每次将500个值保存在一个列表中,然后将它们插入一个大循环中,如下所示: 然后我删除列表中的所有值,并再次开始收集500个值。它不应该工作得更好吗? 我的插入代码是: 我有一些问题: 1。为什么当我批量插入时它不能更快地工作