JPA Spring игнорирует отложенную загрузку внутри @Transacational - PullRequest
0 голосов
/ 29 октября 2019

У меня есть класс обслуживания Spring, где я загружаю объект JPA (цель) через CRUD. Этот целевой класс имеет сопоставление «один на май» с отложенной загрузкой.

Я хотел бы запросить этот объект внутри метода службы Spring, который помечен @Transactional, и избежать загрузки дочерних объектов.

Когда я выполняю следующий код, все дочерние данные загружаются и лень игнорируется.

        @Override
        @Transactional
        public boolean changeState(boolean enabled, final EventType eventType, final String deviceSerialNumber) {
            final UniqueUser user = userService.getUser();
            final Target target = targetRepository.findEventsByUserIdAndTenantIdAndTargetDeviceId(user.getUserId(), user.getTenantId(), deviceSerialNumber);
//here everything gets loaded
            if (target == null) {
                return false;
            }
            final List<EventHistory> events = target.getEvents().stream()
                    .filter(event -> event.getEventType() == eventType)
                    .filter(event -> event.isActive() != enabled)
                    .collect(Collectors.toList());
            events.forEach(event -> event.setActive(enabled));
            if (events.isEmpty()) {
                return false;
            }
            return true;
        }

Отображения:

    @ToString
    @Entity
    @Table(name = "target")
    public class Target {

        @Id
        @GeneratedValue(generator = "uuid")
        @GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
        @Column(name = "id", unique = true)
        private UUID id;

        @Column(name = "user_id")
        private String userId;

        @Column(name = "tenant_id")
        private String tenantId;

        @Column(name = "target_device_id")
        private String targetDeviceId;

        @Column(name = "target_type")
        private TargetType targetType;

        @OneToMany(mappedBy = "target", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
        @JsonManagedReference
        private List<EventHistory> events = new ArrayList<>();

        public void addEvents(EventHistory event) {
            events.add(event);
            event.setTarget(this);
        }

    }

    @Entity
    @Data
    @Table(name = "event_history")
    public class EventHistory {

        @Id
        @GeneratedValue(generator = "uuid")
        @GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
        @Column(name = "id", unique = true)
        private UUID id;

        @Column(name = "active")
        private boolean active;


        @OneToMany(mappedBy = "event", cascade = CascadeType.ALL, orphanRemoval = true)
        @JsonManagedReference
        private List<EventTimestamp> timestamps = new ArrayList<>();

        @ManyToOne(optional = false, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
        @JoinColumn(name = "target_id", nullable = false)
        @OnDelete(action = OnDeleteAction.CASCADE)
        @JsonBackReference
        private Target target;

        public void addTimestamps(EventTimestamp eventTimestamp) {
            timestamps.add(eventTimestamp);
            eventTimestamp.setEvent(this);
        }

    }

    @Entity
    @Data
    @Table(name = "event_timestamp")
    public class EventTimestamp {
        @Id
        @GeneratedValue(generator = "uuid")
        @GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
        @Column(name = "id", unique = true)
        private UUID id;


        @Column(name = "due_timestamp")
        private Timestamp dueTimestamp;

        @Column(name = "period")
        private String period;

        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "event_id", nullable = false)
        @JsonBackReference
        private EventHistory event;

Итак, мой вопрос, как сохранить ленивую загрузку внутри аннотированных транзакциями функций?

Ответы [ 2 ]

1 голос
/ 31 октября 2019

Мое первое предположение, что основной причиной была неправильно реализованная функция хранилища, было неверным. Настоящей проблемой была аннотация @ToString. Это добавило коллекцию событий «один ко многим» в toString (). Находясь внутри транзакции и получая доступ к объекту, вызывается toString, и коллекция загружается.

Решением было исключить коллекцию из toString через.

@OneToMany(mappedBy = "target", cascade = CascadeType.ALL, orphanRemoval = true)
@ToString.Exclude
private List<EventHistory> events;
0 голосов
/ 30 октября 2019

Я понял это. Проблема заключалась в коде репозитория. Метод findBy ожидает List вместо одного объекта. Мой оригинальный репозиторий выглядел следующим образом:

@Repository
public interface TargetRepository extends CrudRepository<Target, UUID> {

  Target findEventsByUserIdAndTenantIdAndTargetDeviceId(String userId, String tenantId, String targetId);

}

Если изменить его на приведенную ниже версию, то исправим.

@Repository
public interface TargetRepository extends CrudRepository<Target, UUID> {
    List<Target> findEventsByUserIdAndTenantIdAndTargetDeviceId(String userId, String tenantId, String targetId);
}
...