DenEwout
DenEwout

Reputation: 944

How does Hibernate work with @OneToOne and Cascade.ALL? (using Spring)

I have a class Customer that has a OneToOne bidirectional relationship with a Subscription:

@Entity
@Table(name = "customers")
public class Customer{
    @OneToOne(mappedBy="customer",cascade = CascadeType.ALL)
    private Subscription currentSubscription;
}

@Entity
@Table(name = "subscriptions")
public class Subscription {

    @Id
    @Column(columnDefinition = "INT8",name="id", unique=true, nullable=false)
    @GeneratedValue(generator="gen")
    @GenericGenerator(name="gen", strategy="foreign", parameters=@Parameter(name="property", value="customer"))
    private Long id;

    @OneToOne
    @PrimaryKeyJoinColumn
    private Customer customer;
}

Now, when I create a customer with a subscription and call persist on the customer, it nicely saves the subscription as well into the database. However when I have already persisted a customer, and want to add a subscription, it fails with the following error:

Caused by: org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.qmino.miredot.portal.domain.Subscription.customer]

I've written a test in order to explain what I want to achieve:

@Test
public void shouldCascadeUpdateSubscription(){
    Customer owner = customerRepository.save(CustomerMother.getCustomer(false));

    Subscription subscription = SubscriptionBuilder.create()
            .setBillingDayOfMonth(LocalDate.now().getDayOfMonth())
            .setSubscriptionPlan(subscriptionPlan)
            .build();

    subscription.setCustomer(owner);
    owner.setCurrentSubscription(subscription);

    customerRepository.save(owner);

    Customer result = customerRepository.findOne(owner.getId());
    assertThat(result.getCurrentSubscription(),is(notNullValue()));
    assertThat(result.getCurrentSubscription().getId(),is(result.getId()));
}

Where did I go wrong?

Upvotes: 0

Views: 3168

Answers (1)

Jordi Castilla
Jordi Castilla

Reputation: 26961

Cascade here is not the problem, Cascade indicates the action to be done by entity when deleted or updated. What is correct if you want to save complete entity. But for that, you need to have the correct data, your message suggest it tries to update the Customer entity but it founds an empty AccountDetails, so in order to correctly fetch the other entities, you need to add FecthType.EAGER, to get all attributes of mapped entities.

@OneToOne(mappedBy="customer",cascade = CascadeType.ALL, fetch = FetchType.EAGER))

Upvotes: 2

Related Questions