Ich versuche, die Ergebnisse einer systemeigenen Abfrage mithilfe von @SqlResultSetMapping mit @ConstructorResult einem POJO zuzuordnen. Hier ist mein Code:
@SqlResultSetMapping(name="foo",
classes = {
@ConstructorResult(
targetClass = Bar.class,
columns = {
@ColumnResult(name = "barId", type = Long.class),
@ColumnResult(name = "barName", type = String.class),
@ColumnResult(name = "barTotal", type = Long.class)
})
})
public class Bar {
private Long barId;
private String barName;
private Long barTotal;
...
Und dann in meinem DAO:
Query query = em.createNativeQueryBar(QUERY, "foo");
... set some parameters ...
List<Bar> list = (List<Bar>) query.getResultList();
Ich habe gelesen, dass diese Funktionalität nur in JPA 2.1 unterstützt wird, aber das ist, was ich verwende. Hier ist meine Abhängigkeit:
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.0.Final</version>
</dependency>
Ich habe ein paar Ressourcen gefunden, einschließlich dieser: @ConstructorResult-Zuordnung in jpa 2.1 . Aber ich habe immer noch kein Glück.
Was vermisse ich? Warum kann das SqlResultSetMapping nicht gefunden werden?
javax.persistence.PersistenceException: org.hibernate.MappingException: Unknown SqlResultSetMapping [foo]
@SqlResultSetMapping
Annotation sollte nicht in einem POJO abgelegt werden. Legen Sie es in (beliebiger) @Entity
-Klasse. "Unknown SqlResultSetMapping [foo]" sagt Ihnen, dass der JPA-Provider keine Zuordnung unter dem Namen "foo" sieht. Bitte sehen Sie eine andere Antwort von mir für das richtige Beispiel
Kurzes Arbeitsbeispiel:
DTO POJO-Klasse
@lombok.Getter
@lombok.AllArgsConstructor
public class StatementDto {
private String authorName;
private Date createTime;
}
Repository Bean:
@Repository
public class StatementNativeRepository {
@PersistenceContext private EntityManager em;
static final String STATEMENT_SQLMAP = "Statement-SQL-Mapping";
public List<StatementDto> findPipelinedStatements() {
Query query = em.createNativeQuery(
"select author_name, create_time from TABLE(SomePipelinedFun('xxx'))",
STATEMENT_SQLMAP);
return query.getResultList();
}
@SqlResultSetMapping(name= STATEMENT_SQLMAP, classes = {
@ConstructorResult(targetClass = StatementDto.class,
columns = {
@ColumnResult(name="author_name",type = String.class),
@ColumnResult(name="create_time",type = Date.class)
}
)
}) @Entity class SQLMappingCfgEntity{@Id int id;} // <- workaround
}
Ich kann es so machen:
Session session = em().unwrap(Session.class);
SQLQuery q = session.createSQLQuery("YOUR SQL HERE");
q.setResultTransformer( Transformers.aliasToBean( MyNotMappedPojoClassHere.class) );
List<MyNotMappedPojoClassHere> postList = q.list();
@Entity
@SqlResultSetMapping(name="ConnexionQueryBean",
entities={
@EntityResult(entityClass=com.collecteJ.business.bean.ConnexionQueryBean.class, fields={
@FieldResult(name="utilisateurId", column="UTILISATEUR_ID"),
@FieldResult(name="nom", column="NOM"),
@FieldResult(name="prenom", column="PRENOM"),
@FieldResult(name="nomConnexion", column="NOM_CONNEXION"),
@FieldResult(name="codeAgence", column="CODE_AGENCE"),
@FieldResult(name="codeBanque", column="CODE_BANQUE"),
@FieldResult(name="codeDevise", column="CODE_DEVISE"),
@FieldResult(name="codeCollecteur", column="CODE_COLLECTEUR")})
})
public class ConnexionQueryBean implements Serializable {
@Id
private long utilisateurId;
private String codeCollecteur;
private String nom;
private String prenom;
private String nomConnexion;
private String codeAgence;
private String codeBanque;
private String codeDevise;
public ConnexionQueryBean() {
}
public long getUtilisateurId() {
return utilisateurId;
}
public void setUtilisateurId(long utilisateurId) {
this.utilisateurId = utilisateurId;
}
public String getCodeCollecteur() {
return codeCollecteur;
}
public void setCodeCollecteur(String codeCollecteur) {
this.codeCollecteur = codeCollecteur;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public String getPrenom() {
return prenom;
}
public void setPrenom(String prenom) {
this.prenom = prenom;
}
public String getNomConnexion() {
return nomConnexion;
}
public void setNomConnexion(String nomConnexion) {
this.nomConnexion = nomConnexion;
}
public String getCodeAgence() {
return codeAgence;
}
public void setCodeAgence(String codeAgence) {
this.codeAgence = codeAgence;
}
public String getCodeBanque() {
return codeBanque;
}
public void setCodeBanque(String codeBanque) {
this.codeBanque = codeBanque;
}
public String getCodeDevise() {
return codeDevise;
}
public void setCodeDevise(String codeDevise) {
this.codeDevise = codeDevise;
}
@Override
public String toString() {
return "ConnexionQueryBean{" + "utilisateurId=" + utilisateurId + ", codeCollecteur=" + codeCollecteur + ", nom=" + nom + ", prenom=" + prenom + ", nomConnexion=" + nomConnexion + ", codeAgence=" + codeAgence + ", codeBanque=" + codeBanque + ", codeDevise=" + codeDevise + '}';
}
Dies ist keine Entität, da sie keiner Datenbanktabelle entspricht. Die Annotationen @Entity
Und @Id
Sind jedoch für JPA obligatorisch, um die Zuordnung zu verstehen. Wenn Sie nicht wirklich möchten, dass @Entity / @Id
In dieser Klasse enthalten ist, können Sie die Anmerkung @SqlResultSetMapping
Entfernen und in eine beliebige andere Entität einfügen, sofern JPA sie scannen kann.
Sie sollten auch sicherstellen, dass Ihr @ComponentScan
Das entsprechende Paket enthält. Wenn Sie eine Java) - basierte Frühjahrskonfiguration verwenden, sollten Sie Ihre Entität explizit im persistence.xml/orm.xml
im Verzeichnis META-INF
.
Dies ist der Aufruf
String connexionQuery = "SELECT u.UTILISATEUR_ID, u.NOM, u.PRENOM, u.NOM_CONNEXION, a.CODE_AGENCE, a.CODE_BANQUE, a.CODE_DEVISE, c.CODE_COLLECTEUR FROM UTILISATEUR u, AGENCE a, COLLECTEUR c "
+ " WHERE (a.CODE_AGENCE = c.CODE_AGENCE AND u.UTILISATEUR_ID = c.UTILISATEUR_ID AND u.NOM_CONNEXION = '"+nomConnextion+"')";
ConnexionQueryBean ConnexionResults = (ConnexionQueryBean) defaultService.getEntityManager().createNativeQuery(connexionQuery,"ConnexionQueryBean").getSingleResult();
System.out.println(ConnexionResults.toString());
Ich verwende Spring, JPA 2.1, Hibernate 5 und Oracle. Ich denke, dass dies mit einer niedrigeren JPA-Version möglicherweise nicht möglich ist. Weitere Informationen finden Sie hier http://www.thoughts-on-Java.org/result-set- Mapping-Komplex-Mappings /
Das Problem beim Hinzufügen von @Entity
Zu Ihrem DTO POJO ist, dass es eine Tabelle in Ihrer Datenbank erstellt, die Sie nicht benötigen. Das Hinzufügen von @Id
Und eines vorübergehenden Schlüsselworts zu den erforderlichen Feldern ist ebenfalls mühsam. Eine einfache Lösung besteht darin, Ihren @SqlResultSetMapping
In eine abstrakte Klasse zu verschieben.
@MappedSuperclass
@SqlResultSetMapping(name="foo",
classes = {
@ConstructorResult(
targetClass = Bar.class,
columns = {
@ColumnResult(name = "barId", type = Long.class),
@ColumnResult(name = "barName", type = String.class),
@ColumnResult(name = "barTotal", type = Long.class)
})
})
public abstract class sqlMappingCode {}
Vergessen Sie nicht, @MappedSuperclass
Hinzuzufügen. Dadurch wird sichergestellt, dass Hibernate Ihre Zuordnung automatisch verkabelt.
Ich habe eine etwas abwechslungsreiche Antwort, die nur aus der Antwort von Wildloop abgeleitet ist.
Hier ist meine Antwort:
Konstantenklasse: Konstanten.Java
public class Constants {
public final String TESTQUERYRESULT_MAPPING_NAME = "TestQueryResultMapping";
}
Klasse für Ergebniszuordnung: TestQueryResult.Java
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.EntityResult;
import javax.persistence.FieldResult;
import javax.persistence.Id;
import javax.persistence.SqlResultSetMapping;
@Getter
@Setter
@SqlResultSetMapping(
//name = "TestQueryResultMapping"
name = Constants.TESTQUERYRESULT_MAPPING_NAME
,entities = @EntityResult(
entityClass = TestQueryResult.class
,fields = {
@FieldResult(name = "rowId", column = "row_id")
,@FieldResult(name = "rowName", column = "row_name")
,@FieldResult(name = "value", column = "row_value")
}
)
)
@Entity
public class TestQueryResult {
@Id
private Integer rowId;
private String rowName;
private String value;
}
Dann ... irgendwo in meinem Repository-Implementierungscode:
public class TestQueryRepository {
//... some code here to get the entity manager here
public TestQueryResult getTopMost(Integer rowName) {
//... some code here
String queryString = "... some query string here" + rowName;
TestQueryResult testQueryResult = null;
//this.entityManager.createNativeQuery(queryString ,"TestQueryResultMapping").getResultList();
List<TestQueryResult> results = this.entityManager.createNativeQuery(queryString ,Constants.TESTQUERYRESULT_MAPPING_NAME).getResultList();
if (results != null && !results.isEmpty()) {
testQueryResult = results.get(0);
}
return testQueryResult;
}
}
... dann violah! Ich habe einige Ergebnisse erhalten: D!
Prost,
Artanis Zeratul
QLRM könnte eine Alternative sein: http://simasch.github.io/qlrm/
Es ist nicht auf eine bestimmte JPA-Implementierung bezogen und funktioniert auch mit JDBC.