Bidirectional relation without cascadeType doesn't fetch sub-entities

Viewed 35

I have a problem with loading a collection of related entites with @OneToMany and @ManyToOne mapping with no cascadeType set.

FYI, we use Lombok.

Base entity - a parent class of all our entities:

@SuperBuilder
@Getter
@Setter
@NoArgsConstructor
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {

  @Id
  @GeneratedValue
  @Column(columnDefinition = "uuid", updatable = false)
  private UUID id;

  @Version @Builder.Default private long version = 1;
}

An entity on "one" side:

@SuperBuilder
@Getter
@Setter
@ToString(includeFieldNames = true)
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@Entity
@Table(name = "companies")
public class Company extends BaseEntity {

  @ToString.Exclude
  @OneToMany(mappedBy = "company")
  private List<OperatingFacility> operatingFacilities;

}

An entity on "many" side:

@SuperBuilder
@Getter
@Setter
@ToString(includeFieldNames = true)
@NoArgsConstructor
@Entity
@Table(name = "operating_facilities")
public class OperatingFacility extends BaseEntity {

  @ToString.Exclude
  @ManyToOne()
  @JoinColumn(name = "company_id")
  private Company company;

}

Here is my test case:

    Company company = companyRepository.save(Company.builder().build());

    OperatingFacility operatingFacility =
        operatingFacilityRepository.save(
            OperatingFacility.builder()
                .company(company)
                .build());

    final List<OperatingFacility> gotFoundByCompanyId =
        operatingFacilityRepository.findByCompanyId(company.getId());

    // SUCCESS At this point a link looks OK
    Assertions.assertEquals(1, gotFoundByCompanyId.size());

    Optional<Company> companyOptional = companyRepository.findById(company.getId());
    Assertions.assertTrue(companyOptional.isPresent());
    Assertions.assertNotNull(companyOptional.get().getOperatingFacilities());
    
    // FAIL When gathering operating facilities from Company, then there are 0
    Assertions.assertEquals(1, companyOptional.get().getOperatingFacilities().size());

When I search for OperatingFacilities by company id, then it works just fine. But when I want to get them using a fetched Company then a collection is empty (not null).

I've checked similar questions e.g. Can someone explain mappedBy in JPA and Hibernate? , but I do thing exactly as other working solutions.

One point what is different is there is no cascadeType set in Company. I want to handle OperatingFacility items using a dedicated OperatingFacilityRepository.

1 Answers

This is a bidirectional relationship that is controlled by the OperatingFacility.company reference - it is common for users to only set this one side and think JPA will automatically fix the other side for you; this is not the case. If you add it to one side, you must maintain both sides to keep the model in synch with the database as they are POJOs that may be cached. If you don't add operatingFacility to the company's list yourself, it will only get there if you force a refresh/reload of the entity from the database using em.refresh(). A read in a cleared context (em.clear()) might also work if there isn't a second level cache.

Related