I ran into bigger problems than I had hoped for, and therefore decided to abandon the requirements for my first iteration. Currently, I am trying to allow such extensions only for the entire company, in other words, I am rejecting all the requirements of the Owner. Therefore, the problem can be rephrased as "How to add virtual columns (records in another object that act as an additional column) for the object at runtime?"
My current implementation is as follows: filtered abstract parts:
@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() { .. } }
The as implementation allows me to load the Company object, and Hibernate ensures that all its ExtensionEntries are also loaded and that I can access the extensions corresponding to these ExtensionEntries. In other words, if I wanted, for example, to display this additional information on a web page, I could access all the necessary information as follows:
Company company = findCompany(); for (ExtensionEntry extensionEntry : company.getExtensionEntries()) { String label = extensionEntry.getExtension().getLabel(); String value = extensionEntry.getValueAsString(); }
However, there are many problems associated with this. First, when using FetchType.EAGER with @OneToMany, Hibernate uses an external join and as such will return duplicate companies (one for each ExtensionEntry). This can be resolved using Criteria.DISTINCT_ROOT_ENTITY, but this, in turn, will cause errors in my pagination and, as such, is an unacceptable answer. An alternative is to change FetchType to LAZY, but that means that I will always “manually” download ExtensionEntries. As far as I understand, if, for example, I downloaded a list of 100 companies, I would have to iterate and query each of them, generating 100 SQL statements, which is unacceptable for performance.
Another problem that I encountered is that, ideally, I would like to download all Extensions whenever a company boots up. I mean, I would like this @Transient getter named getExtensions () to return all extensions for any company. The problem here is that between the company and the extension there is no connection between the foreign key, since the extension does not apply to any instance of the company, but to all of them. Currently, I can pass this code as shown below, but this will not work when referring to reference objects (if, for example, I have an Employee employee who has a link to a company, a company that I get through employee.getCompany (), won "Not loaded extensions":
List<Company> companies = findAllCompanies(); List<Extension> extensions = findAllExtensions(); for (Company company : companies) {
So, I was at the moment, and I have no idea how to act to overcome these problems. I think my whole design may be wrong, but I'm not sure how else to try to get closer to it.
Any ideas and suggestions are welcome!