2011-08-28 27 views
15

是否有方法使用JPA映射不可變的Value對象,如電子郵件地址?不可變值對象和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(); 
    } 
} 

現在,我得到異常的實體保存

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) 

我想用最後的字段和Hibernate作爲JPA實現。

回答

13

你贏了」使用標準的JPA註解和一個可嵌入的對象可以做到這一點,因爲必須使用默認構造函數創建對象,並通過反射設置值。

但是,您可以使用Hibernate自定義類型。閱讀this part of the Hibernate reference documentation,其中有一個示例Money類型,該類型使用帶參數的構造函數實例化,因此可以是不可變的。

+0

非常感謝。它工作得很好。 –

3

對於JPA能夠通過反射創建對象,您必須有一個默認的構造函數,但它不必是公共的。我也喜歡讓我的領域保持最終狀態,但這可能會限制反思 - 你必須嘗試。

我建議丟棄最後一個字段修改和增加私人默認構造一個簡短的註釋(所以你還是知道爲什麼無操作構造有下週):

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

    private EmailAddress() { 
     // for JPA 
    } 

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

謝謝。但我希望有更好的一個) –

+0

一個快速測試,我沒有讓我通過反射設置私人最終字段 - 所以你可以有'私人最終的字符串值;'在默認的構造函數做'this.value = null' - - 但Hibernate文檔建議保留最終修飾符(也適用於類)以啓用代理/延遲加載:http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/persistent -classes.html#persistent-classes-pojo-final –

+0

我認爲這將是很好的快速解決方案。但我更喜歡顯式粘合劑來進行EE開發。 –

7

也許最簡單的解決方案是使用像3.5的Hibernate這樣的老版本來實現org.hibernate.usertype.UserType。 這裏面有不少方法,但爲不可變的類型,你可以提取他們中的大多數公共超:

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; 
} 
} 

而且你可以使用它像這樣:

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; 
} 
} 

更詳細的解釋這段代碼你可以找到here

+0

謝謝。我會看看。 –