Estoy usando JPA en mi proyecto.
Llegué a una consulta en la que necesito hacer una operación de unión en cinco tablas. Entonces creé una consulta nativa que devuelve cinco campos.
Ahora quiero convertir el objeto de resultado a la clase de Java POJO que contiene las mismas cinco cadenas.
¿Hay alguna forma en JPA para transmitir directamente ese resultado a la lista de objetos POJO?
Llegué a la siguiente solución ...
@NamedNativeQueries({
@NamedNativeQuery(
name = "nativeSQL",
query = "SELECT * FROM Actors",
resultClass = db.Actor.class),
@NamedNativeQuery(
name = "nativeSQL2",
query = "SELECT COUNT(*) FROM Actors",
resultClass = XXXXX) // <--------------- problem
})
Ahora aquí en resultClass, ¿necesitamos proporcionar una clase que sea una entidad JPA real? O ¿Podemos convertirlo a cualquier clase de JAVA POJO que contenga los mismos nombres de columna?
Respuestas:
JPA proporciona un
SqlResultSetMapping
que le permite asignar lo que devuelve su consulta nativa a una entidado una clase personalizada.EDITAR JPA 1.0 no permite la asignación a clases que no sean de entidad. Solo en JPA 2.1 se ha agregado un ConstructorResult para asignar valores de retorno a una clase java.
Además, para el problema de OP con el recuento, debería ser suficiente para definir una asignación de conjunto de resultados con un solo
ColumnResult
fuente
ConstructorResult
como uno de los parámetrosSqlResultSetMapping
que permiten usar un pojo con todos los campos establecidos en el constructor. Actualizaré la respuesta.He encontrado un par de soluciones para esto.
Uso de entidades mapeadas (JPA 2.0)
Con JPA 2.0 no es posible asignar una consulta nativa a un POJO, solo se puede hacer con una entidad.
Por ejemplo:
Pero en este caso,
Jedi
debe ser una clase de entidad asignada.Una alternativa para evitar la advertencia no verificada aquí sería utilizar una consulta nativa con nombre. Entonces, si declaramos la consulta nativa en una entidad
Entonces, simplemente podemos hacer:
Esto es más seguro, pero todavía estamos restringidos a usar una entidad asignada.
Mapeo manual
Una solución que experimenté un poco (antes de la llegada de JPA 2.1) fue hacer un mapeo contra un constructor POJO usando un poco de reflexión.
Básicamente, este método toma una matriz de tuplas (como la devuelven las consultas nativas) y la asigna a una clase POJO proporcionada buscando un constructor que tenga el mismo número de campos y del mismo tipo.
Entonces podemos usar métodos convenientes como:
Y simplemente podemos usar esta técnica de la siguiente manera:
JPA 2.1 con @SqlResultSetMapping
Con la llegada de JPA 2.1, podemos usar la anotación @SqlResultSetMapping para resolver el problema.
Necesitamos declarar una asignación de conjunto de resultados en algún lugar de una entidad:
Y luego simplemente hacemos:
Por supuesto, en este caso
Jedi
no necesita ser una entidad mapeada. Puede ser un POJO regular.Usar mapeo XML
Soy uno de los que encuentran que agregar todo esto es
@SqlResultSetMapping
bastante invasivo en mis entidades, y particularmente no me gusta la definición de consultas con nombre dentro de las entidades, por lo que alternativamente hago todo esto en elMETA-INF/orm.xml
archivo:Y esas son todas las soluciones que conozco. Los dos últimos son la forma ideal si podemos usar JPA 2.1.
fuente
@SqlResultSetMapping
debe colocarse en una entidad porque de eso JPA leerá los metadatos. No puede esperar que JPA inspeccione sus POJO. La entidad en la que coloca el mapeo es irrelevante, tal vez la que está más relacionada con sus resultados POJO. Alternativamente, la asignación podría expresarse en XML para evitar el acoplamiento con una entidad totalmente no relacionada.@SqlResultSetMapping
él, vale la pena señalar que laJedi
clase requerirá un constructor todo arg y la@ColumnResult
anotación puede necesitar que setype
agregue el atributo a las conversiones que podrían no ser implícitas (necesitaba agregartype = ZonedDateTime.class
algunas columnas).Sí, con JPA 2.1 es fácil. Tienes anotaciones muy útiles. Simplifican tu vida.
Primero declare su consulta nativa, luego su asignación de conjunto de resultados (que define la asignación de los datos devueltos por la base de datos a sus POJO). Escriba su clase de POJO para referirse (no se incluye aquí por brevedad). Por último, pero no menos importante: cree un método en un DAO (por ejemplo) para llamar a la consulta. Esto funcionó para mí en una aplicación dropwizard (1.0.0).
Primero declare una consulta nativa en una clase de entidad:
Debajo puede agregar la declaración de mapeo del conjunto de resultados:
Más adelante en un DAO puede referirse a la consulta como
Eso es.
fuente
Si lo usa
Spring-jpa
, este es un complemento de las respuestas y esta pregunta. Corrija esto si hay fallas. Principalmente he usado tres métodos para lograr el "resultado de mapeoObject[]
a un pojo" basado en la necesidad práctica que cumplo:sql
con suEntity
es suficienteLos primeros 2 fallaron, y tengo que usar a
nativeQuery
. Aquí están los ejemplos. El pojo esperaba:Método 1 : cambie el pojo a una interfaz:
Y repositorio:
Método 2 : repositorio:
Nota: la secuencia de parámetros del constructor POJO debe ser idéntica tanto en la definición POJO como en sql.
Método 3 : Uso
@SqlResultSetMapping
y@NamedNativeQuery
en laEntity
que el ejemplo de la respuesta de Edwin Dalorzo.Los primeros dos métodos llamarían a muchos manejadores intermedios, como convertidores personalizados. Por ejemplo,
AntiStealing
define asecretKey
, antes de que persista, se inserta un convertidor para encriptarlo. Esto daría como resultado que los primeros 2 métodos devuelvan una conversión convertidasecretKey
que no es lo que quiero. Si bien el método 3 superaría el convertidor, y se devolveríasecretKey
sería el mismo que se almacena (uno cifrado).fuente
El procedimiento de desenvoltura se puede realizar para asignar resultados a entidades que no sean entidades (que es Beans / POJO). El procedimiento es el siguiente.
El uso es para la implementación JPA-Hibernate.
fuente
JobDTO
debe tener un constructor predeterminado. O puede implementar su propio transformador basado en laAliasToBeanResultTransformer
implementación.Primero declare las siguientes anotaciones:
Luego anote su POJO de la siguiente manera:
Luego escriba el procesador de anotaciones:
Use el marco anterior de la siguiente manera:
fuente
BeanUtils
?La forma más fácil es usar las proyecciones . Puede asignar resultados de consultas directamente a las interfaces y es más fácil de implementar que usar SqlResultSetMapping.
A continuación se muestra un ejemplo:
Los campos de la interfaz proyectada deben coincidir con los campos de esta entidad. De lo contrario, la asignación de campo podría romperse.
Además, si usa la
SELECT table.column
notación, siempre defina alias que coincidan con los nombres de la entidad, como se muestra en el ejemplo.fuente
En hibernación, puede usar este código para asignar fácilmente su consulta nativa.
fuente
Usando Hibernate:
fuente
Estilo antiguo con ResultSet
fuente
Como otros ya han mencionado todas las soluciones posibles, estoy compartiendo mi solución alternativa.
En mi situación con
Postgres 9.4
, mientras trabajo conJackson
,Estoy seguro de que puede encontrar lo mismo para otras bases de datos.
También para su información, resultados de consultas nativas JPA 2.0 como mapa
fuente
No estoy seguro de si esto encaja aquí, pero tuve una pregunta similar y encontré la siguiente solución / ejemplo simple para mí:
En mi caso, tuve que usar las partes SQL definidas en cadenas en otro lugar, por lo que no pude usar NamedNativeQuery.
fuente
Estilo antiguo con Resultset
fuente
Hemos resuelto el problema de la siguiente manera:
fuente
Vea el siguiente ejemplo para usar un POJO como pseudoentidad para recuperar el resultado de una consulta nativa sin usar SqlResultSetMapping complejo. Solo necesito dos anotaciones, una @Enity desnuda y una @Id ficticia en tu POJO. @Id se puede usar en cualquier campo de su elección, un campo @Id puede tener claves duplicadas pero no valores nulos.
Como @Enity no se asigna a ninguna tabla física, este POJO se denomina pseudoentidad.
Entorno: eclipselink 2.5.0-RC1, jpa-2.1.0, mysql-connector-java-5.1.14
Puedes descargar el proyecto completo de Maven aquí
La consulta nativa se basa en ejemplos de empleados de MySQL db http://dev.mysql.com/doc/employee/en/employees-installation.html
persistence.xml
Employee.java
EmployeeNativeQuery.java
fuente
list
es, supuestamente, una lista deEmployee
, ¿por qué su ciclo for-each itera sobre un tipoObject
? Si escribe su ciclo for-each comofor(Employee emp : list)
entonces, descubriría que su respuesta es incorrecta y que el contenido de su lista no son empleados y que esa advertencia que suprimió tenía el propósito de alertarlo sobre este posible error.List<Employee> list = (List<Employee>) query.getResultList();
Cambiarfor (Object emp : list)
afor (Employee emp : list)
es mejor, pero no hay errores si se mantieneObject emp
ya que la lista es una instancia deList<Employee>
. Cambié el código en el proyecto git pero no aquí para mantener tu comentario relevante a la publicación originalQuery query = em.createNativeQuery("select * ...", Employee.class);
y persistence.xml, la consulta nativa devuelve una lista de Empleado. Acabo de salir y ejecutar el proyecto sin problema. Si configura los empleados de muestra de MySQL DB localmente, también debería poder ejecutar el proyectoEmployee
lo que supongo que es una entidad. ¿No es así?si está usando Spring, puede usar
org.springframework.jdbc.core.RowMapper
Aquí hay un ejemplo:
fuente
Usando Hibernate:
fuente
Manera simple de convertir la consulta SQL a la colección de clase POJO,
fuente
Todo lo que necesitas es un DTO con un constructor:
y llámalo:
fuente
Uso
DTO Design Pattern
. Fue utilizado enEJB 2.0
. La entidad fue administrada por contenedor.DTO Design Pattern
se utiliza para resolver este problema Pero, podría usarse ahora, cuando la aplicación se desarrolleServer Side
y porClient Side
separado.DTO
se usa cuandoServer side
no quiere pasar / regresarEntity
con anotación aClient Side
.Ejemplo de DTO:
PersonEntity.java
PersonDTO.java
DTOBuilder.java
EntityBuilder.java <- puede ser necesario
fuente