Java 在JPA中,是否有方法在DB中批量插入数据,并且仅在DB中不存在数据时插入

Java 在JPA中,是否有方法在DB中批量插入数据,并且仅在DB中不存在数据时插入,java,spring,hibernate,jpa,Java,Spring,Hibernate,Jpa,在JPA中,是否有任何方法可以在DB中批量插入数据,并且只有在DB中不存在数据时才插入数据。当我尝试批量插入时,由于唯一键约束,它抛出异常。我想插入数据库中不存在的所有记录,其余的应该跳过 org.springframework.dao.DataIntegrityViolationException:无法 执行语句;SQL[n/a];约束 [参考号\主控号\ id \密钥];嵌套异常是 org.hibernate.exception.ConstraintViolationException:无法

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

org.springframework.dao.DataIntegrityViolationException:无法 执行语句;SQL[n/a];约束 [参考号\主控号\ id \密钥];嵌套异常是 org.hibernate.exception.ConstraintViolationException:无法 执行语句


我和我的团队最终创建了我们的JpaRepositories扩展以添加此类行为的实现

主接口

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

    void batchInsertUsingMode(Collection<T> items, final Mode mode);
}
批插入器实现

@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;
    }
}
@组件
@范围(“原型”)
@懒惰的
公共类BatchInserter扩展了AbstractBaseBatchJDBC,实现了BatchInsertable{
@凌驾
公共无效批处理插入模式(最终收集项目,最终模式){
最终映射参数=new HashMap();
参数.put(“MODE”,MODE==MODE.IGNORE?“INSERT IGNORE”:MODE==MODE.REPLACE?“REPLACE”:“INSERT”);
parameters.put(“STAGING_TABLE”,getTableName());
parameters.put(“COLUMNS”,buildColumnNameList());
parameters.put(“占位符”,buildBindVariablePlaceholderList());
最终StrSubstitutor StrSubstitutor=新StrSubstitutor(参数);
最后一个字符串sqlTemplate=“${MODE}转换为`${STAGING_TABLE}`(${COLUMNS})值(${PLACEHOLDERS})”;
getJdbcTemplate().batchUpdate(strSubstitutor.replace(sqlTemplate)、buildBindVariables(items));
}
}
批删除器实现

@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;
    }
}
@组件
@范围(“原型”)
@懒惰的
公共类BatchDeleter扩展AbstractBaseBatchJDBC实现BatchDeletable{
@凌驾
public int batchDelete(最终列表项){
最终映射参数=new HashMap();
parameters.put(“STAGING_TABLE”,getTableName());
parameters.put(“COLUMNS”,buildColumnNameList());
parameters.put(“占位符”,buildBindVariablePlaceholderList());
最终StrSubstitutor StrSubstitutor=新StrSubstitutor(参数);
最后一个字符串sqlTemplate=“从`${STAGING_TABLE}`中删除(${COLUMNS})=(${PLACEHOLDERS})”;
final int[]updateCounts=getJdbcTemplate().batchUpdate(strSubstitutor.replace(sqlTemplate),buildBindVariables(items));
返回金额(更新计数);
}
私有静态整数和(最终整数[]更新计数){
整数和=0;
for(最终int updateCount:updateCounts){
sum+=updateCount;
}
回报金额;
}
}
维护公共配置绑定逻辑的抽象类

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;
        }

    }
}
公共抽象类AbstractBaseBatchJDBC{
私有JdbcTemplate JdbcTemplate;
私有列表列提取器;
私有字符串表名;
公共void setColumnExtractor(最终列表ColumnExtractor){
this.columnExtractors=新的ArrayList(columnExtractors);
}
public void setTableName(最终字符串tableName){
this.tableName=tableName;
}
受保护的列表buildBindVariables(最终集合项){
返回fluenterable.from(items).transform(新BulkBindingTransform(ColumnExtractor)).toList();
}
受保护的JdbcTemplate getJdbcTemplate(){
返回jdbc模板;
}
受保护的字符串getTableName(){
返回表名;
}
@自动连线
public void setDataSource(最终数据源数据源){
this.jdbcTemplate=新的jdbcTemplate(数据源);
}
受保护的字符串buildColumnNameList(){
返回join(extract(columnExtractors,on(ColumnValueExtractors.class).getColumnName()),“,”;
}
受保护的字符串BuildBindVariablePlaceholder列表(){
返回联接(nCopies(columnExtractor.size(),“?”,”);
}
私有静态类BulkBindingTransform实现函数{
私有最终列表列;
私有BulkBindingTransform(最终列表列){
this.columns=列;
}
@可空
@凌驾
公共对象[]应用(最终T输入){
最终对象[]绑定=新对象[columns.size()];
对于(int i=0;i

这还可以让您绕过直接与默认的
save(Iterable Iterable)
接口交互时可能遇到的一些缓慢情况。我们在批处理SQL操作中使用它。这么简单的任务有多复杂,真令人惊讶。我敢打赌,您可以根据自己的具体需要对其进行精简。:)

非常翔实的回答。但是这并没有使用JPA特性,是吗?这不是更多的SpringJDBC模板吗?无论如何,这应该足以作为OP的解决方案。当我们以JPA/Hibernate方式处理它时,我们需要担心刷新缓存等以避免内存问题。同意。它与JPA配合得很好,但我不知道用纯JPA或纯Hibernate做这件事的方法。这就是为什么我们创造了这只野兽:)我很乐意找到一个更简单的选择。