[error] play - Cannot invoke the action, eventually got an error: java.lang.RuntimeException: Cannot instantiate class models.Customer. It must have a default constructor
When I am using play framework 2.3.5, I met this problem. It seems there is an overriding between the default constructor and the constructor that written by myself. But the Customer entity extends User entity, there should be a constructor to manage this. Therefore, I don't know how to fix it.
Controller.Appllication
package controllers;
import models.Customer;
import models.User;
import play.*;
import play.data.Form;
import play.db.jpa.JPA;
import play.db.jpa.Transactional;
import play.mvc.*;
import views.html.*;
import static play.data.Form.form;
public class Application extends Controller {
//for logging
public static Logger LOG = new Logger();
final static Form<Customer> signupForm = form(Customer.class);
public static Result blank() {
return ok(signup.render(signupForm));
}
public static Result submit(){
Form<Customer> filledForm = signupForm.bindFromRequest();
LOG.info(filledForm.toString());
LOG.info("Username: " + filledForm.field("username").value());
//instantiate User entity
Customer created = filledForm.get();
LOG.info("User:" + created.toString());
Customer newcus = Customer.create(created.getEmail(), created.getUsername(), created.getPassword());
session("email", newcus.getEmail());
LOG.info("sessionUser:" + newcus.getEmail());
return redirect(
//return to home page
routes.Application.welcome()
);
}
}
Model.Customer
import java.util.ArrayList;
import java.util.Collection;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import play.db.jpa.JPA;
@Entity
public class Customer extends User{
protected Customer(String email, String username, String password) {
super(email, username, password);
}
public static Customer create(String email, String username, String password){
Customer cus = new Customer(password, password, password);
JPA.em().persist(cus);
return cus;
}
@ManyToMany(cascade = { CascadeType.ALL })
private Collection<ConcreteCourse> selectedCourses = new ArrayList<ConcreteCourse>();
@OneToMany(mappedBy = "author", cascade = { CascadeType.ALL })
private Collection<Review> reviews = new ArrayList<Review>();
}
Model.User package models;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import play.data.format.Formats;
import play.data.validation.Constraints;
import common.BaseModelObject;
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class User extends BaseModelObject {
//
@Constraints.Required
@Formats.NonEmpty
@Column(unique = true)
public String email;
@Constraints.Required
@Formats.NonEmpty
public String username;
@Constraints.Required
@Formats.NonEmpty
public String password;
protected User(String email, String username, String password) {
this.email = email;
this.username = username;
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Neither Customer
nor the User
classes above have a default constructor. The moment you declare your own constructor with parameters, you immediately disable the default one. Or rather, if you never declare any constructor, Java automatically creates a default one for your.
You will need to define a default constructor for both of your classes. Since there are no final fields in your classes, this should be fairly simple. In fact, you can keep your older constructors as well (if they are of help somewhere in your app).
public class Customer extends User{
public Customer() {
}
}
Note: You need to have the default constructor public. What most JPA frameworks do is to use Java reflection to instantiate your classes. Most of them rely that you have a public default constructor (without any parameters) that they can easily use (otherwise they would need to determine what kind of data to pass to your constructors). After they have your class instantiated, they use any setter or getter methods to configure the fields (e.g. email
, username
), unless your fields are public (as in your case) in which case such methods are not needed.