У меня возникло больше проблем, чем я надеялся, и поэтому я решил не указывать требования для своей первой итерации. В настоящее время я пытаюсь разрешить такие Расширения только для всей сущности Company , другими словами, я отбрасываю все требование Владелец . Таким образом, проблему можно перефразировать как «Как я могу добавить виртуальные столбцы (записи в другом объекте, которые действуют как дополнительный столбец) к объекту во время выполнения?"
Моя текущая реализация выглядит следующим образом (несущественные части отфильтрованы):
@Entity
class Company {
// The set of Extension definitions, for example "Location"
@Transient
public Set<Extension> getExtensions { .. }
// The actual entry, for example "Atlanta"
@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "companyId")
public Set<ExtensionEntry> getExtensionEntries { .. }
}
@Entity
class Extension {
public String getLabel() { .. }
public ValueType getValueType() { .. } // String, Boolean, Date, etc.
}
@Entity
class ExtensionEntry {
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "extensionId")
public Extension getExtension() { .. }
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "companyId", insertable = false, updatable = false)
public Company getCompany() { .. }
public String getValueAsString() { .. }
}
Реализация в том виде, в каком она есть, позволяет загрузить сущность Company , и Hibernate гарантирует, что все ее ExtensionEntries также загружены и что я могу получить доступ к Extensions соответствующие этим ExtensionEntries . Другими словами, если бы я хотел, например, отобразить эту дополнительную информацию на веб-странице, я мог бы получить доступ ко всей необходимой информации следующим образом:
Company company = findCompany();
for (ExtensionEntry extensionEntry : company.getExtensionEntries()) {
String label = extensionEntry.getExtension().getLabel();
String value = extensionEntry.getValueAsString();
}
Однако есть ряд проблем с этим. Во-первых, при использовании FetchType.EAGER с @OneToMany Hibernate использует внешнее объединение и, как таковое, будет возвращать дубликаты компаний (по одной для каждого ExtensionEntry). Эту проблему можно решить с помощью Criteria.DISTINCT_ROOT_ENTITY, но это, в свою очередь, приведет к ошибкам в моей нумерации страниц и, следовательно, является неприемлемым ответом. Альтернативой является изменение FetchType на LAZY, но это означает, что мне всегда придется «вручную» загружать ExtensionEntries . Насколько я понимаю, если бы, например, я загрузил список 100 компаний , мне пришлось бы зацикливаться и запрашивать каждый из них, генерируя 100 операторов SQL, что не приемлемо для производительности. мудрый.
Другая проблема, с которой я столкнулся, заключается в том, что в идеале я хотел бы загружать все Extensions всякий раз, когда загружается Company . Имея это в виду, я хотел бы, чтобы получатель @Transient с именем getExtensions () возвращал все расширения для любой компании . Проблема здесь заключается в том, что не существует отношения внешнего ключа между Company и Extension , поскольку Extension не применимо ни к одной Company экземпляр, а точнее для всех них. В настоящее время я могу преодолеть это с помощью кода, подобного представленному ниже, но это не будет работать при доступе к ссылочным объектам (если, например, у меня есть объект Employee , который имеет ссылку на Company , Company , которую я получаю через employee.getCompany (), не будет загружен Extensions :
List<Company> companies = findAllCompanies();
List<Extension> extensions = findAllExtensions();
for (Company company : companies) {
// Extensions are the same for all Companies, but I need them client side
company.setExtensions(extensions);
}
Так вот, где я сейчас нахожусь, и я понятия не имею, как поступить, чтобы обойти эти проблемы. Я думаю, что весь мой дизайн может быть ошибочным, но я не уверен, как еще попытаться приблизиться к нему.
Любые идеи и предложения приветствуются!