no creo que hay un apoyo fuera de la caja para la generación de ID personalizados utilizando anotaciones personalizadas utilizando directamente la API JPA-2. Pero si desea usar una API específica del proveedor, entonces el trabajo es bastante simple. Sample Example
Para ser independiente del proveedor pruebe cualquiera de los siguientes trucos ....
IdGeneratorHolder interfaz
public abstract class IdGeneratorHolder {
/* PersistentEntity is a marker interface */
public static IdGenerator getIdGenerator(Class<? extends PersistentEntity> entityType) {
/* sample impelementation */
if(Product.class.isAssignableFrom(entityType)) {
return new ProductIdGenerator();
}
return null;
}
}
general IdGenerator
public interface IdGenerator {
String generate();
}
específico IdGenerator - Id de producto generador
public class ProductIdGenerator implements IdGenerator {
public String generate() {
/* some complicated logic goes here */
return ${generatedId};
}
}
Ahora establezca el id. Generado en el constructor sin argumentos O en el método @PrePersist.
Product.java
public class Product implements PersistentEntity {
private String id;
public Product() {
id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
}
@PrePersist
public void generateId() {
id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
}
}
En el ejemplo por encima de todos los identificadores son del mismo tipo, es decir java.lang.String
. Si las entidades persistentes tienen ID de diferentes tipos .....
IdGenerator.java
public interface IdGenerator {
CustomId generate();
}
CustomId.java
public class CustomId {
private Object id;
public CustomId(Object id) {
this.id = id;
}
public String toString() {
return id.toString();
}
public Long toLong() {
return Long.valueOf(id.toString());
}
}
Item.java
@PrePersist
public void generateId() {
id = IdGeneratorHolder.getIdGenerator(getClass()).generate().toLong();
}
También puede utilizar su anotación personalizada ...
CustomIdGenerator.java
public @interface CustomIdGenerator {
IdStrategy strategy();
}
IdStrategy.java
enum IdStrategy {
uuid, humanReadable,
}
IdGeneratorHolder.java
public abstract class IdGeneratorHolder {
public static IdGenerator getIdGenerator(Class<? extends PersistentEntity> entityType) {
try { // again sample implementation
Method method = entityType.getMethod("idMethod");
CustomIdGenerator gen = method.getAnnotation(CustomIdGenerator.class);
IdStrategy strategy = gen.strategy();
return new ProductIdGenerator(strategy);
}
Una cosa más .... Si configuramos la identificación en el método @PrePersist, el método equals() no puede depender del campo de identificación (es decir, clave sustituta), tenemos que usar la clave comercial/natural para implementar el método equals(). Pero si configuramos el campo id con algún valor único (uuid o "app-uid" único dentro de la aplicación) en el constructor no-arg, nos ayuda a implementar el método equals().
public boolean equals(Object obj) {
if(obj instanceof Product) {
Product that = (Product) obj;
return this.id ==that.id;
}
return false;
}
Si nosotros o alguien que llame (intencionalmente o por error) el método @PrePersist anotado más de un momento, el "identificador único seremos transformados !!!" Por lo tanto, es preferible establecer el id en el constructor no-arg. O para abordar este problema ponga un cheque no nulo ...
@PrePersist
public void generateId() {
if(id != null)
id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
}
}
ACTUALIZACIÓN
Si ponemos la generación id en un constructor sin argumentos , no habría que causar un problema al cargar entidades de la base de datos? porque hibernación llamará al constructor sin argumentos que causan los identificadores existentes para ser re- generada
Sí tienes razón, me perdí esa parte. :(En realidad, quería decirte que: en mi aplicación, cada objeto Entity está asociado a una Entidad Organizadora, así que he creado una superclase abstracta con dos constructores, y cada Entidad (excepto Organización) extiende esta clase.
protected PersistentEntityImpl() {
}
protected PersistentEntityImpl(Organization organization) {
String entityId = UUIDGenerator.generate();
String organizationId = organization.getEntityId();
identifier = new EntityIdentifier(entityId, organizationId);
}
el constructor sin argumentos es para el proveedor JPA, nunca invoca sin argumentos constructor, pero el constructor basado otra organización. Como se puede ver. ID se le asigna en el constructor basado organización. (extrañé mucho a este punto al escribir la respuesta, perdón por eso).
Consulte si puede implementar esta o una estrategia similar en su aplicación.
La segunda opción fue usar la anotación @PrePersist. Lo puse en y el método nunca fue golpeado y le di una excepción que decía que necesitaba para configurar el id manualmente. ¿Hay alguna otra cosa que debería hacer? ?
Idealmente, el proveedor de JPA debe invocar los métodos @PrePersist (uno declarado en clase y también todos los otros métodos que se declaran en superclases) antes de persistir el objeto de entidad. No puedo decirle lo que está mal, a menos que muestre algún código y consola.
¡Gracias por su respuesta! Estoy un poco confundido acerca de sus dos sugerencias. Si ponemos la generación de ID en un constructor no-arg, ¿no sería eso un problema al cargar entidades desde la base de datos? porque hibernate llamará al constructor no-arg causando que los identificadores existentes se vuelvan a generar, que no es lo que quiero (incluso si puse un cheque nulo). La segunda opción era usar la anotación @PrePersist. Lo puse y el método nunca fue golpeado y me dio una excepción indicando que necesitaba configurar el ID manualmente. ¿Hay algo más que debería estar haciendo? – digiarnie
@digiarnie +1 por señalar el error. Verifique la parte ACTUALIZAR en la respuesta. – dira
en aplicaciones multiproceso, seguramente tendrá problemas –