6

I'm trying to understand how does the inheritance work in play! But unsuccessfully yet.

So, I have such superclass:

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)  
abstract class SuperClass extends Model {  
    @Id  
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "SEQ_TABLE")   
    @TableGenerator(name = "SEQ_TABLE")  
    Long id;  

    int testVal;
}

And 2 inherited classes:

@Entity
public class Sub extends SuperClass {        
    String name;

    @Override
    public String toString() {
            return name;
    }
}

@Entity
public class Sub1 extends SuperClass {        
    String name;

    @Override
    public String toString() {
            return name;
    }
}

Also I have 2 controllers for inherited classes:

public class Subs and Sub1s extends CRUD {

}

After application was started, I recieve 2 tables in MySQL db for my models (Sub and Sub1) with such structure: id bigint(20), name varchar(255). Without testVal which is in superclass.

And when I try to create new object of Sub class in CRUD interface I recieve such error: Execution error occured in template {module:crud}/app/views/tags/crud/form.html. Exception raised was MissingPropertyException : No such property: testVal for class: models.Sub.

In {module:crud}/app/views/tags/crud/form.html (around line 64) #{crud.numberField name:field.name, value:(currentObject ? currentObject[field.name] : null) /}

  1. What should I do to generate MySQL tables for inherited models properly and fix the error?
  2. Is it possible to have a single superController for several inherited classes?
Thomas
  • 2,751
  • 5
  • 31
  • 52
gl0om
  • 109
  • 1
  • 9
  • 1
    note that you declare your fields with default access modifier. Change it to public so that play PropertiesEnhaner could do its work. – sdespolit Nov 12 '11 at 08:41
  • Thank yout. I've added public access to `Long id` and `int testVal`. Now I can see testVal field in CRUD interface, and can save model without errors. But there's still no such field in MySql, so I can't save this value. I believe that I should add some kind of annotation to this value, but what? – gl0om Nov 12 '11 at 10:52
  • Finally, I found the solution: I've just added `@MappedSuperclass` to `SuperClass` and removed `Long id` from it. **sdespolit**, how to accept your suggestion to add public access midifier? I can't find any accept button. – gl0om Nov 12 '11 at 17:31
  • 1
    you can not accept comments. but you can write your own answer summing it all up. i will not object. – sdespolit Nov 12 '11 at 17:44
  • btw make sure you're aware of drawbacks of the strategy http://docs.jboss.org/hibernate/stable/annotations/reference/en/html/entity.html#d0e1191. And besides i've tested your schema (as in your 1st comment but without id) - and it works fine in postgresql. – sdespolit Nov 12 '11 at 18:00
  • Is this for play 1 or play 2 ? Please edit your post so that it is clear. – Moebius Oct 17 '14 at 14:17

2 Answers2

2

Well, thanks to sdespolit, I've made some experiments. And here is what I've got:

Superclass:

@MappedSuperclass
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class SuperClass extends Model {
}

Inherited class:

@Entity 
public class Sub extends SuperClass {
}

"Super Controller" I made in such way:

@With({Secure.class, SuperController.class})
@CRUD.For(Sub.class)
public class Subs extends CRUD {
}

@With({Secure.class, SuperController.class})
@CRUD.For(Sub1.class)
public class Sub1s extends CRUD {
}

@CRUD.For(Sub.class) is used to tell the interceptors with what class it should work

public class SuperController extends Controller {

    @After/Before/Whatever
    public static void doSomething() {
        String actionMethod = request.actionMethod;
        Class<? extends play.db.Model> model = getControllerAnnotation(CRUD.For.class).value();

        List<String> allowedActions = new ArrayList<String>();
        allowedActions.add("show");
        allowedActions.add("list");
        allowedActions.add("blank");

        if (allowedActions.contains(actionMethod)) {
            List<SuperClass> list = play.db.jpa.JPQL.instance.find(model.getSimpleName()).fetch();
        }
    }
}

I'm not sure about doSomething() approach is truly nice and Java-style/Play!-style. But it works for me. Please tell me if it's possible to catch out the model's class in more native way.

gl0om
  • 109
  • 1
  • 9
1

"and table per class is an optional feature of the JPA spec, so not all providers may support it" from WikiBook.

Why don't you use @MappedSuperclass? Furthermore you should extend GenericModel. In your example you defined id twice, which could be the reason of you problem too.

niels
  • 7,321
  • 2
  • 38
  • 58