users@jpa-spec.java.net

[jpa-spec users] Proposal: allow JPA entities not to have a default no-arg constructor

From: Oliver Gierke <ogierke_at_vmware.com>
Date: Sun, 13 Nov 2011 04:30:11 -0800

Hi all,

I'd like to propose to allow JPA entities not to have a default constructor. Section 2.1 of the JPA 2.0 spec states.

> The entity class must have a no-arg constructor. The entity class may have other constructors
> as well. The no-arg constructor must be public or protected.

as well as

> The entity class must not be final. No methods or persistent instance variables of the entity
> class may be final.

Both of theses constraints prevent entities to be designed as strong domain classes that enforce some constraints and thus allow safer programming. Given a User class I might want make sure always has a a not-null, not-empty email address and I want to make that sure on the type level. Thus the class I'd like to write is the following

class User {

  private final String emailAddress;

  public User(String emailAddress) {
    Assert.hasText(emailAddress);
    this.emailAddress = emailAddress;
  }
}

Due to the current restrictions of the spec I have to write:

class User {

  private String emailAddress;

  public User(String emailAddress) {
    Assert.hasText(emailAddress);
    this.emailAddress = emailAddress;
  }

  protected User() {
  
  }
}

knowing that persistence providers will use the no-arg constructor pretty much breaking my domain constraints and populating the emailAddress property via reflection.

So here's what I am proposing:

0. Allow entities not to provide a no.arg constructor and use final fields.
1. Define a constructor resolution strategy such as:

   - if a nor-arg constructor is found, use this one
   - if a single complex constructor is found, use this one (see more on parameter binding below)
   - if multiple single complex constructors are found, insist on one being selected as the one to be used via an annotation (e.g. @PersistenceConstructor) or the appropriate XML equivalent

2. Resolve constructor parameters by either inspecting parameter names (req. compilation with debug flag), inspecting @Column information on a property with same name as parameter name or define an annotation to define the column the parameter object shall be read from.
3. If relations are passed into the constructor this requires the relation to be resolved eagerly.
4. The so defined constructor is used for queries not explicitly using a constructor expression (see 4.8.2 of the 2.0 spec).

Another question that plays into this is that I am wondering what the rationale behind limiting the results of constructor expressions in queries to be in new or detached state? I can perfectly understand why this is the case for (non-entity) DTOs. Nevertheless I think it might make sense to let an entity in its entirety be constructed from such an expression. Why shouldn't it be in a managed state? E.g.

@Entity
class User {

  @Id
  @GeneratedValue
  private Long id;

  private String firstname;
  private String emailAddress;

  public User(String emailAddress) {
    Asser.hasText(emailAddress);
    this.emailAddress = emailAddress;
  }
  
  protected User() {}

  
}

Why shouldn't I be able to define a query "select new User(u.emailAddress) from User u" forcing the persistence provider to use a particular constructor in that scenario and just get the entity back in the same state as if I had queried "select u from User u"?

Regards,
Ollie

-- 
/**
 * @author Oliver Gierke - Senior Member Technical Staff
 *
 * @param email ogierke_at_vmware.com
 * @param phone +49-351-30929001
 * @param fax   +49-351-418898439
 * @param skype einsdreizehn
 * @see http://www.olivergierke.de
 */