Avoid Bi-directional Relationships ☝️
In bi-directional relations we have to remember to set both ends of the relation to reference each other whenever we want to persist one of them.
If we don't one of the two ends will have a null reference to the other and we would mess up our data 😿
Moreover it's very uncommon for us to traverse relationships both ways.
Let's look at an example 👇
Say we have Order and OrderLine entities that have a one-to-many relationship.
Let’s do it the bi-directional way 🚫
@Entity
@Table(name = "ORDERS")
@Data
public class Order{
@Id
@GeneratedValue
Integer id;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
List<OrderLine> orderLines = new ArrayList<>();
}
@Entity
@Data
public class OrderLine
{
@Id
@GeneratedValue
Integer id;
@ManyToOne
Order order;
}
So, to make sure we have both ends set up we need to
Order order = new Order();
OrderLine orderLine = new OrderLine();
orderLine.setOrder(order);
order.getOrderLines().add(orderLine);
entityManager.persist(order);
And now let’s do it the unidirectional way ✅
@Entity
@Table(name = "ORDERS")
@Data
public class Order{
@Id
@GeneratedValue
Integer id;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "ORDER_ID")
List<OrderLine> orderLines = new ArrayList<>();
}
@Entity
@Data
public class OrderLine
{
@Id
@GeneratedValue
Integer id;
}
Doing the following should persist both entities.
Order order = new Order();
OrderLine orderLine = new OrderLine();
order.getOrderLines().add(orderLine);
entityManager.persist(order);
Limit Relationships Via Numeric IDs ☝🏾
For entities that have different contexts, represent the relationship between them using Ids and not actual classes.
Say we have an OrderLine entity and a Product entity.
@Entity
@Data
public class OrderLine
{
@Id
@GeneratedValue
Integer id;
@ManyToOne
Product product;
}
@Entity
@NoArgsConstructor
@AllArgsConstructor
public class Product
{
@Id
@GeneratedValue
Integer id;
}
In this case if we fetch an OrderLine object then the Product will be fetched eagerly as well because the default behavior of ManyToOne
is an EAGER
fetch 🏃🏽♂️
Plus BDD principals says we should keep the Product and OrderLine entities separate from each other because they belong to different contexts.
The best way is to include Product in OrderLine as an ID.
Example time 🏋🏽♂️
@Entity
@Data
public class OrderLine
{
@Id
@GeneratedValue
Integer id;
Integer productId;
}
And the code to set the product ID would be like:
Product product = new Product();
entityManager.persist(product);
Order order = new Order();
OrderLine orderLine = new OrderLine();
order.getOrderLines().add(orderLine);
orderLine.setProductId(product.getId());
entityManager.persist(order);
Credits 🙌🏾
This post was made mainly derived from Victor Rentea's Designing Expressive and Performant Persistence Models for Relational DBs.
Top comments (0)