I have a parent entity:
@Data
@Entity
@Table(uniqueConstraints = {
@UniqueConstraint(columnNames = "email")
})
public class PCUser implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@NotBlank
private String firstName;
private String lastName;
@NotBlank
@Email
private String email;
private String companyName = null;
@OneToMany(mappedBy = "pcUser", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Circuit> circuits = new ArrayList<>();
public void addCircuit(Circuit circuit) {
circuit.setPCUser(this);
circuits.add(circuit);
}
}
And a child entity:
@Data
@Entity
@Table(uniqueConstraints={
@UniqueConstraint(columnNames = {"userID", "name"}),
@UniqueConstraint(columnNames = "circuitID")
})
public class Circuit implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@NotBlank
private String circuitID = CommonsUtil.randomAlphanumeric(10);
@NotBlank
private String name;
@Embedded
private NetworkEndpoint networkEndpoint;
@NotNull
private Boolean active;
@NotNull
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "userID", nullable = false)
private PCUser pcUser;
}
I try to create a new Circuit by using the addCircuit method of PCUser in a service class:
public void createCircuit(PCUser pcUser, Circuit circuit) {
validateCircuit(circuit);
pcUser.addCircuit(circuit);
pcUserRepository.save(pcUser);
logCircuitCreation(circuit);
}
But I get the following error:
Caused by: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.pcmap.commons.models.Circuit
Most suggestions for this problem is to use the Cascade type on the mapping so all child entities are saved beforehand when the parent is updated. But here I am already using it with CascadeType.ALL so what is going wrong?
EDIT: Some more details showing createCircuit method is called from a REST controller.
@PostMapping(produces = "application/json")
public ResponseEntity<Object> createCircuit(@Valid @RequestBody CircuitCreateRequest circuitCreateRequest,
Principal principal) {
circuitCreateRequest.clean();
String username = principal.getName();
PCUser pcUser = proxyCircuitUserRepository.findByAuth0UserID(username);
if (pcUser == null)
throw new PCUserNotFoundException();
Circuit circuit = new Circuit();
circuit.setName(circuitCreateRequest.getName());
circuit.setRegionSlug(circuitCreateRequest.getRegionSlug());
circuit.setCircuitType(ValuesUtil.getCircuitType(circuitCreateRequest.getCircuitType()));
circuit.setPassword(circuitCreateRequest.getPassword());
circuitManagerService.createCircuit(pcUser, circuit);
CircuitIDResponse circuitIDResponse = new CircuitIDResponse();
circuitIDResponse.setCircuitID(circuit.getCircuitID());
return new ResponseEntity<>(circuitIDResponse, HttpStatus.CREATED);
}
And here are the methods validateCircuit and checkCircuitLimit called from createCircuit
private void validateCircuit(Circuit circuit) {
if (circuit.getName() == null)
circuit.setName(circuit.getCircuitID());
if (circuit.getPassword() == null)
circuit.setPassword(circuit.getCircuitID());
circuit.setActive(true);
NetworkEndpoint networkEndpoint = new NetworkEndpoint();
networkEndpoint.setHostname(getHostname());
networkEndpoint.setPort(junctionProperties.getPort());
circuit.setNetworkEndpoint(networkEndpoint);
if (circuit.getName() == null)
throw new InvalidOperationException("Invalid Circuit name");
if (circuitRepository.findByNameIgnoreCaseAndPCUser(circuit.getName(),
circuit.getPCUser()) != null)
throw new InvalidOperationException("Circuit with same name already exists");
if (circuit.getPassword() == null)
throw new InvalidOperationException("Invalid username/password");
if (!validateRegionSlug(circuit.getRegionSlug()))
throw new InvalidOperationException("Invalid regionSlug");
if (circuit.getCircuitType() == null)
throw new InvalidOperationException("Invalid CircuitType");
}
private void checkCircuitLimit(PCUser pcUser) {
if (pcUser.getCircuits().size() > pcUser.getMaxCircuits())
throw new InvalidOperationException("Circuit max limit reached");
}