EDIT: Rewrote die Frage. Bounty als wichtig für mich hinzugefügt. Der letzte Hinweis, mit dem ich findByAttributes zum Arbeiten bringen kann (ohne ihn in Unterklassen neu zu implementieren), wird meine Punkte bekommen.Verschieben Sie die Implementierung einer generischen Methode in eine abstrakte Superklasse
In meiner App mache ich typsichere Datenbankabfragen mit der neuen JPA2-Kriterienabfrage. Daher habe ich eine Eigenschaft DAO, die für alle Entitäten in meiner Anwendung (wiederverwendbar) sein sollte. Das ist also, wie der Umriss der aktuelle Merkmal i Aussehen bin mit wie (die Werke):
trait DAO[T, K](implicit m: Manifest[T]) {
@PersistenceContext
var em:EntityManager = _
lazy val cb:CriteriaBuilder = em.getCriteriaBuilder
def persist(entity: T)
def update(entity: T)
def remove(entity: T)
def findAll(): ArrayList[T]
// Pair of SingularAttribute and corresponding value
// (used for queries for multiple attributes)
type AttributeValuePair[A] = Pair[SingularAttribute[T, A], A]
// Query for entities where given attribute has given value
def findByAttribute[A](attribute:AttributeValuePair[A]):ArrayList[T]
// Query for entities with multiple attributes (like query by example)
def findByAttributes[A](attributes:AttributeValuePair[_]*):ArrayList[T]
}
In einem konkreten DAO, bin erstreckt ich dieses Merkmal wie diese, die Art Festlegung und Umsetzung der Methoden (alle mit Ausnahme der wichtigsten Verfahren entfernt):
class UserDAO extends DAO[User, Long] {
override type AttributeValuePair[T] = Pair[SingularAttribute[User, T], T]
override def findByAttributes[T](attributes:AttributeValuePair[_]*):ArrayList[User] = {
val cq = cb.createQuery(classOf[User])
val queryRoot = cq.from(classOf[User])
var criteria = cb.conjunction
for (pair <- attributes)
criteria = cb.and(cb.equal(queryRoot.get(pair._1), pair._2))
cq.where(Seq(criteria):_*)
val results = em.createQuery(cq).getResultList
results.asInstanceOf[ArrayList[User]]
}
}
BTW, ist findByAttributes wirklich nett zu benutzen. Beispiel:
val userList = userEJB.findByAttributes(
User_.title -> Title.MR,
User_.email -> "[email protected]"
)
Ich erkennen, dass findByAttributes
ist so allgemein, dass es das gleiche in allen Klassen meiner app, die die DAO implementieren. Das einzige, was sich ändert, ist der Typ, der in der Methode verwendet wird. So in eine andere wich Klasse erbt DAO, seine
def findByAttributes[T](attributes:AttributeValuePair[_]*):ArrayList[Message] = {
val cq = cb.createQuery(classOf[Message])
val queryRoot = cq.from(classOf[Message])
var criteria = cb.conjunction
for (pair <- attributes)
criteria = cb.and(cb.equal(queryRoot.get(pair._1), pair._2))
cq.where(Seq(criteria):_*)
val results = em.createQuery(cq).getResultList
results.asInstanceOf[ArrayList[User]]
}
Also habe ich eine neue abstrakte Klasse namens SuperDAO, die die Umsetzung generische Methoden enthalten sollte, so dass ich muss sie in jeder Unterklasse nicht erneut implementieren. Nach etwas Hilfe von Landei (danke), der (wichtigste Teil meiner) aktuelle Umsetzung meiner SuperDAO sieht wie folgt aus
abstract class SuperDAO[T, K](implicit m: Manifest[T]) {
@PersistenceContext
var em:EntityManager = _
lazy val cb:CriteriaBuilder = em.getCriteriaBuilder
type AttributeValuePair[A] = Pair[SingularAttribute[T, A], A]
def findByAttributes(attributes:AttributeValuePair[_]*):ArrayList[T] = {
val cq = cb.createQuery(m.erasure)
val queryRoot = cq.from(m.erasure)
var criteria = cb.conjunction
for (pair <- attributes) {
criteria = cb.and(
cb.equal(
// gives compiler error
queryRoot.get[SingularAttribute[T,_]](pair._1)
)
,pair._2
)
}
cq.where(Seq(criteria):_*)
val results = em.createQuery(cq).getResultList
results.asInstanceOf[ArrayList[T]]
}
So das aktuelle Problem ist, dass die Leitung mit queryRoot.get den folgenden Fehler erzeugt:
overloaded method value get with alternatives:
(java.lang.String)javax.persistence.criteria.Path
[javax.persistence.metamodel.SingularAttribute[T, _]] <and>
(javax.persistence.metamodel.SingularAttribute[_ >: Any,
javax.persistence.metamodel.SingularAttribute[T,_]])
javax.persistence.criteria.Path
[javax.persistence.metamodel.SingularAttribute[T, _]]
cannot be applied to
(javax.persistence.metamodel.SingularAttribute[T,_$1])
Was ist mit $ 1 gemeint ??
Bei Bedarf: SingularAttribute Javadoc
EDIT @Landei:
Ändern der Methodensignatur
def findByAttributesOld[A](attributes:AttributeValuePair[A]*):ArrayList[T] = {
Und die queryRoot.get zu
queryRoot.get[A](pair._1.asInstanceOf[SingularAttribute[T,A]])
Ergebnisse in der (viel kürzer !) Fehler:
overloaded method value get with alternatives:
(java.lang.String)javax.persistence.criteria.Path[A] <and>
(javax.persistence.metamodel.SingularAttribute[_ >: Any, A])
javax.persistence.criteria.Path[A] cannot be applied to
(javax.persistence.metamodel.SingularAttribute[T,A])
Die Lösung von @Sandor Muraközi scheint zu funktionieren. Ich muss es ein wenig testen. Aber ich würde auch eine kürzere Lösung schätzen, wenn es überhaupt möglich ist! (?)
Die übliche terminologische Vorbehalt gilt: Wenn Sie schreiben 'def FBA (...): Sometype = {... } 'Sie definieren eine * Methode *, nicht eine Funktion. Es gibt viele Möglichkeiten, Funktionen in Scala zu bekommen. Z.B.Teilanwendung, die verwendet werden kann, um eine Methode auf eine entsprechende Funktion zu heben: 'def mFBA (...): ... = {...}; val fFBA = mFBA _ '. –
Ich werde nie die Unterscheidung zwischen beiden richtig bekommen ... aber Ihr Kommentar geholfen. Ersetzte "Funktion" durch "Methode". – ifischer
Über die "def findByAttributesOld [A] ..." Version: Ich denke, es ist nicht ganz richtig, weil die Liste der Attribute nicht wirklich homogen ist: Es kann z. Int und String-Attribute, so dass A schließlich Any wird (zumindest im allgemeinsten Fall). Ich bin mir auch nicht sicher, ob es helfen kann: Wenn meine Vermutung über die existenziellen Probleme richtig ist, dann denke ich, dass es nicht sehr wahrscheinlich ist. –