【问题标题】:JPA Spring ignores Lazy loading inside @TransacationalJPA Spring 忽略 @Transacational 中的延迟加载
【发布时间】:2019-10-29 14:52:57
【问题描述】:

我有一个 spring 服务类,我正在通过 CRUD 加载一个 JPA 对象(目标)。这个目标类有一个设置为延迟加载的一对五映射。

我想在带有@Transactional 注释的spring 服务方法中查询该对象,并避免加载子对象。

当我执行以下代码时,所有子数据都被加载,惰性被忽略。

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

所以我的问题是如何在事务注释函数中保持延迟加载?

【问题讨论】:

  • 你能发布DAO代码吗?通常我认为没有必要将服务类注释为事务性的,尤其是当它只是调用始终是事务性的 DAO 时。
  • findById 首先在持久性缓存中执行查找。只是,如果它找不到现有实例,它会访问数据库。你确定 Foo 之前没有加载吗?您能否验证此操作是否命中数据库?
  • 您在该服务方法中还做了什么?贴出完整代码
  • 关键问题:您如何验证集合确实已加载?
  • 我看到正在执行的sql

标签: java hibernate jpa spring-data-jpa spring-data


【解决方案1】:

我第一个假设根本原因是错误实现的存储库功能是错误的。真正的问题是 @ToString 注释。这将一对多的事件集合添加到 toString()。在事务内部并访问对象时,调用了 toString 并加载了集合。

解决方案是从 toString via 中排除集合。

@OneToMany(mappedBy = "target", cascade = CascadeType.ALL, orphanRemoval = true)
@ToString.Exclude
private List<EventHistory> events;

【讨论】:

    【解决方案2】:

    我想通了。问题出在存储库代码中。 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);
    }
    

    【讨论】:

      猜你喜欢
      • 2021-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-06
      • 2011-10-02
      • 2013-11-02
      • 2022-09-29
      • 2011-12-20
      相关资源
      最近更新 更多