Hibernate Cascade unable to persist Child entities and fails with Transient instance error

Viewed 183

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");
}
0 Answers
Related