最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

java - "Detached entity passed to persist" immediately after saving - Stack Overflow

programmeradmin1浏览0评论

I have the following method which is throwing a .springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.domain.Passenger on the vehicleRepository.saveAll call. I don't understand why this is happening since the Passenger is persisted just a few lines before, and therefore shouldn't it be an attached entity still? I'm especially confused because the Property save and passing that into Passenger works just fine.

On the Vehicle entity, the relationship with Passenger is:

@OneToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "passenger_id", updatable = false)
@JsonManagedReference(value = "vehicle-passenger")
private Passenger passenger;

And here is the unit test set up method:

@DataJpaTest
@Transactional(propagation = NOT_SUPPORTED)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class Tests {

    @BeforeAll
    void setup() {

        Vehicle veh1 = build.fromFile(Vehicle.class, "json/Vehicle");
        Vehicle veh2 = build.fromFile(Vehicle.class, "json/Vehicle");
        Vehicle veh3 = build.fromFile(Vehicle.class, "json/Vehicle");
        Vehicle veh4 = build.fromFile(Vehicle.class, "json/Vehicle");

        // persist a property to use
        Property property = build.fromFile(Property.class, "json/Property");
        property.setPropertyId(1L);
        propertyRepository.save(property);

        Passenger passenger1 = build.fromFile(Passenger.class, "json/Passenger");
        Passenger passenger2 = build.fromFile(Passenger.class, "json/Passenger");

        // add test properties to passengers
        passenger1.setProperty(property);
        passenger2.setProperty(property);
        passenger1 = passengerRepository.save(passenger1);
        passenger2 = passengerRepository.save(passenger2);

        // add test passengers to vehicles
        veh1.setPassenger(passenger1);
        veh2.setPassenger(passenger2);

        vehicleRepository.saveAll(List.of(
            veh1,
            veh2,
            veh3,
            veh4
        ));
    }

.....
@Data
@Entity
@DiscriminatorValue("VEHICLE")
@FieldNameConstants
@SuperBuilder
@RequiredArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Vehicle extends Conveyance {
   .. removed unrelated properties for brevity ...
}
@Data
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "CONVEYANCE_TYPE", discriminatorType = DiscriminatorType.STRING)
@FieldNameConstants
@SuperBuilder
@RequiredArgsConstructor
@AllArgsConstructor
public abstract class Conveyance {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "conveyance_id_seq")
    @SequenceGenerator(name = "conveyance_id_seq", allocationSize = 1)
    @Setter(AccessLevel.NONE)
    private Long id;

    @OneToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name = "passenger_id", updatable = false)
    @JsonManagedReference(value = "conveyance-passenger")
    private Passenger passenger;
}

@Data
@Entity
@FieldNameConstants
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Passenger {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "passenger_id_seq")
    @SequenceGenerator(name = "passenger_id_seq", allocationSize = 1)
    @Setter(AccessLevel.NONE)
    private Long id;

    @Column(
        unique = true,
        updatable = false,
        columnDefinition = "bigint GENERATED ALWAYS AS IDENTITY (START WITH 10)"
    )
    @Generated(event = EventType.INSERT)
    private Long passengerId;

    @ManyToOne
    @JoinColumn(name = "property_id")
    @JsonBackReference
    private Property property;

    @OneToOne(cascade = CascadeType.PERSIST, mappedBy = Conveyance.Fields.passenger)
    @EqualsAndHashCode.Exclude
    @JsonBackReference(value = "conveyance-passenger")
    private Conveyance conveyance;
}
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@FieldNameConstants
public class Property {

    public Property(Long id) {
        this.id = id;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "property_id_seq")
    @SequenceGenerator(name = "property_id_seq", allocationSize = 1)
    @Setter(AccessLevel.NONE)
    private Long id;

    @Column(unique = true)
    private Long propertyId;
}

I have the following method which is throwing a .springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.domain.Passenger on the vehicleRepository.saveAll call. I don't understand why this is happening since the Passenger is persisted just a few lines before, and therefore shouldn't it be an attached entity still? I'm especially confused because the Property save and passing that into Passenger works just fine.

On the Vehicle entity, the relationship with Passenger is:

@OneToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "passenger_id", updatable = false)
@JsonManagedReference(value = "vehicle-passenger")
private Passenger passenger;

And here is the unit test set up method:

