All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fluentjdbc.DbSyncBuilderContext Maven / Gradle / Ivy

There is a newer version: 0.5.3
Show newest version
package org.fluentjdbc;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class DbSyncBuilderContext  {
    private final DbTableContext table;
    private EnumMap status = new EnumMap<>(DatabaseSaveResult.SaveStatus.class);
    private final List theirObjects;
    private boolean isCached = false;
    private Map, List> ourRows;
    private Map, List> theirRows;
    private List uniqueFields = new ArrayList<>();
    private List> uniqueValueFunctions = new ArrayList<>();
    private List updatedFields = new ArrayList<>();
    private List> updatedValueFunctions = new ArrayList<>();

    public DbSyncBuilderContext(DbTableContext table, List theirObjects) {
        this.table = table;
        this.theirObjects = theirObjects;
        Stream.of(DatabaseSaveResult.SaveStatus.values()).forEach(v -> status.put(v, 0));
    }

    public DbSyncBuilderContext unique(String field, Function valueFunction) {
        uniqueFields.add(field);
        uniqueValueFunctions.add(valueFunction);
        return this;
    }

    public DbSyncBuilderContext field(String field, Function valueFunction) {
        updatedFields.add(field);
        updatedValueFunctions.add(valueFunction);
        return this;
    }

    public DbSyncBuilderContext cacheExisting() {
        if (isCached) {
            return this;
        }
        Map, List> ourRows = new HashMap<>();
        table.query().forEach(row -> {
            List key = new ArrayList<>();
            for (String field : uniqueFields) {
                key.add(row.getObject(field));
            }
            List fields = new ArrayList<>();
            for (String field : updatedFields) {
                fields.add(row.getObject(field));
            }
            ourRows.put(key, fields);
        });
        this.ourRows = ourRows;

        Map, List> theirRows = new HashMap<>();
        theirObjects.forEach(entity -> {
            List keys = uniqueValueFunctions.stream()
                    .map(function -> DatabaseStatement.toDatabaseType(function.apply(entity), table.getConnection()))
                    .collect(Collectors.toList());
            theirRows.put(keys, updatedValueFunctions.stream()
                    .map(function -> DatabaseStatement.toDatabaseType(function.apply(entity), table.getConnection()))
                    .collect(Collectors.toList()));
        });
        this.theirRows = theirRows;
        isCached = true;

        return this;
    }

    public DbSyncBuilderContext deleteExtras() {
        cacheExisting();
        int count = table.bulkDelete(this.ourRows.keySet().stream()
                .filter(key -> !theirRows.containsKey(key)))
                .whereAll(uniqueFields, entry -> entry)
                .execute();
        status.put(DatabaseSaveResult.SaveStatus.DELETED, count);
        return this;
    }

    public DbSyncBuilderContext insertMissing() {
        cacheExisting();
        int count = table.bulkInsert(this.theirRows.entrySet().stream()
                .filter(entry -> !ourRows.containsKey(entry.getKey())))
                .setFields(uniqueFields, Map.Entry::getKey)
                .setFields(updatedFields, Map.Entry::getValue)
                .execute();
        status.put(DatabaseSaveResult.SaveStatus.INSERTED, count);
        return this;
    }

    public DbSyncBuilderContext updateDiffering() {
        cacheExisting();
        int count = table.bulkUpdate(this.theirRows.entrySet().stream()
                .filter(entry -> ourRows.containsKey(entry.getKey()))
                .filter(entry -> {
                    boolean equal = valuesEqual(entry.getKey());
                    if (equal) {
                        addStatus(DatabaseSaveResult.SaveStatus.UNCHANGED);
                    }
                    return !equal;
                }))
                .whereAll(uniqueFields, Map.Entry::getKey)
                .setFields(updatedFields, Map.Entry::getValue)
                .execute();
        status.put(DatabaseSaveResult.SaveStatus.UPDATED, count);

        return this;
    }

    protected boolean valuesEqual(List key) {
        return areEqual(ourRows.get(key), theirRows.get(key));
    }

    protected boolean areEqual(List a, List b) {
        if (a == null) {
            return b == null;
        } else if (a.size() != b.size()) {
            return false;
        }
        for (int i = 0; i < a.size(); i++) {
            if (!areEqual(a.get(i), b.get(i))) {
                return false;
            }
        }
        return true;
    }

    protected boolean areEqual(Object o, Object o1) {
        if (o instanceof BigDecimal) {
            if (!(o1 instanceof BigDecimal)) {
                return false;
            }
            return ((BigDecimal)o).compareTo((BigDecimal)o1) == 0;
        }
        return Objects.equals(o, o1);
    }

    private void addStatus(DatabaseSaveResult.SaveStatus status) {
        this.status.put(status, this.status.get(status) + 1);
    }

    public EnumMap getStatus() {
        return status;
    }
}