I spent many times to solve this problem - I know, this is very common problem but I can't find any solution!
I try to save my object (in one shot) that consists of some nested objects/collections. I want to fill window
object with data from windowDto
and then save window object in db together with nested objects
I get the following error:
object references an unsaved transient instance - save the transient instance before flushing : pl.entity.Window.path-> window.domain.model.entity.Path
I want to save object window:
@EqualsAndHashCode(callSuper = false, of = "id")
@Builder
@Entity
@Table(name = "window")
@Getter
@Setter
public class Window {
@Column(nullable = false, updatable = false)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "path_id", foreignKey = @ForeignKey(name = "FK_path_w"))
private Path path;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "store_id", foreignKey = @ForeignKey(name = "FK_store_w"))
@NotNull
private Store store;
}
Then my first nested objects:
@Entity
@Getter
@Setter
@EqualsAndHashCode(callSuper = false, of = "uuid")
@NoArgsConstructor
public class Path {
@Column(nullable = false, updatable = false)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OrderBy(value = "point_order asc")
@OneToMany(mappedBy = "path", cascade = CascadeType.ALL, orphanRemoval = true)
Set<Point> points;
public void replacePoints(Set<Point> points) {
if (!CollectionUtils.isEmpty(this.getPoints())) {
this.getPoints().clear();
}
if (this.getPoints() != null) {
this.getPoints().addAll(points);
} else {
this.setPoints(points);
}
}
public void removePoint(Point point) {
point.setPath(null);
this.getPoints().remove(point);
}
}
Path
has points
:
@Entity
@Getter
@Setter
@Table(name = "point")
public class Point {
@Column(nullable = false, updatable = false)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OrderBy(value = "operation_order asc")
@OneToMany(mappedBy = "point", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
private Set<PointOperation> operations;
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "path_id", foreignKey = @ForeignKey(name = "FK_point_path"), nullable = false)
private Path path;
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.DETACH}, fetch = FetchType.LAZY)
@JoinColumn(name = "path_address_id", foreignKey = @ForeignKey(name = "FK_point_path_address"), nullable = false)
private PathAddress pathAddress;
public void removePointOperation(PointOperation spotOperation) {
pointOperation.setPoint(null);
this.getOperations().remove(pointOperation);
}
}
And my second nested objects/collection:
Entity
@Getter
@Setter
@Table(name = "point_operation")
public class PointOperation {
@Column(nullable = false, updatable = false)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "point_id", foreignKey = @ForeignKey(name = "FK_point_point_operation"), nullable = false)
private Point point;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "window_id", foreignKey = @ForeignKey(name = "FK_point_w"))
private Window window;
}
I try to save my object window in my service, in transaction:
@Transactional
public myService() {
//window object is not persisted - its just a POJO mapped from windowDto (with id = null)
pointsModifier.modifyPoints(window, windowDto);
Window savedWindow= windowRepository.save(window);
}
and the after transaction I invoke in my controller:
return mapper.map(getMyWindow(), ClassOnlyWithWindowId.class);
In pointsModifier
method I just modify points from window:
@RequiredArgsConstructor
@Service
public class PointsModifier {
private final StoreRepository warehouseRepository;
private final Mapper mapper;
public void modifyPoints(Window window, WindowWriteDto windowWriteDto) {
warehouseRepository.findById(windowWriteDto.getStore().getId())
.ifPresent(windowStore -> {
for (Point point : window.getPath().getPoints()) {
point.setPath(window.getPath());
setPointPathAddress(point, windowWriteDto, windowStore, window);
}
});
}
private void setPointPathAddress(Point point, WindowWriteDto windowDto, Store windowStore, Window window) {
windowDto.getPath().getPoints().stream()
.filter(s -> s.getOrder().equals(point.getOrder()))
.findFirst()
.ifPresent(pointDto -> {
if (pointDto.getStoreId() != null && pointDto.getStoreId().equals(windowStore.getId())) {
point.setPathAddress(mapper.map(windowStore, PathAddress.class));
} else {
point.setPathAddress(mapper.map(pointDto.getPathAddress(), PathAddress.class));
}
setOperations(pointDto, point, window);
});
}
private void setOperations(PointWriteDto pointDto, Point point, Window window) {
for (PointOperation pointOperation : point.getOperations()) {
pointOperation.setPoint(point);
pointDto.getOperations().stream()
.filter(op -> op.getOrder().equals(pointOperation.getOrder()))
.findFirst()
.ifPresent(pointOperationDto -> {
if (pointOperationDto.isInside()) {
pointOperation.setWindow(window);
}
});
}
}
What is wrong with my code? Why I can't save my object with nested objects in one shot?