@DataJpaTest
@Transactional(propagation = NOT_SUPPORTED)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class Tests {

    @BeforeAll
    void setup() {

        Vehicle veh1 = build.fromFile(Vehicle.class, "json/Vehicle");
        Vehicle veh2 = build.fromFile(Vehicle.class, "json/Vehicle");
        Vehicle veh3 = build.fromFile(Vehicle.class, "json/Vehicle");
        Vehicle veh4 = build.fromFile(Vehicle.class, "json/Vehicle");

        // persist a property to use
        Property property = build.fromFile(Property.class, "json/Property");
        property.setPropertyId(1L);
        propertyRepository.save(property);

        Passenger passenger1 = build.fromFile(Passenger.class, "json/Passenger");
        Passenger passenger2 = build.fromFile(Passenger.class, "json/Passenger");

        // add test properties to passengers
        passenger1.setProperty(property);
        passenger2.setProperty(property);
        passenger1 = passengerRepository.save(passenger1);
        passenger2 = passengerRepository.save(passenger2);

        // add test passengers to vehicles
        veh1.setPassenger(passenger1);
        veh2.setPassenger(passenger2);

        vehicleRepository.saveAll(List.of(
            veh1,
            veh2,
            veh3,
            veh4
        ));
    }

.....
@Data
@Entity
@DiscriminatorValue("VEHICLE")
@FieldNameConstants
@SuperBuilder
@RequiredArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Vehicle extends Conveyance {
   .. removed unrelated properties for brevity ...
}
@Data
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "CONVEYANCE_TYPE", discriminatorType = DiscriminatorType.STRING)
@FieldNameConstants
@SuperBuilder
@RequiredArgsConstructor
@AllArgsConstructor
public abstract class Conveyance {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "conveyance_id_seq")
    @SequenceGenerator(name = "conveyance_id_seq", allocationSize = 1)
    @Setter(AccessLevel.NONE)
    private Long id;

    @OneToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name = "passenger_id", updatable = false)
    @JsonManagedReference(value = "conveyance-passenger")
    private Passenger passenger;
}

@Data
@Entity
@FieldNameConstants
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Passenger {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "passenger_id_seq")
    @SequenceGenerator(name = "passenger_id_seq", allocationSize = 1)
    @Setter(AccessLevel.NONE)
    private Long id;

    @Column(
        unique = true,
        updatable = false,
        columnDefinition = "bigint GENERATED ALWAYS AS IDENTITY (START WITH 10)"
    )
    @Generated(event = EventType.INSERT)
    private Long passengerId;

    @ManyToOne
    @JoinColumn(name = "property_id")
    @JsonBackReference
    private Property property;

    @OneToOne(cascade = CascadeType.PERSIST, mappedBy = Conveyance.Fields.passenger)
    @EqualsAndHashCode.Exclude
    @JsonBackReference(value = "conveyance-passenger")
    private Conveyance conveyance;
}
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@FieldNameConstants
public class Property {

    public Property(Long id) {
        this.id = id;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "property_id_seq")
    @SequenceGenerator(name = "property_id_seq", allocationSize = 1)
    @Setter(AccessLevel.NONE)
    private Long id;

    @Column(unique = true)
    private Long propertyId;
}
Share Improve this question edited Feb 4 at 14:47 Goulash asked Feb 4 at 13:38 GoulashGoulash 3,8487 gold badges32 silver badges49 bronze badges 5
  • Try running the same code in a @Test. Does it pass? – Lesiak Commented Feb 4 at 14:16
  • @Lesiak no, it fails with the same exception at the same place – Goulash Commented Feb 4 at 14:26
  • Ah, you specified Propagation.NOT_SUPPORTED. Thus, even in case of @Test your method executes non-transactionally - passengers are indeed detached. – Lesiak Commented Feb 4 at 14:30
  • Please post Passenger entity and especially property to compare why it passes in that case. – Lesiak Commented Feb 4 at 14:38
  • I have added all of the related entities now – Goulash Commented Feb 4 at 14:47
Add a comment  | 

1 Answer 1

Reset to default 1

You are executing your method without an active transaction. There are 2 reasons for that:

  • you explicitly disabled transactions with @Transactional(propagation = NOT_SUPPORTED)
  • even if this was not the case, transactions are not activated in a @BeforeAll method. TransactionalTestExecutionListener activates them only in @Test methods. See How does @Transactional work on test methods?

After saving a passanger, it is in a detached state. You specified to cascade persist of passanger when you persist a Vehicle: @OneToOne(cascade = CascadeType.PERSIST)

Thus, you are persisting a detached entity - which causes the exception.

The difference between Passenger.property is that there is no cascade in this case - when persisting the Passenger the persist operation is not cascaded to property.

See Overview of JPA/Hibernate Cascade Types

发布评论

评论列表(0)

  1. 暂无评论