public class Product {
@Id
private String id;
@Column(name = "name", nullable = false)
private String name;
@ElementCollection(fetch = FetchType.LAZY) //
@CollectionTable(name = "product_price", joinColumns = @JoinColumn(name = "product_id"))
private List<Money> prices = Lists.newArrayList();
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "product_plan", joinColumns = @JoinColumn(name = "product_id"))
@Column(name = "plan_type_id")
private Set<String> planTypeIds = Sets.newHashSet();
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "product_city_pair", joinColumns = @JoinColumn(name = "product_id"))
private Set<CityPair> cityPairs = Sets.newHashSet();
}
- EAGER => load product with SQL join, so you'll get many lines with duplicate id, name
- LAZY => load product first, then using separate sql to load data
- Why
Set<CityPair> works? CityPair overwrites hashCode() and equals(), JPA/Hibernate actually populates duplicate records, while Java Set ensures DISTINCT
- Why
List<Money> does not work if using EAGER fetch?
- we expect the price is unique per currency
List<Money> cannot handle "remove duplicate", thus we'll get duplicate elements after loading data
- we use Property annotation rather than accessor annotation, we cannot manually handle it
- we can fix it by using
LAZY (easiest will extra SQL execution)
- use accessor annotation, and manually solve duplicate in setter
- but we cannot distinguish loading data usecase and object modification usecase, so we cannot do input validation only for object modification usecase, e.g. throw IllegalArgumentException if duplicate currency found.
- maybe we can use
default setter for loading data usecase, and use customised method replacePrices(List<Money>) for object modification usecase.
- or
@PostLoad and EntityManager (@PostLoad does not work for Hibernate if using SessionFactory)
- or write hibernate interceptor
- or use
Map<Currency, Money> (logically it should work, not verified)