2015-02-05 261 views
14

我有一個自定義轉換器加載到加載JPA 2.1轉換爲UUID將其傳送到一個字符串,而不是一個二值:休眠失敗當與彈簧引導和彈簧數據JPA

package de.kaiserpfalzEdv.commons.jee.db; 
import javax.persistence.AttributeConverter; 
import javax.persistence.Converter; 
import java.util.UUID; 

@Converter(autoApply = true) 
public class UUIDJPAConverter implements AttributeConverter<UUID, String> { 
    @Override 
    public String convertToDatabaseColumn(UUID attribute) { 
     return attribute.toString(); 
    } 

    @Override 
    public UUID convertToEntityAttribute(String dbData) { 
     return UUID.fromString(dbData); 
    } 
} 

該轉換器(ⅰ有其他一些特別的時間/日期處理)駐留在庫.jar文件中。

然後我有一個.jar文件中的實體。像這樣:

package de.kaiserpfalzEdv.office.core.security; 

import de.kaiserpfalzEdv.commons.jee.db.OffsetDateTimeJPAConverter; 
import de.kaiserpfalzEdv.commons.jee.db.UUIDJPAConverter; 
import org.apache.commons.lang3.builder.EqualsBuilder; 
import org.apache.commons.lang3.builder.HashCodeBuilder; 
import org.apache.commons.lang3.builder.ToStringBuilder; 
import org.apache.commons.lang3.builder.ToStringStyle; 

import javax.persistence.Column; 
import javax.persistence.Convert; 
import javax.persistence.Entity; 
import javax.persistence.FetchType; 
import javax.persistence.Id; 
import javax.persistence.JoinColumn; 
import javax.persistence.ManyToOne; 
import javax.persistence.Table; 
import javax.validation.constraints.NotNull; 
import java.io.Serializable; 
import java.time.OffsetDateTime; 
import java.time.ZoneId; 
import java.util.Collections; 
import java.util.HashSet; 
import java.util.Set; 
import java.util.UUID; 

@Entity 
@Table(
     name = "tickets" 
) 
public class SecurityTicket implements Serializable { 
    private final static ZoneId TIMEZONE = ZoneId.of("UTC"); 
    private final static long DEFAULT_TTL = 600L; 
    private final static long DEFAULT_RENEWAL = 600L; 

    @Id @NotNull 
    @Column(name = "id_", length=50, nullable = false, updatable = false, unique = true) 
    @Convert(converter = UUIDJPAConverter.class) 
    private UUID id; 

    @ManyToOne(fetch = FetchType.EAGER) 
    @JoinColumn(name = "account_id_", nullable = false, updatable = false, unique = true) 
    private Account account; 

    @Convert(converter = OffsetDateTimeJPAConverter.class) 
    @Column(name = "created_", nullable = false, updatable = false) 
    private OffsetDateTime created; 

    @Convert(converter = OffsetDateTimeJPAConverter.class) 
    @Column(name = "validity_", nullable = false, updatable = false) 
    private OffsetDateTime validity; 


    @Deprecated 
    public SecurityTicket() { 
    } 


    public SecurityTicket(@NotNull final Account account) { 
     id = UUID.randomUUID(); 
     this.account = account; 
     created = OffsetDateTime.now(TIMEZONE); 
     validity = created.plusSeconds(DEFAULT_TTL); 
    } 


    public void renew() { 
     validity = OffsetDateTime.now(TIMEZONE).plusSeconds(DEFAULT_RENEWAL); 
    } 

    public boolean isValid() { 
     OffsetDateTime now = OffsetDateTime.now(TIMEZONE); 

     System.out.println(validity.toString() + " is hopefully after " + now.toString()); 

     return validity.isAfter(now); 
    } 

    public UUID getId() { 
     return id; 
    } 

    public OffsetDateTime getValidity() { 
     return validity; 
    } 

    public String getAccountName() { 
     return account.getAccountName(); 
    } 

    public String getDisplayName() { 
     return account.getDisplayName(); 
    } 

    public Set<String> getRoles() { 
     HashSet<String> result = new HashSet<>(); 

     account.getRoles().forEach(t -> result.add(t.getDisplayNumber())); 

     return Collections.unmodifiableSet(result); 
    } 

    public Set<String> getEntitlements() { 
     return Collections.unmodifiableSet(new HashSet<>()); 
    } 


