views:

927

answers:

6

Hi!

I don't like the requirement on have at least one empty constructor and public setters on JPA entities. While I understand the issue on the EntityManager side, this invalidates class invariants.

Does anyone have a solution for this (design pattern or idiom level) ?

Thanks!

Igor

+1  A: 

With DataNucleus you don't have to add a default constructor if you don't want to; it will be added automatically by bytecode enhancement. Also you could persist fields instead of properties, hence no need for public setters.

--Andy (DataNucleus)

DataNucleus
A: 

Yes, persist fields instead of properties, but on the not wanting a default constructor you're typically (unless the byte code enhancer does some trick) you're not going to get away from it.

You have to allow your jpa implementation to instantiate the class, so a public default constructor is mandatory.

Michael Wiles
+3  A: 

With JPA, the default constructor is required, however, you are not required to use setters. You can choose a property access strategy(field or method) based on where you place the annotations.

The following code will use direct field access and will work as a part of an entity without a setter:

@Column(name = DESCRIPTION)
private String description;

public String getDescription() { return description; }

Versus method access with a setter:

private String description;

@Column(name = DESCRIPTION)
public void setDescription(String description) {
     this.description = description;
}

public String getDescription() { return description; }
Brad C
A: 

Just make your constructor protected or private, so you preserve the class invariants!

public class Person {
 private String firstName;
 private String lastName;

 public Person(String firstName, String lastName) {
  setFirstName(firstName);
  setLastName(lastName);
 }

 // private no-arg constructor for hibernate.
 private Person() {

 }

 public String getFirstName() {
  return firstName;
 }
 public String getLastName() {
  return lastName;
 }

 // private setters for hibernate
 private void setFirstName(String nme) {
  firstName = nme;
 }
 private void setLastName(String nme) {
  lastName = nme;
 }
}

see http://www.javalobby.org/java/forums/m91937279.html for details.

KlausMeier
A: 

OpenJPA can add a no-arg ctor as a part of enhancing your entities.

Just so we're clear, the requirement is mandated in the JPA spec. The previous answer says that you can make the no-arg ctor private, but that is not compliant with the spec(I see the link points to a Hibernate specific page). The spec states that an Entity must have a public or protected no-arg ctor.

-Rick

Rick
+2  A: 

In point of fact you should have both a no-args constructor and getter and setter methods. The requirements are indicated in section 2.1 of the spec.

The no-arg constructor requirement is found on page 17 in my copy :

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.

Page 18 has the requirement for accessor methods :

The persistent state of an entity is represented by instance variables, which may correspond to Java- Beans properties. An instance variable may be directly accessed only from within the methods of the entity by the entity instance itself. Instance variables must not be accessed by clients of the entity. The state of the entity is available to clients only through the entity’s accessor methods (getter/setter methods) or other business methods. Instance variables must be private, protected, or package visibility.

Field vs. Property access indicates how the JPA provider interacts with your entity, not how the client application interacts with it. The client should always use get and set methods.

Some JPA providers are more lenient in these requirements and you may be able to make the constructor private (as suggested above) with a specific vendor. The application might not be portable though so you could be in for a surprise if you migrate in the future.

So I wouldn't recommend omitting the methods entirely. In order to resolve the problem I'd mark the public no-arg ctor as deprecated (put something in the javadoc about it being for JPA provider use only). The set methods can contain the logic you want to maintain your invariants.

It isn't ideal but it should prevent the wrong ctor from being used by accident (I'm assuming you have a ctor that sets the invariants).

Mike