РЕДАКТИРОВАТЬ: переписал вопрос. Добавлена награда, так как это важно для меня. Последний совет, с которым я могу заставить работать findByAttributes (без переопределения его в подклассах), получит мои очки.
В моем приложении я делаю безопасные запросы к базе данных с новым JPA2 Criteria Query. Поэтому у меня есть черта DAO, которая должна (повторно) использоваться для ВСЕХ сущностей в моем приложении.
Так вот как выглядит текущая черта, которую я использую (работает):
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]
}
В конкретном DAO я расширяю эту черту следующим образом: устанавливаю тип и реализую методы (удалены все, кроме самого важного метода):
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]]
}
}
Кстати, findByAttributes действительно хорош в использовании. Пример:
val userList = userEJB.findByAttributes(
User_.title -> Title.MR,
User_.email -> "email@test.com"
)
Я понял, что findByAttributes
настолько универсален, что он одинаков во ВСЕХ классах моего приложения, которые реализуют DAO. Единственное, что меняется, это тип, используемый в методе. Таким образом, в другом классе, который наследует DAO, его
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]]
}
Итак, я создал новый абстрактный класс с именем SuperDAO, который должен содержать реализованные обобщенные методы, так что мне не нужно повторно реализовывать их в каждом подклассе.
После некоторой помощи от Landei (спасибо) текущая реализация моего SuperDAO (самая важная часть моей) выглядит следующим образом
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]]
}
Таким образом, текущая проблема заключается в том, что строка с queryRoot.get выдает следующую ошибку:
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])
Что значит с $ 1 ???
При необходимости: SingularAttribute Javadoc
РЕДАКТИРОВАТЬ @Landei:
Изменение сигнатуры метода на
def findByAttributesOld[A](attributes:AttributeValuePair[A]*):ArrayList[T] = {
И queryRoot.get для
queryRoot.get[A](pair._1.asInstanceOf[SingularAttribute[T,A]])
Результат (намного короче!):
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])
Решение @ Sandor Murakozi похоже работает. Придется немного это проверить. Но я также был бы признателен за более короткое решение, если оно вообще возможно!