Skip to content
Advertisement

Spring JPA/Hibernate – OneToMany bi-directional problem

I’ve encountered this problem that won’t let me sleep

I have 2 entities Property and Tenant

each property can have 0..1 tenant, each tenant can have 1..N properties

// some annotations
public class Tenant {

  // more fields

  @OneToMany(mappedBy = "tenant", cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
  private Set<Property> properties = new HashSet<>();

}

// some annotations
public class Property {

  // more fields

  @ManyToOne(fetch = FetchType.LAZY)
  private Tenant tenant;

}

what am I trying to accomplish is when I create a new tenant, using cascading I want to attach it to an existing property

saving is done via JpaRepository.save(Tenant)

for example first I create new Property

{
  // more fields
  "tenant": null
}

then sometime later I decide I want to create a tenant for this property

{
  // more fields
  "properties": [
    {
      "id": 1, // property id I want to create this tenant for
      "tenant": null
    }  
  ]
}

I believe I’ve tried every cascade type (except ALL and PERSIST, these 2 can’t be used with detached entities)

How would it be possible to synchronize a newly created tenant with an already existing property?

I read about this that CascadeType.MERGE should do the trick but it does not for me

Any help is appreciated Thanks in advance

edit: all getters and setters are generated by lombok

rest resource for saving

  @PostMapping
  public ResponseEntity<Void> saveTenant(
      @Valid @RequestBody TenantDto tenantDto, UriComponentsBuilder uriComponentsBuilder) {
    log.info(tenantDto.toString());
    Long id = tenantService.saveTenant(tenantDto);
    URI location = uriComponentsBuilder.path("/api/tenants/{id}").buildAndExpand(id).toUri();
    return ResponseEntity.created(location).build();
  }

service

  @Override
  public Long saveTenant(TenantDto tenantDto) {
    // mapstruct mapping
    return tenantRepository.save(tenantMapper.toEntity(tenantDto)).getId();
  }

repo

@Repository
public interface TenantRepository extends JpaRepository<Tenant, Long> {}

Advertisement

Answer

Cascading is when you have 2 entities, and you want both of them to be UPDATEed / INSERTed / whatever even though you did something only to one of them explicitly. Your question doesn’t really relate to cascading – because you want to INSERT one entity, but UPDATE another one.

Now, you put a mappedBy on the Tenant.properties. This means that Property will be the owning entity – it’s responsible for saving the connection between these 2 entities.

It means that you need to retrieve Property from DB and set Tenant to it. Then when saving Property you’ll get your Foreign Key filled.

Advertisement