Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics and the Play Framework

I am using the Play Framework in it's current version and my model classes extend play.db.jpa.JPABase.

Today I tried to make an often used type of query generic and define a static helper method to construct it.

I wrote the following:

import play.db.jpa.Model;
import play.libs.F;

public class GenericQueries {

  public static <T extends Model> F.Option<T> firstOption(
          Class<T> clazz,
          String query,
          Object... parameters){

    final T queryResult = T.find(query,parameters).first();

    return (queryResult == null) ?
            F.Option.<T>None() :
            F.Option.Some(queryResult);
  }
}

However, I get the following error:

Execution exception

UnsupportedOperationException occured : Please annotate your JPA model with @javax.persistence.Entity annotation.

I debugged into the method, at runtime T seems to be correctly set to it's corresponding Model class. I even see the annotation.

I suspect some class enhancing voodoo by the play guys responsible for this, but I am not entirely sure.

Any ideas?

Update: added Model class as Requested

Here is a shortened Version of one of the Model classes I use.

package models;

import org.apache.commons.lang.builder.ToStringBuilder;
import play.data.validation.Required;
import play.db.jpa.Model;
import play.modules.search.Field;
import play.modules.search.Indexed;

import javax.persistence.Column;
import javax.persistence.Entity;
import java.util.Date;

@Entity @Indexed
public class FooUser extends Model {

  @Required
  public Date firstLogin;

  @Field
  @Required(message = "needs a username")
  @Column(unique = false,updatable = true)
  public String name;

  @Field
  public String description;

  @Required
  public boolean isAdmin;

  @Override
  public String toString(){
    return new ToStringBuilder(this)
            .append("name", name)
            .append("admin", isAdmin)
            .toString();
  }
}
like image 996
msung Avatar asked Feb 23 '23 09:02

msung


2 Answers

In Play entites should extend play.db.jpa.Model and use @Entity annotation (class level).

For what you say I understand that you are extending play.db.jpa.JPABase.

This may be the reason of the issue, as Play (as you point) dynamically enhances classes and it may be clashing with your inheritance.

EDIT: I tested the issue

The problem is that Play is not enhancing the object T. This means that the find method called id the one of GenericModel (parent of Model) whose implementation is to throw an exception with the message.

The enhancer seems to detect only the classes with @Entity.

Even the solution of mericano1 doesn't work, the enhancer doesn't pick it. So I feel you won't be able to use that method in Play.

like image 197
Pere Villega Avatar answered Apr 19 '23 19:04

Pere Villega


The best way to do that is to use a base class that extends play.db.jpa.Model with just the static methods that will be shared by the subclasses.

Add the @Entity annotation to the base class and no class fields.

import play.db.jpa.Model;
import play.libs.F;

public class BaseModel extends Model {

  public static <T extends Model> F.Option<T> firstOption(
          Class<T> clazz,
          String query,
          Object... parameters){

    final T queryResult = T.find(query,parameters).first();

    return (queryResult == null) ?
            F.Option.<T>None() :
            F.Option.Some(queryResult);
  }
}

And then

@Entity 
public class FooUser extends BaseModel {
....
}
like image 24
mericano1 Avatar answered Apr 19 '23 17:04

mericano1