【发布时间】:2011-03-19 11:56:24
【问题描述】:
编辑:重写问题。添加赏金对我很重要。我可以让 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 的解决方案似乎有效。必须测试一下。但如果可能的话,我也希望有一个更短的解决方案!
【问题讨论】:
-
通常的术语警告适用:当您编写
def fBA(...): SomeType = { ... }时,您定义的是一个方法,而不是一个函数。在 Scala 中有很多方法可以获取函数。例如,部分应用,可用于将方法提升到相应的函数:def mFBA(...): ... = { ... }; val fFBA = mFBA _. -
我永远无法区分两者之间的区别...但是您的评论有所帮助。将“函数”替换为“方法”。
-
关于“def findByAttributesOld[A]...”版本:我认为这不太正确,因为属性列表并不是真正同质的:它可以包含例如Int 和 String 属性,所以 A 最终会变成 Any(至少在最一般的情况下)。我也不确定它是否有帮助:如果我对存在类型问题的猜测是正确的,那么我认为这不太可能。