Я думаю, что это может быть тот случай, когда я знаю ответ, но мне просто не нравится.
Моя отправная точка была более ранним вопросом, Hibernate @ Entity конфликт со Spring @Autowired для не -column object .
У меня есть @Entity, который «естественно» связан в отношении «один ко многим» с другим набором сущностей. В моем примере я называю это ItemEntity, и у него (очень) большая история цен. Настолько большой, что ленивая нагрузка Hibernate снижает производительность, потому что реальные случаи использования никогда не требуют всей истории (сотни тысяч цен против нескольких сотен, которые обычно необходимы). Таким образом, у меня есть служба PriceCache, которая получает то, что мне нужно по требованию.
«Естественная» вещь из обычного варианта использования - это получение интересующего элемента ItemEntity, а затем запросить связанную историю цен в течение некоторого времени. временной диапазон. Как указывалось в вышеприведенном посте, использование этого сервиса внутри ItemEntity не является нормальным, хотя это можно сделать и заставить работать.
В приведенном ниже примере кода я написал это по-другому, имеющий интерфейс Item с реализацией, которая фактически является прокси для ItemEntity плюс служба PriceCache. Это пример кода, и биты отсутствуют; Я думаю (надеюсь), что достаточно подарка, чтобы быть ясным.
Мой набор сущностей и их свойства не настолько велики, что я не мог бы сделать это вручную для всех них; пара дюжин сущностей, каждая с 5-20 свойствами. Это было бы умеренно больно и скучно, но это должно сработать.
Но ... есть ли более простой способ создания прокси-объекта с добавленной дополнительной службой? Или, может быть, вопрос в том, есть ли более ленивый способ сделать это?
@Entity @Table(name="item")
public class ItemEntity {
@Id @Column(name="id")
private long id;
@Column(name="name")
private String name;
/* ... setters, getters ... */
}
@Service
public class ItemCache {
@Autowired
private ItemDAO itemDAO;
@Autowired
private PriceCache priceCache;
private Map<Long,Item> itemCache;
public ItemCache() {
itemCache = new HashMap<>();
}
public Item get(long id) {
if (itemCache.containsKey(id))
return itemCache.get(id);
ItemEntity itemEntity = itemDAO.find(id);
Item item = (itemEntity == null) ? null : new ItemImpl(itemEntity, priceCache);
itemCache.put(id, item); // caches nulls to avoid retry
return item;
}
}
@Service
public class PriceCache {
@Autowired
private PriceDAO priceDAO;
/* ... various cache/map structures to hold previous query results ... */
public PriceCache() {
/* ... initialize all those cache/map structures ... */
}
public Collection<Price> getPrices(long id, LocalDateTime begTime, LocalDateTime endTime) {
Collection<Price> results;
/* ... check the caches to see if we already have the data ... */
/* ... otherwise, use priceDAO to find it and save the results in the cache ... */
return results;
}
}
public interface Item {
public long getId();
public String getName();
public Collection<Price> getPrices(LocalDateTime begTime, LocalDateTime endTime);
}
public class ItemImpl implements Item {
public ItemImpl(ItemEntity underlying, PriceCache priceCache) { ... }
public long getId() {
return underlying.getId();
}
public String getName() {
return underlying.getName();
}
public Collection<Price> getPrices(LocalDateTime begTime, LocalDateTime endTime) {
priceCache.get(getId(), begTime, endTime);
}
}