EDITAR: volvió a escribir la pregunta. La recompensa añadida es importante para mí. La última sugerencia con la que puedo hacer que FindByAttributes funcione (sin volver a implementarla en subclases) obtendrá mis puntos.Mueva la implementación de un método genérico a una superclase abstracta
En mi aplicación realizo consultas de base de datos de tipo seguro con la nueva consulta de criterios JPA2. Por lo tanto, tengo un rasgo DAO que debería ser (re) utilizable para TODAS las entidades de mi aplicación. Así que esta es la forma en el contorno del rasgo actual que estoy usando se parece (que trabaja):
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]
}
En un DAO concreto, les extiendo este rasgo de esta manera, ajustar el tipo y la aplicación de los métodos (eliminado todos excepto el método más importante):
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]]
}
}
Por cierto, findByAttributes es muy agradable de usar. Ejemplo:
val userList = userEJB.findByAttributes(
User_.title -> Title.MR,
User_.email -> "[email protected]"
)
me di cuenta, que findByAttributes
es tan genérico, que es la misma en todas las clases de mi aplicación que implementan la DAO. Lo único que cambia es el tipo utilizado en el método. Así, en otra clase hereda wich DAO, su
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]]
}
así que creé una nueva clase abstracta llamada SuperDAO que debe contener los métodos genéricos implementadas, por lo que no tengo que volver a aplicar en cada subclase. Después de una cierta ayuda de Landei (gracias), el (la parte más importante de mi) implementación actual de mi SuperDAO se parece a esto
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]]
}
Así que el problema actual es que la línea con queryRoot.get produce el siguiente error:
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])
Cuál es significaba $ 1 ???
si es necesario: SingularAttribute Javadoc
EDITAR @Landei:
Cambio del método de firma a
def findByAttributesOld[A](attributes:AttributeValuePair[A]*):ArrayList[T] = {
Y el queryRoot.get a
queryRoot.get[A](pair._1.asInstanceOf[SingularAttribute[T,A]])
Los resultados en el (mucho más corto !) Error:
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])
La solución de @Sandor Murakozi parece funcionar. Tengo que probarlo un poco. Pero también agradecería una solución más corta, ¡si es posible! (?)
La advertencia terminológica habitual: Cuando se escribe 'def fBA (...): SomeType = {... } 'estás definiendo un * método *, no una función. Hay muchas formas de obtener funciones en Scala. P.ej., aplicación parcial, que se puede utilizar para elevar un método a una función correspondiente: 'def mFBA (...): ... = {...}; val fFBA = mFBA _'. –
Nunca tendré la distinción entre los dos ... pero tu comentario ayudó. Reemplazó "función" por "método". – ifischer
Acerca de la versión "def findByAttributesOld [A] ...": Creo que no es del todo correcto, porque la lista de atributos no es realmente homogénea: puede contener, p. Int y String atributos, por lo que A finalmente se convertirá en Any (al menos en el caso más general). Tampoco estoy seguro de si puede ayudar: si mi conjetura acerca de los problemas de tipo existencial es correcta, entonces creo que no es muy probable. –