2011-08-28 18 views
15

¿Hay forma de asignar objetos de valor inmutables como la dirección de correo electrónico mediante JPA?Objetos de valor inmutables y JPA

@Immutable 
@Embeddable 
public final class EmailAddress { 
    private final String value; 

    public EmailAddress(String value) { 
     this.value = value; 
    } 

    public String getValue() { 
     return value; 
    } 

    @Override 
    public boolean equals(Object o) { 
     if (this == o) return true; 
     if (o == null || getClass() != o.getClass()) return false; 
     EmailAddress that = (EmailAddress) o; 
     return value.equals(that.value); 
    } 

    @Override 
    public int hashCode() { 
     return value.hashCode(); 
    } 
} 

ahora consigo excepción de entidad de ahorro

org.hibernate.InstantiationException: No default constructor for entity: com.domain.EmailAddress 
    org.hibernate.tuple.PojoInstantiator.instantiate(PojoInstantiator.java:107) 
    org.hibernate.tuple.component.AbstractComponentTuplizer.instantiate(AbstractComponentTuplizer.java:102) 
    org.hibernate.type.ComponentType.instantiate(ComponentType.java:515) 
    org.hibernate.type.ComponentType.deepCopy(ComponentType.java:434) 
    org.hibernate.type.TypeHelper.deepCopy(TypeHelper.java:68) 
    org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:302) 
    org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:203) 
    org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:129) 
    org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:69) 
    org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:179) 
    org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:135) 
    org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61) 
    org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:808) 
    org.hibernate.impl.SessionImpl.persist(SessionImpl.java:782) 
    org.hibernate.impl.SessionImpl.persist(SessionImpl.java:786) 
    org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:672) 
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    java.lang.reflect.Method.invoke(Method.java:597) 
    org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240) 
    $Proxy25.persist(Unknown Source) 
    org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:360) 
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    java.lang.reflect.Method.invoke(Method.java:597) 
    org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:368) 
    org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:349) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) 
    $Proxy26.save(Unknown Source) 
    com.controller.UserController.create(UserController.java:64) 

quiero utilizar campos finales y de hibernación como la implementación de la APP.

Respuesta

13

usted ganó' ser capaz de hacer eso usando anotaciones JPA estándar y un objeto incrustable, porque el objeto tendrá que ser creado usando un constructor predeterminado, y el valor establecido por reflexión.

Sin embargo, podría utilizar un tipo personalizado Hibernate. Lea this part of the Hibernate reference documentation, donde hay un tipo de ejemplo Money, que se crea una instancia utilizando un constructor con argumentos, y por lo tanto puede ser inmutable.

+0

Muchas gracias. Funciona perfectamente bien –

3

Para que JPA pueda crear objetos mediante reflexión, debe tener un constructor predeterminado, pero no tiene que ser público. También me gusta mantener mis campos finales, pero esto podría ser demasiado restrictivo para reflexionar, tendrás que intentarlo.

yo sugeriría dejar caer el modificador de campo final y la adición de un constructor por defecto privado con un breve comentario (por lo que aún sabe por qué ese constructor no-op es allí la próxima semana):

public final class EmailAddress { 
    private String value; // no final modifier 

    private EmailAddress() { 
     // for JPA 
    } 

    public EmailAddress(String value) { 
     this.value = value; 
    } 
... 
} 
+0

Gracias. Pero espero que haya uno mejor) –

+0

Una prueba rápida que sí me permitió establecer campos finales privados mediante reflexión, por lo que podría tener 'valor de cadena final privado;' y en el constructor predeterminado hacer 'this.value = null' - - pero los documentos de Hibernate sugieren que se mantengan los modificadores finales (también para la clase) para habilitar proxying/lazy-loading: http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/persistent -classes.html # persistent-classes-pojo-final –

+0

Creo que sería una buena solución rápida. Pero preferiría una carpeta explícita para el desarrollo de EE. –

7

Probablemente la solución más fácil que funciona con versiones un poco más antiguas como 3.5 de Hibernate es implementar org.hibernate.usertype.UserType. Hay un buen número de métodos en ella, pero para los tipos inmutables que se puede extraer la mayor parte de ellos a superclase común:

package com.acme; 

import java.io.Serializable; 

import org.hibernate.usertype.UserType; 

public abstract class AbstractImmutableType 
    implements UserType { 

public AbstractImmutableType() { 
    super(); 
} 

public boolean isMutable() { 
    return false; 
} 

public Serializable disassemble(Object value) { 
    return (Serializable) value; 
} 

public Object assemble(Serializable cached, Object owner) { 
    return cached; 
} 

public Object deepCopy(Object value) { 
    return value; 
} 

public Object replace(Object original, Object target, 
    Object owner) { 
    return original; 
} 

public boolean equals(Object x, Object y) { 
    if (x != null && y != null) { 
    return x.equals(y); 
    } 
    // Two nulls are equal as well 
    return x == null && y == null; 
} 

public int hashCode(Object x) { 
    if (x != null) { 
    return x.hashCode(); 
    } 
    return 0; 
} 
} 

Y usted puede utilizar de esta manera:

package com.acme; 

import java.sql.PreparedStatement; 
import java.sql.ResultSet; 
import java.sql.SQLException; 
import java.sql.Types; 

public class CurrencyType extends AbstractImmutableType { 

public static final String TYPE = "com.acme.CurrencyType"; 

private static final int[] SQL_TYPES = { 
    Types.VARCHAR 
}; 

public CurrencyType() { 
    super(); 
} 

public Object nullSafeGet(ResultSet rs, String[] names, 
    Object owner) throws SQLException { 
    String value = rs.getString(names[0]); 
    if (rs.wasNull()) { 
    return null; 
    } 
    return Currency.valueOf(value); 
} 

public void nullSafeSet(PreparedStatement st, Object value, 
    int index) throws SQLException { 
    if (value != null) { 
    st.setString(index, ((Currency)value).getCode()); 
    } else { 
    st.setNull(index, SQL_TYPES[0]); 
    } 
} 

public Class<?> returnedClass() { 
    return Currency.class; 
} 

public int[] sqlTypes() { 
    return SQL_TYPES; 
} 
} 

más larga explicación de este código usted puede encontrar here

+0

Gracias. Voy a mirar. –

Cuestiones relacionadas