Eu tenho um método de repositório Spring Data com uma 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)
GroupDetails getGroupDetails(@Param("userId") Integer userId, @Param("groupId") Integer groupId);
e gostaria de mapear o resultado para POJO de não entidade GroupDetails
.
É possível e, em caso afirmativo, poderia dar um exemplo?
GroupDetails
marcado com@Entity
? Se possível, você pode dizer em qual classe a anotação@NamedNativeQuery
deve ser aplicada?@SqlResultSetMapping
e as@NamedNativeQuery
anotações devem estar presentes na entidade usada em seu repositório Spring Data (por exemplo, porquepublic interface CustomRepository extends CrudRepository<CustomEntity, Long>
é aCustomEntity
classe)Acho que a maneira mais fácil de fazer isso é usar a chamada projeção. Ele pode mapear os resultados da consulta para interfaces. Usar
SqlResultSetMapping
é inconveniente e torna seu código feio :).Um exemplo direto do código-fonte JPA da 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(); } }
Você também pode usar este método para obter uma lista de projeções.
Verifique esta entrada de documentos JPA de dados de primavera para obter mais informações sobre as projeções.
Nota 1:
Lembre-se de ter sua
User
entidade definida como normal - os campos da interface projetada devem coincidir com os campos desta entidade. Caso contrário, o mapeamento de campo pode ser interrompido (getFirstname()
pode retornar o valor do sobrenome etc.).Nota 2:
Se você usar a
SELECT table.column ...
notação, sempre defina aliases que correspondam aos nomes da entidade. Por exemplo, este código não funcionará corretamente (a projeção retornará nulos para cada getter):@Query(value = "SELECT user.firstname, user.lastname FROM SD_User user WHERE id = ?1", nativeQuery = true) NameOnly findByNativeQuery(Integer id);
Mas isso funciona bem:
@Query(value = "SELECT user.firstname AS firstname, user.lastname AS lastname FROM SD_User user WHERE id = ?1", nativeQuery = true) NameOnly findByNativeQuery(Integer id);
No caso de consultas mais complexas, prefiro usar o
JdbcTemplate
repositório personalizado.fonte
JdbcTemplate
( docs.spring.io/spring-framework/docs/current/javadoc-api/org/… ). Você pode usar ogetClob
método on aresultSet
fim de buscar clobInputStream
. Para um exemplo:rs.getClob("xml_column").getCharacterStream()
.Acho que a abordagem de Michal é melhor. Porém, há mais uma maneira de obter o resultado da 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);
Agora, você pode converter esta matriz de string 2D na entidade desejada.
fonte
Você pode escrever sua consulta nativa ou não nativa da maneira que desejar e pode agrupar os resultados da consulta JPQL com instâncias de classes de resultados personalizadas. Crie um DTO com os mesmos nomes das colunas retornadas na consulta e crie um construtor de todos os argumentos com a mesma sequência e nomes retornados pela consulta. Em seguida, use a seguinte maneira para consultar o banco de dados.
@Query("SELECT NEW example.CountryAndCapital(c.name, c.capital.name) FROM Country AS c")
Criar DTO:
package example; public class CountryAndCapital { public String countryName; public String capitalName; public CountryAndCapital(String countryName, String capitalName) { this.countryName = countryName; this.capitalName = capitalName; } }
fonte
Use o método padrão na interface e obtenha o EntityManager para ter a oportunidade de definir o ResultTransformer, então você pode retornar o POJO puro, assim:
final String sql = "SELECT g.*, gm.* FROM group g LEFT JOIN group_members gm ON g.group_id = gm.group_id and gm.user_id = ? WHERE g.group_id = ?"; default GroupDetails getGroupDetails(Integer userId, Integer groupId) { return BaseRepository.getInstance().uniqueResult(sql, GroupDetails.class, userId, groupId); }
E o BaseRepository.java é assim:
@PersistenceContext public EntityManager em; public <T> T uniqueResult(String sql, Class<T> dto, Object... params) { Session session = em.unwrap(Session.class); NativeQuery q = session.createSQLQuery(sql); if(params!=null){ for(int i=0,len=params.length;i<len;i++){ Object param=params[i]; q.setParameter(i+1, param); } } q.setResultTransformer(Transformers.aliasToBean(dto)); return (T) q.uniqueResult(); }
Esta solução não afeta nenhum outro método no arquivo de interface do repositório.
fonte
Você pode fazer 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)")
E deve haver um construtor 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; }
fonte
No meu computador, eu consigo que esse código funcione. É um pouco diferente da resposta de Daimon.
fonte