Ing*_*her 6 generics scala scala-2.8
编辑:重写了这个问题.添加赏金对我来说很重要.我可以使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]
}
Run Code Online (Sandbox Code Playgroud)
在一个具体的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]]
}
}
Run Code Online (Sandbox Code Playgroud)
顺便说一句,findByAttributes真的很好用.例:
val userList = userEJB.findByAttributes(
User_.title -> Title.MR,
User_.email -> "email@test.com"
)
Run Code Online (Sandbox Code Playgroud)
我意识到,这findByAttributes是非常通用的,它在我的app的所有类中实现DAO都是一样的.唯一改变的是方法中使用的Type.所以在另一个类中继承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]]
}
Run Code Online (Sandbox Code Playgroud)
所以我创建了一个名为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]]
}
Run Code Online (Sandbox Code Playgroud)
所以当前的问题是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])
Run Code Online (Sandbox Code Playgroud)
这意味着1美元???
如果需要:SingularAttribute Javadoc
编辑@Landei:
将方法签名更改为
def findByAttributesOld[A](attributes:AttributeValuePair[A]*):ArrayList[T] = {
Run Code Online (Sandbox Code Playgroud)
而queryRoot.get为
queryRoot.get[A](pair._1.asInstanceOf[SingularAttribute[T,A]])
Run Code Online (Sandbox Code Playgroud)
结果(更短!)错误:
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])
Run Code Online (Sandbox Code Playgroud)
@Sandor Murakozi的解决方案似乎有效.必须测试一下.但如果可能的话,我也会感谢更短的解决方案!
编辑:根据@ifischer的要求添加评论
我认为你的主要问题是,你会因为返回而丢失有价值的类型信息,m.erasure而Class[_]不是Class[T]你在这里真正想要的信息。在进行其他操作之前先进行转换可以避免一些令人讨厌的事情。
此外,JPA 2.0 中使用的未绑定通配符有点烦人,因为您需要跳一些圈子才能绕过它们。
由于查询没有属性没有多大意义,因此我从*- 参数中提取了第一个属性。这也意味着您不需要从 开始conjunction。
我缩短了一些名称,以便代码可以无换行地放入框中:
// import java.util.list as JList, so it does not shadow scala.List
import java.util.{List => JList}
abstract class SuperDAO[T <: AnyRef, K](implicit m: Manifest[T]) {
@PersistenceContext
var em: EntityManager = _
// pretend that we have more type info than we have in the Class object.
// it is (almost) safe to cast the erasure to Class[T] here
def entityClass = m.erasure.asInstanceOf[Class[T]]
lazy val cb: CriteriaBuilder = em.getCriteriaBuilder
// Type alias for SingularAttributes accepted for this DAOs entity classes
// the metamodel will only ever provide you with Attributes of the form
// SingularAttribute<? super X,E>, where X is the entity type (as your
// entity class may extend from another) and E is the element type.
// We would actually like to use a contravariant definition of the first
// type parameter here, but as Java has no notion of that in the definition
// side, we have to use an existential type to express the contravariance
// similar to the way it would be done in Java.
type Field[A] = (SingularAttribute[_ >: T,A],A)
// As we need at least one attribute to query for, pull the first argument out
// of the varargs.
def findByAttributes(attribute: Field[_], attributes: Field[_]*): JList[T] = {
val cq = cb.createQuery(entityClass)
val root = cq.from(entityClass)
// shorthand for creating an equal predicate as we need
// that multiple times below
def equal(a: Field[_]) = cb.equal(root.get(a._1), a._2)
// the Seq of Predicates to query for:
def checks = Seq(
// if there is only one argument we just query for one equal Predicate
if (attributes.isEmpty) equal(attribute)
// if there are more, map the varargs to equal-Predicates and prepend
// the first Predicate to them. then wrap all of them in an and-Predicate
else cb.and(equal(attribute) +: attributes.map(equal) : _*)
)
// as we already casted the entityClass we do not need to cast here
em.createQuery(cq.where(checks : _*)).getResultList
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
928 次 |
| 最近记录: |