Extender los datos de la entidad JPA en tiempo de ejecución

Necesito permitir que los usuarios cliente extiendan los datos contenidos por una entidad JPA en tiempo de ejecución. En otras palabras, necesito agregar una columna virtual a la tabla de entidades en tiempo de ejecución. Esta columna virtual solo será aplicable a ciertas filas de datos y posiblemente haya bastantes de estas columnas virtuales . Como tal, no quiero crear una columna adicional real en la base de datos, sino que quiero hacer uso de entidades adicionales que representan estas columnas virtuales .

Como ejemplo, considere la siguiente situación. Tengo una entidad de la Compañía que tiene un campo denominado Propietario , que contiene una referencia al propietario de la empresa . En tiempo de ejecución, un usuario cliente decide que todas las empresas que pertenecen a un propietario específico deben tener el campo adicional etiquetado como detalles de contacto .

Mi diseño preliminar utiliza dos entidades adicionales para lograr esto. El primero básicamente representa la columna virtual y contiene información tal como el nombre del campo y el tipo de valor esperado. El otro representa los datos reales y conecta una fila de entidad a una columna virtual . Por ejemplo, la primera entidad puede contener los datos “Detalles de contacto”, mientras que la segunda entidad contiene, por ejemplo, “555-5555”.

¿Es esta la manera correcta de hacer esto? ¿Hay una mejor alternativa? Además, ¿cuál sería la forma más fácil de cargar automáticamente estos datos cuando se carga la entidad original? Quiero que mi llamada DAO devuelva la entidad junto con sus extensiones .

EDITAR: Cambié el ejemplo de un campo etiquetado Tipo que podría ser un Socio o un Cliente a la versión actual, ya que era confuso.

Quizás una alternativa más simple podría ser agregar una columna CLOB a cada empresa y almacenar las extensiones como un XML. Aquí hay un conjunto diferente de compensaciones en comparación con su solución, pero mientras los datos adicionales no necesiten ser accesibles por SQL (sin índices, códigos de barras, etc.) probablemente será más simple que lo que hace ahora.

También significa que si tiene una lógica sofisticada con respecto a los datos adicionales, necesitará implementarla de manera diferente. Por ejemplo, si necesita una lista de todos los tipos de extensiones posibles, deberá mantenerla por separado. O si necesita capacidades de búsqueda (encuentre al cliente por número de teléfono) necesitará una solución similar o lucene.

Puedo elaborar más si te interesa.

EDITAR:

Para habilitar la búsqueda, querrá algo como lucene, que es un gran motor para realizar búsquedas de texto libre en datos arbitrarios. También está la búsqueda de hibernación que integra lucene directamente con hibernación usando anotaciones y cosas similares. No lo he usado pero escuché cosas buenas sobre él.

Para buscar / escribir / acceder a datos, básicamente se trata de XML, por lo que debe aplicarse cualquier técnica de XML. El mejor enfoque realmente depende del contenido real y de cómo se usará. Sugeriría buscar en XPath el acceso a los datos, y tal vez considerar la definición de su propio tipo de uso de hibernación para que todo el acceso esté encapsulado en una clase y no simplemente en cadena.

El ejemplo con Compañía, Socio y Cliente es en realidad una buena aplicación para el polymorphism que se admite mediante la herencia con JPA: tendrá una de las siguientes 3 estrategias para elegir: tabla única, tabla por clase y unida. Su descripción suena más como una estrategia unida pero no necesariamente.

También puede considerar una relación uno a uno (o cero) en su lugar. Entonces necesitarás tener tal relación para cada valor de tu columna virtual ya que sus valores representan diferentes entidades. Por lo tanto, tendrá una relación con la entidad socia y otra relación con la entidad del cliente y cualquiera, ambos o ninguno puede ser nulo.

Me he encontrado con más problemas de los que esperaba y, como tal, decidí simplificar los requisitos para mi primera iteración. Actualmente estoy tratando de permitir dichas extensiones solo en toda la entidad de la compañía , en otras palabras, estoy retirando todo el requisito de propietario . Entonces, el problema podría reformularse como “¿Cómo puedo agregar columnas virtuales (entradas en otra entidad que actúan como una columna adicional) a una entidad en tiempo de ejecución?”

Mi implementación actual es la siguiente (partes irrelevantes filtradas):

@Entity class Company { // The set of Extension definitions, for example "Location" @Transient public Set getExtensions { .. } // The actual entry, for example "Atlanta" @OneToMany(fetch = FetchType.EAGER) @JoinColumn(name = "companyId") public Set 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() { .. } } 

La implementación tal como está me permite cargar una entidad de compañía e Hibernate asegurará que todas sus entradas de extensión también estén cargadas y que pueda acceder a las extensiones correspondientes a esas entradas de extensión . En otras palabras, si quisiera, por ejemplo, mostrar esta información adicional en una página web, podría acceder a toda la información requerida de la siguiente manera:

 Company company = findCompany(); for (ExtensionEntry extensionEntry : company.getExtensionEntries()) { String label = extensionEntry.getExtension().getLabel(); String value = extensionEntry.getValueAsString(); } 

Sin embargo, hay varios problemas con esto. En primer lugar, al usar FetchType.EAGER con @OneToMany, Hibernate usa una combinación externa y, como tal, devolverá Empresas duplicadas (una para cada ExtensionEntry). Esto se puede resolver usando Criteria.DISTINCT_ROOT_ENTITY, pero a su vez causará errores en mi paginación y, como tal, es una respuesta inaceptable. La alternativa es cambiar el FetchType a LAZY, pero eso significa que siempre tendré que cargar ExtensionEntries “manualmente”. Por lo que yo entiendo, si, por ejemplo, cargué una Lista de 100 compañías , tendría que repetir el ciclo y consultar cada una de ellas, generando 100 declaraciones SQL que no son aceptables para el rendimiento.

El otro problema que tengo es que, idealmente, me gustaría cargar todas las extensiones cada vez que se carga una empresa . Con eso quiero decir que me gustaría que @Transient getter named getExtensions () devuelva todas las extensiones para cualquier compañía . El problema aquí es que no existe una relación de clave externa entre la Empresa y la Extensión , ya que la Extensión no se aplica a ninguna instancia de la Compañía , sino a todas ellas. Actualmente puedo pasar eso con el código que presento a continuación, pero esto no funcionará cuando acceda a entidades referenciadas (si, por ejemplo, tengo una entidad Empleado que tiene una referencia a la Compañía , la Compañía que recupero a través de employee.getCompany () ganó no tienen las extensiones cargadas):

 List companies = findAllCompanies(); List extensions = findAllExtensions(); for (Company company : companies) { // Extensions are the same for all Companies, but I need them client side company.setExtensions(extensions); } 

Así que eso es en lo que estoy actualmente, y no tengo idea de cómo proceder para superar estos problemas. Estoy pensando que todo mi diseño podría tener fallas, pero no estoy seguro de cómo intentarlo.

¡Todas y cada una de las ideas y sugerencias son bienvenidas!

Usa el decorador de patrones y oculta tu entidad dentro de decoratorClass bye

El uso del patrón de EAV es una mala opción en mi humilde opinión, debido a problemas de rendimiento y problemas con la presentación de informes (muchas uniones). Buscando la solución He encontrado algo más aquí: http://www.infoq.com/articles/hibernate-custom-fields