2

I have four Entities , an Gorilla, a Health Check, Transmitter and a Microchip - I have a question about wiring these entities together.

Gorillas have Health Checks 3-4 times a year so they are OneToMany. Each Gorilla normally has 1 microchip during the course of there life. Microchips are inserted during health checks, but usually only one unless one has fallen out (rare). So I make this OneToMany also on Gorilla to Microchip. Each Gorilla has one Transmitter at a time, but they are replaced every year. So also OneToMany.

I have setup my entities like this:

GORILLA

@Data
public class Gorilla {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;
    @Column(unique = true)
    @NotEmpty(message = "Name must not be null or empty")
    private String name;
   
    @JsonIgnore
    @OneToMany (cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.MERGE, CascadeType.REFRESH}, orphanRemoval = true, mappedBy = "gorilla")
    private List<Microschip> listMicroschip = new ArrayList<>();

    @JsonIgnore
    @OneToMany (cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.MERGE, CascadeType.REFRESH}, orphanRemoval = true, mappedBy = "gorilla")
    private List<Transmitter> listTransmitter = new ArrayList<>();

    @JsonIgnore
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "gorilla")
    private List<HealthCheck> listHealthCheck = new ArrayList<>();

MICROCHIP

@Data
public class Microchip {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;
    @Column(unique = true)
    @NotEmpty(message = "Name must not be null or empty")
    private String code;
    private LocalDate dateInserted;
    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    private Gorilla gorilla;

HEALTHCHECK

@Data
public class HealthCheck {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private Gorilla gorilla;
    // other stuff

When viewing the gorilla entity as a list or a single i normally get the Gorilla with Name, current transmitter, current microchip and sometimes a list of summary fields for the health check.

When viewing a HealthCheck want to retrieve it with Gorilla details, any microchips or transmitters that were attached during that HealthCheck.

The problem with the current design with the Microchips and Transmitters having a relationship with the Gorilla is it makes it easy to get a Gorilla with its current (most recent) transmitter and (most recent) microchip.

This design however makes it hard to get a healthcheck and see which microchip or transmitter was attached on that healthcheck (if any were).

My code for getting the newest microchip for example is:

   default void helperMapping(Gorilla source, GorillaDTO target) {
        if (!source.getListTransmitter().isEmpty()) {
            Transmitter trans = (source.getListTransmitter()
                    .stream()
                    .max(Comparator.comparing(Transmitter::getDateAttached)).get());
            target.setChannel(trans.getChannel());
            target.setChannelOffset(trans.getChannelOffset());
        }

How can I setup the ORM so that:

  1. I don't have to stream over the list of Gorilla's transmitters/microchips each time I want the current one - seems very in-efficent.
  2. I can retrieve a list of healthchecks with any microchips or transmitters relevant to that healthcheck
  3. Less common get a list of all transmitters/microchips for a given Gorilla

(PS: I also tried microchip/transmitter sitting under HealthCheck but they made it even hard to get current microchip/transmitter)

UPDATE

I am considering having a two relationships: Gorilla (One) to Transmitter (Many) HealthCheck (One) to Transmitter (One and Optional)

This way I could easily find the newest Transmitter for any Gorilla (newest in list) and I can see which health check a transmitter was created in.

Thoughts?

Radika Moonesinghe
  • 361
  • 2
  • 6
  • 17
  • I would have the relationships owned the one side and be uni-directional - so that e.g. `Microchip` owns the relationship and the `Gorilla` doesn’t care. I would then remove the lists from `Gorilla` and add [derived fields](https://stackoverflow.com/a/29004311/2071828) to get the current microchip/transmitter. I don’t think `Gorilla` should have anything about the `Healthcheck` - it makes little object modelling sense for the `Gorilla` to know that. – Boris the Spider Apr 18 '22 at 08:37
  • Why remove the list from Gorilla? When viewing a Gorilla I want to see a list of healthchecks. – Radika Moonesinghe Apr 18 '22 at 08:42
  • Do _you_ know all your health checks? Their dates and details? No! Of course you don’t. You presumably log into a website to get the details - a “health check service” if you will… – Boris the Spider Apr 18 '22 at 08:43
  • Can you be more specific? You mean that the HealthChecks should be retrieved separate to a Gorilla (by the HCservice and the DTO constructed? – Radika Moonesinghe Apr 18 '22 at 08:52
  • Chat is here @BoristheSpider https://chat.stackoverflow.com/rooms/info/243985/entity-orm-design?tab=general – Radika Moonesinghe Apr 18 '22 at 09:25

1 Answers1

0

I was misunderstanding. If Transmitter and Microchip are only changed during healthcheck, how about this approach?

Gorilla

@Data
public class Gorilla {
    
    @OneToOne // unidirectional
    private Microchip currentMicroChip

    @OneToOne // unidirectional
    private Transmitter currentTransmitter

    @JsonIgnore
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "gorilla")
    private List<HealthCheck> listHealthCheck = new ArrayList<>();

MICROCHIP

@Data
public class Microchip {
   
    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    private HealthCheck healthCheck;

HEALTHCHECK

@Data
public class HealthCheck {

    @JsonIgnore
    @OneToMany (cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.MERGE, CascadeType.REFRESH}, orphanRemoval = true, mappedBy = "gorilla")
    private List<Microschip> listMicroschip = new ArrayList<>();

    @JsonIgnore
    @OneToMany (cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.MERGE, CascadeType.REFRESH}, orphanRemoval = true, mappedBy = "gorilla")
    private List<Transmitter> listTransmitter = new ArrayList<>();
   
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private Gorilla gorilla;