1

I am creating a Spring JPA (PostgresSQL & H2) service that deals with offices. I can have a stand-alone office, or an parent office (corporate) with children (remote) offices. Offices have an address and a list of employees (who can belong to more than one office). I am struggling with the design and would really appreciate any help you can offer.

My tables are currently in H2 for testing/dev so I don't have any referential integrity on the tables.
This is making me wonder if my model couldn't be improved. Is there a better way to do this sort of recursive/nested relationship?

UPDATE Based on the excellent feedback from jfneis, I now have this approach:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "OfficeType")
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "officeId")
public abstract class Office {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "officeId", updatable = false, nullable = false)
    private long officeId;

    @Column
    private String name;
    @Column
    private String ein;
    @Column
    @Temporal(TemporalType.DATE)
    private Date startDate;
    @Column
    @Temporal(TemporalType.DATE)
    private Date endDate;

    @OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "addressId", nullable = false)
    private Address address;

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @Fetch(value = FetchMode.SUBSELECT)
    private List<Employee> employees = new ArrayList<>();
}

and the corporate office:

@Entity(name = "CorporateOffice")
@DiscriminatorValue("Corporate")
public class CorporateOffice extends Office {

    @Column
    private String url;
}

and remote office:

@Entity(name = "RemoteOffice")
@DiscriminatorValue("Remote")
public class RemoteOffice extends Office {

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "corporateOfficeId")
    private Office corporateOffice;
}

If I understand correctly, both office types will be stored in the "office" table with a column indicating which type it is.

On startup I am seeding some test data so I can further see what is going on:

@Component
@Transactional
public class SeedOffices {

@Autowired
private OfficeRepository officeRepository;

@PostConstruct
public void seedOffices() {
    CorporateOffice corporate = new CorporateOffice();
    corporate.setName("World Domination Corp");
    corporate.setUrl("www.corporationsownstheworld.com");
    corporate.setStartDate(new Date());
    Address corpAddr = new Address();
    corpAddr.setAddressLine1("corp line 1");
    corpAddr.setCity("Denver");
    corporate.setEin("123456789");
    corporate.setAddress(corpAddr);
    officeRepository.save(corporate);

    RemoteOffice office1 = new RemoteOffice();
    office1.setStartDate(new Date());
    Address remote1Addr = new Address();
    remote1Addr.setAddressLine1("remote 1 line 1");
    remote1Addr.setCity("Cheyanne");
    office1.setAddress(remote1Addr);
    officeRepository.save(office1);
    office1.setCorporateOffice(corporate);

    RemoteOffice office2 = new RemoteOffice();
    Address remote2Addr = new Address();
    remote2Addr.setAddressLine1("remote 2 line 1");
    remote2Addr.setCity("Calumet");
    office2.setAddress(remote2Addr);
    officeRepository.save(office2);
    office2.setCorporateOffice(corporate);
}

}

1 Answer 1

1

This is a design question with subjective answers (which is not the kind of answer SO is supposed to answer).

Anyway: I don't like the single Office class. You have only one class for 2 different behaviors: a Corporate office (which CAN have remote offices) and a Remote office (that CAN have a parent office).

Keeping references in both points is up to your business requirements, but my suggestion is: have an abstract class Office, with common attributes, and 2 children classes: CorporateOffice and RemoteOffice, each of them dealing with the desired attributes.

You can map both in the same table using Single Table strategy. For a very good article about JPA subclassing strategies, take a look at this article.


Edit: answering your 2nd question about RemoteOffice mapping:

I believe your RemoteOffice mapping is wrong. You declared corporateOffice as a @OneToMany, but it's actually a @ManyToOne relationship, isn't it? Please test @ManyToOne + @JoinColumn in this field to check if solves your problem.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for the advice and direction. I have updated my original question if you dont mind taking a look
@sonoerin edited my question with the new answer. please give it a try.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.