    @Override 
    public boolean equals(Object obj) { 
     if (obj == null) { 
      return false; 
     } 
     if (obj == this) { 
      return true; 
     } 
     if (obj.getClass() != getClass()) { 
      return false; 
     } 
     SecurityTicket rhs = (SecurityTicket) obj; 
     return new EqualsBuilder() 
       .append(this.id, rhs.id) 
       .isEquals(); 
    } 

    @Override 
    public int hashCode() { 
     return new HashCodeBuilder() 
       .append(id) 
       .toHashCode(); 
    } 


    @Override 
    public String toString() { 
     return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) 
       .append("id", id) 

       .append("account", account) 
       .append("validity", validity) 
       .toString(); 
    } 
} 

當通過maven和testng運行集成測試時,數據庫工作得很好。但是,當我啓動應用程序(第三.jar文件),我得到一個討厭的例外,這可以歸結爲:

Caused by: org.hibernate.HibernateException: Wrong column type in kpoffice.tickets for column id_. Found: varchar, expected: binary(50) 
     at org.hibernate.mapping.Table.validateColumns(Table.java:372) 
     at org.hibernate.cfg.Configuration.validateSchema(Configuration.java:1338) 
     at org.hibernate.tool.hbm2ddl.SchemaValidator.validate(SchemaValidator.java:175) 
     at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:525) 
     at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1859) 
     at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:852) 
     at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:845) 
     at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:398) 
     at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:844) 
     at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60) 
     at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:343) 
     at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:318) 
     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1625) 
     at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1562) 
     ... 120 more 

轉換的自動應用不起作用。我嘗試將轉換器註釋到類和屬性本身。但轉換器沒有使用。但是當我通過hibernate特定註釋hibernate添加hibernate UUID類型時,抱怨它不能爲同一個屬性提供轉換器和hibernate類型定義。所以hibernate讀取轉換器配置。

使用envers時,JPA 2.1轉換器不起作用。但我不使用我的軟件中的envers。

我希望有一個人在那裏誰知道我做錯了什麼......

+6

JPA規範明確規定轉換不會應用於實體的id屬性。也許你可以刪除你的JPA轉換註釋,並使用Hibernate特定的方法來嘗試它? –

+0

http://stackoverflow.com/questions/39547615/convert-in-hibernate-not-working-with-spring-4-java-lang-nosuchmethoderror-org/39551331#39551331 –

回答

10

Andy Wilkinson gave the correct answer.讀規範幫助了很多次。

JPA 2.1轉換器不適用於@Id帶註釋的屬性。

謝謝安迪。

+0

[JSR-000338 JavaTM Persistence 2.1]( https://jcp.org/aboutJava/communityprocess/final/jsr338/index.html)第11.1章。10轉換註釋,不應使用Convert註釋指定以下內容的轉換:** Id屬性** (包括嵌入ID和派生標識的屬性),版本屬性,關係 屬性和_ –

+0

根據http: //www.nailedtothex.org/roller/kyle/entry/using-jpa-2-1-attributeconverter,轉換器應該與EmbeededId一起工作。 但是,在我的情況下,它也失敗了:https://stackoverflow.com/questions/48188365/hibernate-no-type-name-with-attributeconverter-on-map 有什麼建議嗎? – nimo23

+0

如果您閱讀規格(由Tom引用),您會看到規格不包含嵌入ID的屬性。所以如果它有效,那是你不應該指望的專有擴展。 – klenkes74

0

另一種選擇是嵌入轉換邏輯的替代getter/setter方法,就像這樣:

public class SecurityTicket implements Serializable 
{ 
... 
private UUID id; 

@Transient 
public UUID getUUID() 
{ 
    return id; 
} 

@Id @NotNull 
@Column(name = "id_", length=50, nullable = false, updatable = false, unique = true) 
public String getID() 
{ 
    return id.toString(); 
} 

public void setUUID(UUID id) 
{ 
    this.id = id; 
} 

public void setID(String id) 
{ 
    this.id = UUID.fromString(id); 
} 

... 

@Transient註釋會告訴JPA忽略此吸氣所以也沒有覺得有一個單獨的UUID屬性。這是不雅的,但它對我使用JPA在UUID作爲PK類。你通過setId(String)方法冒着其他代碼設置錯誤值的風險,但它似乎是唯一的解決方法。這種方法可能被保護/私密?

雖然正常的Java代碼可以根據不同的參數類型區分爲具有相同名稱的設置器,但如果您不以不同的名稱命名它們,JPA會投訴。

令人討厭的是,JPA不支持Ids上的轉換器,或者它不遵循JAXB約定,不需要轉換器用於具有標準轉換方法的類(即toString/fromString,intValue/parseInt等)。