J'ai une méthode de référentiel Spring Data avec une requête native
@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);
et je voudrais mapper le résultat à POJO non-entité GroupDetails
.
Est-ce possible et si oui, pourriez-vous donner un exemple?
GroupDetails
marqué avec@Entity
? Si possible, pouvez-vous indiquer sur quelle classe l'annotation@NamedNativeQuery
doit être appliquée?@SqlResultSetMapping
et les@NamedNativeQuery
annotations doivent être présentes sur l'entité utilisée dans votre référentiel Spring Data (par exemple, carpublic interface CustomRepository extends CrudRepository<CustomEntity, Long>
c'est laCustomEntity
classe)Je pense que le moyen le plus simple de le faire est d'utiliser ce qu'on appelle la projection. Il peut mapper les résultats de la requête aux interfaces. L'utilisation
SqlResultSetMapping
est peu pratique et rend votre code moche :).Un exemple tiré du code source 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(); } }
Vous pouvez également utiliser cette méthode pour obtenir une liste de projections.
Consultez cette entrée de documentation JPA Spring Data pour plus d'informations sur les projections.
Note 1:
N'oubliez pas que votre
User
entité est définie comme normale - les champs de l'interface projetée doivent correspondre aux champs de cette entité. Sinon, le mappage de champ peut être interrompu (getFirstname()
peut renvoyer la valeur du nom de famille et cetera).Note 2:
Si vous utilisez la
SELECT table.column ...
notation, définissez toujours des alias correspondant aux noms de l'entité. Par exemple, ce code ne fonctionnera pas correctement (la projection renverra des valeurs nulles pour chaque getter):@Query(value = "SELECT user.firstname, user.lastname FROM SD_User user WHERE id = ?1", nativeQuery = true) NameOnly findByNativeQuery(Integer id);
Mais cela fonctionne 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 cas de requêtes plus complexes, je préfère utiliser
JdbcTemplate
un référentiel personnalisé à la place.la source
JdbcTemplate
( docs.spring.io/spring-framework/docs/current/javadoc-api/org/… ) à la place. Vous pouvez utiliser lagetClob
méthode surresultSet
pour récupérer clobInputStream
. Pour un exemple:rs.getClob("xml_column").getCharacterStream()
.Je pense que l'approche de Michal est meilleure. Mais, il existe un autre moyen d'obtenir le résultat de la requête native.
@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);
Maintenant, vous pouvez convertir ce tableau de chaînes 2D en l'entité souhaitée.
la source
Vous pouvez écrire votre requête native ou non native comme vous le souhaitez, et vous pouvez encapsuler les résultats de requête JPQL avec des instances de classes de résultats personnalisées. Créez un DTO avec les mêmes noms de colonnes renvoyées dans la requête et créez un constructeur d'arguments avec la même séquence et les mêmes noms que ceux renvoyés par la requête. Ensuite, utilisez la méthode suivante pour interroger la base de données.
@Query("SELECT NEW example.CountryAndCapital(c.name, c.capital.name) FROM Country AS c")
Créer DTO:
package example; public class CountryAndCapital { public String countryName; public String capitalName; public CountryAndCapital(String countryName, String capitalName) { this.countryName = countryName; this.capitalName = capitalName; } }
la source
Vous pouvez faire quelque chose comme
@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)")
Et il doit y avoir un constructeur comme
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; }
la source
Dans mon ordinateur, je reçois ce code fonctionne, c'est un peu différent de la réponse de Daimon.
la source