我正在使用Scala来执行类型安全的JPA2 Criteria Queries。 因此,我有一个Java MetaModel类(我的代码中唯一的Java,其余的是Scala - >纯Scala问题),它拥有我的模型属性:
@StaticMetamodel(User.class)
public class User_ {
public static volatile SingularAttribute<User, Long> id;
public static volatile SingularAttribute<User, String> name;
}
要对一个属性进行查询,我有这个功能:
def findByAttribute[T](
attribute:SingularAttribute[User, T], value:T):ArrayList[User] =
{
...
}
我可以这样打电话:
userEJB.findByAttribute(User_.name, "John")
现在我正在尝试创建一个查询函数,我可以使用它查询多个属性,因此我想使用SingularAttributes的Map作为我的函数的参数:
// Map is of type scala.collection.immutable.Map
def findByAttributes[T](
attributes:Map[SingularAttribute[User, T], T]):ArrayList[User] =
{
...
}
好的,所以这个功能应该有效...但我怎么称它呢???比方说,我想用这样的地图查询:
User_.name -> "Doe"
User_.id -> 5
所以我在Scala中定义这个Map并将它传递给findByAttributes的第一种方法是:
val criteria = Map(User_.name -> "Doe", User_.id -> 5)
// Produces Compiler Error
val users = userEJB.findByAttributes(criteria)
不幸的是,在将searchFor传递给findByAttributes函数时,编译器不满意,产生以下错误:
no type parameters for method findByAttributes: (attributes:
Map[javax.persistence.metamodel.SingularAttribute[net.teachernews.model.User,
T],T])
java.util.ArrayList[net.teachernews.model.User] exist so that it can be applied to
arguments (scala.collection.immutable.Map[javax.persistence.metamodel.
SingularAttribute[
net.teachernews.model.User, _
>: java.lang.Long with java.lang.String
<: java.lang.Comparable[_
>: java.lang.Long with java.lang.String
<: java.lang.Comparable[_ >: java.lang.Long with java.lang.String
<: java.io.Serializable] with java.io.Serializable]
with java.io.Serializable],Any]) --- because ---
argument expression's type is not compatible with formal parameter type;
found :
scala.collection.immutable.Map[javax.persistence.metamodel.SingularAttribute[
net.teachernews.model.User, _
>: java.lang.Long with java.lang.String
<: java.lang.Comparable[_
>: java.lang.Long with java.lang.String
<: java.lang.Comparable[_ >: java.lang.Long with java.lang.String
<: java.io.Serializable] with java.io.Serializable] with java.io.Serializable],
Any]
required: Map[javax.persistence.metamodel.SingularAttribute[
net.teachernews.model.User,?T],?T]
这是我曾经遇到的最复杂的一般问题。对我的技能来说有点太高了;)任何人都知道如何构建我可以传递给函数的正确的地图类型?它甚至可能,或者编译器不能在我的情况下推断出类型?或者我使用错误的数据结构?
答案 0 :(得分:2)
声明地图时如下
val criteria = Map(User_.name -> "Doe", User_.id -> 5)
编译器将推断出一种有点奇怪的类型:
scala.collection.immutable.Map[SingularAttribute[User, _ >: Long with String], Any]
在Scala的REPL中自己尝试一下!
这里的问题是编译器将Any
推断为String
和Int
的常见类型,这实际上是正确的。因此,您丢失了有关地图中实际值的任何信息。当然,关键也不是你想要的。
这意味着Map
显然不是正确的类型。您可以尝试使用n元组:
((User_.name -> "Doe"), (User_.id -> 5))
因此,所有类型信息都将被正确存储。当然,您将需要创建多个findByAttributes
函数(一个用于1元组,一个用于2元组,3元组等等)。这有点单调乏味,但这是我现在想到的最好的解决方案。
答案 1 :(得分:1)
我还没有完全弄清楚细节,但我想像是
type AttributeValuePair[T] = Pair[SingularAttribute[User, T], T]
def findByAttributeValuePair[T](p:AttributeValuePair[T]):ArrayList[User] =
findByAttribute(p._1, p._2)
def findByAttributes(attributes:AttributeValuePair[_]*):ArrayList[User] =
{
...
}
可能会奏效。然后一个电话看起来像
findByAttributes(User_.name -> "Doe", User_.id -> 5)
编辑:findByAttributeValuePair
方法可能需要调用findByAttribute
来进行类型检查,从我在Scala 2.8 REPL中摆弄来判断。