Suponiendo GroupDetails como en la respuesta de orid, ¿ha probado JPA 2.1 @ConstructorResult ?
@SqlResultSetMapping(
name="groupDetailsMapping",
classes={
@ConstructorResult(
targetClass=GroupDetails.class,
columns={
@ColumnResult(name="GROUP_ID"),
@ColumnResult(name="USER_ID")
}
)
}
)
@NamedNativeQuery(name="getGroupDetails", query="SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", resultSetMapping="groupDetailsMapping")
y use lo siguiente en la interfaz del repositorio:
GroupDetails getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);
De acuerdo con la primavera de datos JPA documentación , primavera en primer lugar tratar de encontrar consulta con nombre que coincidan con su nombre de método - por lo que mediante el uso @NamedNativeQuery
, @SqlResultSetMapping
y @ConstructorResult
que debe ser capaz de lograr que el comportamiento
GroupDetails
marcar con@Entity
? Si es posible, ¿podría indicar en qué clase@NamedNativeQuery
se debe aplicar la anotación ?@SqlResultSetMapping
y las@NamedNativeQuery
anotaciones deben estar presentes en la entidad utilizada en su repositorio de Spring Data (por ejemplo, porquepublic interface CustomRepository extends CrudRepository<CustomEntity, Long>
es laCustomEntity
clase)Creo que la forma más sencilla de hacerlo es utilizar la llamada proyección. Puede asignar resultados de consultas a interfaces. Usar
SqlResultSetMapping
es incómodo y hace que su código sea feo :).Un ejemplo directamente del código fuente JPA de Spring Data:
public interface UserRepository extends JpaRepository<User, Integer> { @Query(value = "SELECT firstname, lastname FROM SD_User WHERE id = ?1", nativeQuery = true) NameOnly findByNativeQuery(Integer id); public static interface NameOnly { String getFirstname(); String getLastname(); } }
También puede utilizar este método para obtener una lista de proyecciones.
Consulte esta entrada de documentos JPA de datos de primavera para obtener más información sobre las proyecciones.
Nota 1:
Recuerde tener su
User
entidad definida como normal: los campos de la interfaz proyectada deben coincidir con los campos de esta entidad. De lo contrario, la asignación de campos podría estar rota (getFirstname()
podría devolver el valor del apellido, etc.).Nota 2:
Si usa
SELECT table.column ...
notación, defina siempre los alias que coincidan con los nombres de la entidad. Por ejemplo, este código no funcionará correctamente (la proyección devolverá nulos para cada captador):@Query(value = "SELECT user.firstname, user.lastname FROM SD_User user WHERE id = ?1", nativeQuery = true) NameOnly findByNativeQuery(Integer id);
Pero esto funciona bien:
@Query(value = "SELECT user.firstname AS firstname, user.lastname AS lastname FROM SD_User user WHERE id = ?1", nativeQuery = true) NameOnly findByNativeQuery(Integer id);
En el caso de consultas más complejas, prefiero usar
JdbcTemplate
un repositorio personalizado.fuente
JdbcTemplate
( docs.spring.io/spring-framework/docs/current/javadoc-api/org/… ) en su lugar. Puede utilizar elgetClob
métodoresultSet
para obtener clobInputStream
. Para un ejemplo:rs.getClob("xml_column").getCharacterStream()
.Creo que el enfoque de Michal es mejor. Pero hay una forma más de obtener el resultado de la consulta nativa.
@Query(value = "SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = :userId WHERE g.group_id = :groupId", nativeQuery = true) String[][] getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);
Ahora, puede convertir esta matriz de cadenas 2D en su entidad deseada.
fuente
Puede escribir su consulta nativa o no nativa de la forma que desee, y puede ajustar los resultados de la consulta JPQL con instancias de clases de resultados personalizadas. Cree un DTO con los mismos nombres de columnas devueltos en la consulta y cree un constructor de todos los argumentos con la misma secuencia y nombres que devuelve la consulta. Luego use la siguiente forma para consultar la base de datos.
@Query("SELECT NEW example.CountryAndCapital(c.name, c.capital.name) FROM Country AS c")
Crear DTO:
package example; public class CountryAndCapital { public String countryName; public String capitalName; public CountryAndCapital(String countryName, String capitalName) { this.countryName = countryName; this.capitalName = capitalName; } }
fuente
Puedes hacer algo como
@NamedQuery(name="IssueDescriptor.findByIssueDescriptorId" , query=" select new com.test.live.dto.IssuesDto (idc.id, dep.department, iss.issueName, cat.issueCategory, idc.issueDescriptor, idc.description) from Department dep inner join dep.issues iss inner join iss.category cat inner join cat.issueDescriptor idc where idc.id in(?1)")
Y debe haber un constructor como
public IssuesDto(long id, String department, String issueName, String issueCategory, String issueDescriptor, String description) { super(); this.id = id; this.department = department; this.issueName = issueName; this.issueCategory = issueCategory; this.issueDescriptor = issueDescriptor; this.description = description; }
fuente
En mi computadora, este código funciona, es un poco diferente de la respuesta de Daimon.
fuente