Reputation: 1335
I have a spring service class where I'm loading a JPA object (target) via CRUD. This target class has a one-to-may mapping that is set to lazy loading.
I would like to query this object inside a spring service method that is annotated with @Transactional
and avoid that the childs are being loaded.
When I execute the following code all child data is loaded and laziness is ignored.
@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;
}
Mappings:
@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;
So my question is how to keep lazy loading inside transaction annotated functions?
Upvotes: 0
Views: 969
Reputation: 1335
My first assumption that the root cause had been the wrongly implemented repository function was wrong. The real issue was the @ToString annotation. This added the one-to-many event collection to toString(). While being inside the transaction and accessing the object, toString got invoked and the the collection was loaded.
Solution was to exclude the collection from toString via.
@OneToMany(mappedBy = "target", cascade = CascadeType.ALL, orphanRemoval = true)
@ToString.Exclude
private List<EventHistory> events;
Upvotes: 2
Reputation: 1335
I figured it out. The problem was ins the Repository code. The findBy method is expecting a List instead of a single object. My original repository looked like this
@Repository
public interface TargetRepository extends CrudRepository<Target, UUID> {
Target findEventsByUserIdAndTenantIdAndTargetDeviceId(String userId, String tenantId, String targetId);
}
Changing it to the below version fixed it.
@Repository
public interface TargetRepository extends CrudRepository<Target, UUID> {
List<Target> findEventsByUserIdAndTenantIdAndTargetDeviceId(String userId, String tenantId, String targetId);
}
Upvotes: 0