Extending JPA entity data at run time

I need to allow client users to extend the data contained in the JPA object at runtime. In other words, I need to add a virtual column to the entity table at runtime. This virtual column applies only to some rows of data, and there may possibly be quite a few such virtual columns. Therefore, I do not want to create the actual additional column in the database, but I want to use additional entities that represent these virtual columns.

As an example, consider the following situation. I have a company that has a box labeled "Owner" that contains a link to the Company Owner. At run time, the client user decides that all Companies belonging to a particular Owner should have an additional field labeled ContactDetails.

My preliminary project uses two additional objects to accomplish this. The first basically represents a virtual column and contains information such as the name of the field and the type of expected value. The other represents the actual data and connects the entity row to the virtual column. For example, the first object may contain "ContactDetails" data, while the second object contains the word "555-5555".

Is this the right way to do this? Is there a better alternative? Also, what would be the easiest way to automatically load this data when loading the original object? I want my DAO call to return an object along with its extensions.

EDIT: I changed the example from the field labeled “Type”, which could be a Partner or Client in the current version, as that was confusing.

+4
source share
5 answers

Perhaps a simpler alternative would be to add a CLOB column for each company and save the extensions as XML. There is a different set of tradeoffs here compared to your solution, but for now, additional data should not be accessible by SQL (without indexes, fkeys, etc.), It will probably be simpler than what you are doing now.

It also means that if you have some kind of bizarre logic regarding additional data that you will need to implement differently. For example, if you need a list of all the possible types of extensions, you will have to maintain it separately. Or, if you need search capabilities (find a customer by phone number), you will need lucene or a similar solution.

I can clarify if you are interested.

EDIT:

To enable search, you need something like lucene , which is a great engine for free text search on arbitrary data. There is also hibernate-search , which integrates lucene directly with hibernate using annotations, etc. - I did not use it, but I heard good things about it.

To get / write / access data, you mostly deal with XML, so any XML method should be applied. The best approach really depends on the actual content and how it will be used. I would suggest looking into XPath for accessing data and possibly exploring the definition of usertype's own hibernation so that all access is encapsulated in a class, not just in String.

+3
source

The Company, Partner, and Client example is actually a good polymorphism application that is supported through inheritance from JPA: you will have one of the following three strategies: one table, a table for each class, and a combined one. Your description is more like a joint strategy, but not necessary.

Instead, you can consider a one-to-one relationship (or "zero"). Then you will need to have such a relationship for each value of your virtual column, since its values ​​represent different objects. Therefore, you will have a relationship with the partner entity and another relationship with the Customer object, and either either both or none may be null.

0
source

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) { // Extensions are the same for all Companies, but I need them client side company.setExtensions(extensions); } 

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!

0
source

Use the picture decorator and hide your object inside decoratorClass bye

0
source

Using the EAV template is the wrong IMHO choice due to performance issues and reporting issues (many join). Digging for a solution. I found something else here: http://www.infoq.com/articles/hibernate-custom-fields

0
source

All Articles