Оптимизация производительности JPA при вставке множества сущностей со встроенным первичным ключом - PullRequest
0 голосов
/ 05 ноября 2019

Я наивно реализовал веб-сервис, который использует список объектов Json и сохраняет их в базе данных SQL, используя springframework.data.jpa (JPA & Hibernate). Однако решение имеет низкую производительность, и профилировщик дал мне подсказку, что основная проблема заключается в создании сущностей из объектов Json один за другим.

Приведенный ниже код упрощен, но в основном: для каждого объекта Json во входящем списке создаются две сущности: DataEntity и IdentityEntity. Первый хранит интересующие данные и использует последний как FK, который имеет сложное PK времени и человека.

Я бы хотел ускорить процесс хранения. С помощью профилировщика я определил, что после вставки каждой новой сущности выполняется слишком много операций flush . Так как мне нужно вставить тысячи записей одновременно, это вызывает проблему с производительностью. Могу ли я выполнить вставку в одной транзакции или как ее оптимизировать?

Класс данных (у меня много похожих классов):

@Entity
public class DataEntity {
    @EmbeddedId
    private IdentityEntity identity;
    private Double data;
}

Встраиваемая сущность:

@Embeddable
public class IdentityEntity implements Serializable {
    @NonNull
    private Long personId;
    @NonNull
    private Long datetimeId;
}

Репозиторий JPA:

@Repository
public interface DataRepository extends JpaRepository<DataEntity, IdentityEntity> {}

Упрощенный контроллер:

public class DataController{
    @Autowired
    private DataRepository dataRepository;
    @Autowired
    private DatetimeRepository datetimeRepository;

    @PostMapping("/upload")
    public void upload(...List<DataJson> items) {
        PersonEntity person = getPerson(...);          // fast enough
        for (DataJson i : items) {                 // begin transaction here?
            saveNewEntity(i, person.getId());
        }
    }

    private void saveNewEntity(DataJson json, Long personId) {
        TimeEntity savedDatetime = datetimeRepository.save(new TimeEntity(json.getDatetime()));
        IdentityEntity mi = IdentityEntity(personId, savedDatetime.getId());

        DataEntity entry = new DataEntity(mi, json.getData());
        dataRepository.save(entry);
    }
}

Редактировать : после дальнейшего копания в профилировщик, яобнаружил, что другой трудоемкой операцией может быть само управление транзакциями. Хотя я не реализовал и не настроил поведение транзакций, я подозреваю, что Spring Boot настроил что-то по умолчанию для Hibernate ORM. Я начинаю думать, что транзакция теперь создается на каждой итерации цикла, что является первой проблемой производительности, а также вызывает вторую проблему, когда в конце транзакции все сбрасывается и записывается в БД.

1 Ответ

1 голос
/ 05 ноября 2019

Да. Все методы в SimpleJpaRepository помечены @Transactional.

Просто добавьте аннотацию @Transactional к вашему методу загрузки.

... или

Сначаласоздайте все объекты и сохраните их за один раз, используя метод save(Iterable<S> entities).

...