Note that there are some explanatory texts on larger screens.

plurals
  1. POMove the implementation of a generic method to an abstract super class
    text
    copied!<p><strong>EDIT: Rewrote the question. Added bounty as its important for me. The final hint with which i can get findByAttributes to work (without reimplementing it in subclasses) will get my points.</strong></p> <p>In my app i'm doing typesafe database queries with the new JPA2 Criteria Query. Therefore I have a trait DAO which should be (re)usable for ALL entities in my application. So this is how the outline the current trait i'm using looks like (which works):</p> <pre><code>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] } </code></pre> <p>In a concrete DAO, i'm extending this trait like this, setting the type and implementing the methods (removed all except the most important method):</p> <pre><code>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 &lt;- 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]] } } </code></pre> <p>BTW, findByAttributes is really nice to use. Example:</p> <pre><code>val userList = userEJB.findByAttributes( User_.title -&gt; Title.MR, User_.email -&gt; "email@test.com" ) </code></pre> <p>I realized, that <code>findByAttributes</code> is so generic, that its the same in ALL classes of my app that implement the DAO. The only thing that changes is the Type used in the method. So in another class wich inherits DAO, its </p> <pre><code>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 &lt;- 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]] } </code></pre> <p>So i created a new abstract class named SuperDAO that should contain the implemented generic methods, so that i don't have to re-implement them in every subclass. After some help from Landei (thanks), the (most important part of my) current implementation of my SuperDAO looks like this</p> <pre><code>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 &lt;- 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]] } </code></pre> <p>So the current problem is that the line with queryRoot.get produces the following error:</p> <pre><code>overloaded method value get with alternatives: (java.lang.String)javax.persistence.criteria.Path [javax.persistence.metamodel.SingularAttribute[T, _]] &lt;and&gt; (javax.persistence.metamodel.SingularAttribute[_ &gt;: 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]) </code></pre> <p>Whats meant with $1???</p> <p>If needed: <a href="http://javadoc.glassfish.org/javaee6/apidoc/javax/persistence/metamodel/SingularAttribute.html" rel="nofollow noreferrer">SingularAttribute Javadoc</a></p> <p><strong>EDIT @Landei:</strong></p> <p>Changing the method signature to </p> <pre><code>def findByAttributesOld[A](attributes:AttributeValuePair[A]*):ArrayList[T] = { </code></pre> <p>And the queryRoot.get to</p> <pre><code>queryRoot.get[A](pair._1.asInstanceOf[SingularAttribute[T,A]]) </code></pre> <p>Results in the (much shorter!) error:</p> <pre><code>overloaded method value get with alternatives: (java.lang.String)javax.persistence.criteria.Path[A] &lt;and&gt; (javax.persistence.metamodel.SingularAttribute[_ &gt;: Any, A]) javax.persistence.criteria.Path[A] cannot be applied to (javax.persistence.metamodel.SingularAttribute[T,A]) </code></pre> <p>The solution of <strong>@Sandor Murakozi</strong> seems to work. Have to test it a bit. But i would also appreciate a shorter solution, if its possible at all!</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload