En nuestro proyecto usamos el enfoque de constructores de vainilla (@see Effective Java). Consideremos el siguiente ejemplo:
@Entity
public class Person {
public static class Builder {
private String firstName;
private String lastName;
private PhoneNumber phone;
public Builder() {}
public Builder withFullName(String fullName) {
Preconditions.notNull(fullName);
String[] split = fullName.split(" ");
if (split == null || split.length != 2) {
throw new IllegalArgumentException("Full name should contain First name and Last name. Full name: " + fullName);
}
this.firstName = split[0];
this.lastName = split[1];
return this;
}
public Builder withPhone(String phone) {
// valueOf does validation
this.phone = PhoneNumber.valueOf(phone);
return this;
}
public Person build() {
return new Person(this);
}
}
//@Columns
private long id;//@Id
private String firstName;
private String lastName;
private String phoneNumber;
// hibernate requires default constructor
private Person() {}
private Person(Builder builder) {
this.firstName = Preconditions.notNull(builder.firstName);
this.lastName = Preconditions.notNull(builder.lastName);
this.phoneNumber = builder.phone != null ? builder.phone : null;
}
//Getters
@Nonnull
public String getFirstName() { return firstName;}
@Nonnull
public String getLastName() { return lastName;}
@Nullable
public String getPhoneName() { return phone;}
public long getId() { return id;}
}
En caso de que quiera a mutar en ocasiones la entidad lo recomiendo para introducir new Builder(Person person)
que copiar todos los datos de nuevo, por lo que puede mutar con el constructor. Por supuesto, producirá una nueva entidad, por lo que la anterior sigue siendo de solo lectura.
El uso (con mutación) es tan sencillo como:
Person.Builder personBuilder = new Person.Builder();
Person person = personBuilder.withFullName("Vadim Kirilchuk").withPhone("12345678").build();
Person modified = new Person.Builder(person).withPhone("987654321").build();
También es importante tener en cuenta que en este ejemplo la persona no está 100% inmutable (y no puede ser) de clase: en primer lugar porque id se establecerá por jpa, también se pueden obtener asociaciones diferidas en tiempo de ejecución y, por último, porque no se pueden tener campos finales (debido al constructor predeterminado requerido) :(Este último punto también es una preocupación para entornos multiproceso, es decir, Es posible que la entidad pase a otra cadena justo después de que #build() pueda generar todo tipo de errores, ya que no se garantiza que el hilo abother vea el objeto completamente construido.
La especificación 2.1, JPA sección "2.1 La clase de entidad", dice:
No hay métodos o variables de instancia persistentes de la clase de entidad pueden ser final.
Uno más enfoque similar: http://vlkan.com/blog/post/2015/03/21/immutable-persistence/
En mi caso sería simplemente añadir id para el constructor lugar la construcción de servicio en la parte superior de los proyectos ..
es así, estás usando un constructor para construir el entidad, pero todavía tienen mutadores? puedes elaborar un poco? –
Esto es lo que hacemos (esto podría no ser aplicable globalmente a todas las aplicaciones). Digamos que tenemos una persona de clase. Luego tendremos PersonFactoryBuilder que creará un objeto Person pero lo lanzará a ReadablePerson. ReadablePerson es una interfaz que solo expone los métodos "get" y lo hace inmutable. Mientras recuperamos de la base de datos, tenemos el PersonRepository (como DAO), que recupera a la persona de la base de datos y, en función de la intención (actualización o visualización), arroja/devuelve una persona legible o persona (mutable). Este diseño fue inspirado de la biblioteca joda-time. http://joda-time.sourceforge.net –
¡Casting a una interfaz! hurra por la abstracción. Esa es una gran idea. Gracias. – b3bop