JPA : How to deal with one to many association

After reading and experiencing lot of solutions for modeling a one to many association, I'll show you the ways you can REALLY do it:

1. Using bidirectional association

the parent entity :

@Entity
@Table(name = "MEASURING_DEVICE")
@NamedEntityGraph(
name = "measuringDeviceWithUncertainties",
attributeNodes = {
@NamedAttributeNode("uncertainties")})
@NamedQuery(name = MeasuringDevice.FIND_DEVICE_BY_ID,
query = "SELECT distinct m FROM MeasuringDevice m "
+ "LEFT JOIN FETCH m.uncertainties "
+ "WHERE m.identification = :identification")
@Data
@NoArgsConstructor
public class MeasuringDevice implements Serializable {
public static final String FIND_DEVICE_BY_ID = "MeasuringDevice.findDeviceById";
@Id
private String identification;
private String designation;
@Column(name = "control_date")
@Temporal(TemporalType.TIMESTAMP)
private Date controlDate;
@Column(name = "periodicity_value")
private Integer periodicityValue;
@Column(name = "periodicity_unit")
@Enumerated(EnumType.STRING)
private PeriodicityType periodicityUnit;
private Boolean active = true;
@OneToMany(mappedBy = "device", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<MeasuringUncertainty> uncertainties = new HashSet<>();
public void addUncertainty(MeasuringUncertainty uncertainty) {
//remove old if any
uncertainties.remove(uncertainty);
uncertainties.add(uncertainty);
uncertainty.setDevice(this);
}
}
view raw parent_assoc hosted with ❤ by GitHub

As you can see, I use the @oneToMany annotation with the following properties :
  • mappedBy --> parent property of the child
  • cascade --> ALL by default
  • orphanRemoval --> true by default
  • fetch --> we use LAZY for performance reason
I use a Set for the collection. I provide also a helper method for adding a child to the parent (You can do the same for removing a child).

The child entity below implements also the equals and hashCode methods:

@Entity
@Table(name = "MEASURING_UNCERTAINTY")
@Data
@NoArgsConstructor
public class MeasuringUncertainty implements Serializable {
@Id
private String identification;
@Enumerated(EnumType.STRING)
private Mode mode;
@Enumerated(EnumType.STRING)
private MeasuringUnit unit;
@Column(name = "uncertainty_value")
private BigDecimal uncertaintyValue;
@Column(name = "uncertainty_unit")
@Enumerated(EnumType.STRING)
private UncertaintyUnit uncertaintyUnit;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="MEASURING_DEVICE_ID")
private MeasuringDevice device;
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(this.identification)
.toHashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (getClass() != obj.getClass()) {
return false;
}
MeasuringUncertainty object = (MeasuringUncertainty) obj;
return new EqualsBuilder()
.append(this.identification, object.identification)
.isEquals();
}
@Override
public String toString() {
return new ReflectionToStringBuilder(this).toString();
}
}


In order to retrieve the parent and the children with ONE query, we have 2 solutions:
  1. @NamedEntityGraph : this annotation tell JPA which node to load automatically. 
  2. @NamedQuery using LEFT JOIN FETCH
These methods are both used in the below repository:
@Repository
public interface MeasuringDeviceRepository extends JpaRepository<MeasuringDevice, String> {
//using @namedQuery
Optional<MeasuringDevice> findDeviceById(@Param("identification") String identification);
@EntityGraph("measuringDeviceWithUncertainties")
Optional<MeasuringDevice> findByIdentification(String identification);
}

2. @ManyToOne ONLY in the child entity 

The only think you need to do is to create a JPQL query for retrieving the child entities associated with the parent.

SELECT m FROM MeasuringUncertainty m WHERE m.device.identification = :identification

This solution is far better for me because I can also paginate, filter and sort the results.

Happy learning!!

Comments

Popular posts from this blog

Spring JPA : Using Specification with Projection

Chip input using Reactive Form