2010-07-19 14 views
1

J'utilise Scala pour faire des requêtes de typesafe JPA2. donc j'ai une classe Java MetaModel (le seul Java dans mon code, le reste est Scala -> pur problème Scala), qui contient les attributs mon modèle:Comment appeler une fonction qui a une carte avec un type générique en tant que paramètre?

@StaticMetamodel(User.class) 
public class User_ { 
    public static volatile SingularAttribute<User, Long> id; 
    public static volatile SingularAttribute<User, String> name; 
} 

Pour faire une requête pour un seul attribut, i cette fonction:

def findByAttribute[T](
    attribute:SingularAttribute[User, T], value:T):ArrayList[User] = 
{ 
    ... 
} 

que je peux appeler comme ceci:

userEJB.findByAttribute(User_.name, "John") 

maintenant, je suis en train de créer une fonction de requête, avec laquelle je peux interroger pour plusieurs attributs à une fois, et donc je veux utiliser une carte de SingularAttributes comme paramètre pour ma fonction:

// Map is of type scala.collection.immutable.Map 
def findByAttributes[T](
    attributes:Map[SingularAttribute[User, T], T]):ArrayList[User] = 
{ 
    ... 
} 

Ok, donc la fonction devrait fonctionner ... Mais comment puis-je appeler ??? Disons, par exemple, je veux interroger avec une carte comme ceci:

User_.name -> "Doe" 
User_.id -> 5 

Donc, ma première approche de la définition de cette carte à Scala et passant à findByAttributes est la suivante:

val criteria = Map(User_.name -> "Doe", User_.id -> 5) 
// Produces Compiler Error 
val users = userEJB.findByAttributes(criteria) 

Malheureusement, le compilateur ISN « t satisfait lors du passage à la SearchFor findByAttributes fonction, produisant l'erreur ci-dessous:

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] 

Ce problème est le générique le plus compliqué que j'ai jamais eu. Un peu trop haut pour mes compétences;) Quelqu'un sait comment je peux construire le bon type de carte que je peux passer à la fonction? Est-ce possible, ou est-ce que le compilateur ne peut plus déduire le type dans mon cas? Ou est-ce que j'utilise la mauvaise infrastructure de données?

+0

L'utilisation d'identificateurs se terminant par le caractère de soulignement est l'un des pires choix possibles. –

+0

L'utilisation d'identifiants se terminant par le caractère de soulignement est une pratique courante lors de la définition de MetaModels pour JPA2, au moins pour Java. Voir http://java.sun.com/developer/technicalArticles/JavaEE/JavaEE6Overview_Part3.html (recherche de Employee_). – ifischer

+0

Que se passe-t-il si vous spécifiez explicitement le type Map [SingularAttribute [User, String], String] pour les critères et le paramètre de type [String] lors de l'appel de findByAttributes? – Landei

Répondre

1

Je n'ai pas entièrement travaillé les détails, mais je pense que quelque chose comme

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] = 
{ 
    ... 
} 

pourrait fonctionner. Ensuite, un appel ressemblerait

findByAttributes(User_.name -> "Doe", User_.id -> 5) 

Edit: La méthode findByAttributeValuePair peut être nécessaire pour obtenir l'appel à findByAttribute taper-chèque, à en juger de mon violon dans la Scala 2.8 REPL.

+0

Wow. Je savais que cela devait être possible en quelque sorte! Juste testé, semble bien fonctionner. Les types sont correctement vérifiés. Savez-vous si une fonction comme mon findByAttributes, avec tout le contrôle de type, est possible en Java? Serait un bon argument pro-Scala ... – ifischer

+0

Dans ce cas, la version Java ne serait pas très différente. Ce serait simplement plus bavard, et vous auriez à trouver ou à écrire une classe Pair car elle n'est pas incluse dans la bibliothèque Java. Les appels devraient être un peu plus laids, en utilisant une méthode d'usine pour effectuer le contrôle de type et construire les paires: findByAttributes (paire (User_.name, "Doe"), paire (User_.id, 5)) –

2

Lorsque vous déclarez la carte comme suit

val criteria = Map(User_.name -> "Doe", User_.id -> 5) 

le compilateur déduire un type un peu bizarre:

scala.collection.immutable.Map[SingularAttribute[User, _ >: Long with String], Any] 

Essayez par vous-même dans le REPL Scala!

Le problème ici est que le compilateur infère Any comme le type commun de String et Int qui est réellement vrai. Ainsi, vous perdez toute information sur les valeurs réelles de la carte. Et bien sûr, la clé n'est pas vraiment ce que vous voulez.

Cela signifie que Map n'est évidemment pas le bon type. Vous pouvez essayer d'utiliser un n-uplup à la place:

((User_.name -> "Doe"), (User_.id -> 5)) 

Ainsi, toutes les informations de type seront stockées correctement. Bien sûr, vous devrez alors créer plusieurs fonctions findByAttributes (une pour 1-tuple, une pour 2-tuple, 3-tuple et ainsi de suite ...). C'est un peu fastidieux, mais c'est la meilleure solution à laquelle je puisse penser maintenant.

+0

Malheureusement, le paramètre doit être de longueur variable, car je veux utiliser cette fonction pour toutes mes entités, sans avoir à le modifier. Et l'utilisateur ne devrait pas avoir à faire attention dans quel ordre les paramètres sont passés. N'y a-t-il pas un autre type en option? Merci pour les conseils, m'a aidé à comprendre le problème. – ifischer