2015-02-05 9 views
14

mam niestandardową konwertera dla UUID do przeniesienia go na sznurku zamiast binarnych:hibernacji nie można załadować JPA 2.1 Converter przy obciążeniu sprężyny-boot i wiosna-data-WZP

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

konwerterów (i mieć inne, w szczególności do obsługi czasu/daty) znajdują się w pliku biblioteki .jar.

Następnie mam podmioty w pliku .jar. Podobnie jak ten:

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

Podczas uruchamiania testów integracji za pomocą programu maven i testng baza danych działa całkiem dobrze. Ale kiedy uruchomić aplikację (trzeci plik .jar), pojawia się przykry wyjątek, który sprowadza się do:

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 

automatycznie włączany konwertowania nie działa. Próbowałem dodać adnotację do konwertera do klasy i do samego atrybutu. Ale konwerter nie jest używany. Ale kiedy dodałem typ UUID w hibernacji za pomocą hibernacji skargi hibernacji, nie można mieć konwertera i definicji typu hibernacji dla tego samego atrybutu. Więc hibernacja odczytuje konfigurację konwertera.

Podczas korzystania z envers, konwerter JPA 2.1 nie działa. Ale nie używam envers w moim oprogramowaniu.

Mam nadzieję, że jest ktoś, kto wie, co robię źle ...

+6

Specyfikacja JPA wyraźnie stwierdza, że ​​konwersja nie zostanie zastosowana do atrybutu id jednostki. Być może możesz usunąć adnotacje o konwersjach JPA i wypróbować je za pomocą podejścia opartego na Hibernate? –

+0

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

Odpowiedz

10

Andy Wilkinson gave the correct answer. Czytając specyfikację pomaga w wielu czasach.

JPA 2.1 Konwertery nie są stosowane do atrybutów o adnotacji z @Id.

Dziękuję Andy.

+0

[JSR-000338 JavaTM Persistence 2.1] (https://jcp.org/aboutJava/communityprocess/final/jsr338/index.html) rozdział 11.1.10 Konwertuj adnotację, Konwersja adnotacji nie powinna być używana do określania konwersji następujących elementów: ** Atrybuty Id ** (w tym atrybuty identyfikatorów wbudowanych i wyprowadzonych tożsamości), atrybuty wersji, atrybuty relacji i –

+0

Według http: //www.nailedtothex.org/roller/kyle/entry/using-jpa-2-1-attributeconverter, konwertery powinny działać z EmbeededId. Jednak w moim przypadku, nie jest on także: https://stackoverflow.com/questions/48188365/hibernate-no-type-name-with-attributeconverter-on-map Wszelkie sugestie? – nimo23

+0

Jeśli czytasz specyfikację (cytowaną przez Toma), widzisz, że specyfikacje nie zawierają atrybutów osadzonego identyfikatora. Jeśli to zadziałało, jest to rozszerzenie własnościowe, na które nie należy liczyć. – klenkes74

0

Innym rozwiązaniem jest osadzenie logiki konwersji w alternatywnych pobierające/ustawiające, tak:

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 adnotacja pokaże JPA zignorować ten getter, więc nie myślę, że jest odrębną własność UUID. Jest nieelegancki, ale zadziałało dla mnie przy użyciu JPA na zajęciach z UUID jako PK. Występuje ryzyko, że inny kod ustawi złe wartości za pomocą metody setId (String), ale wydaje się jedynym rozwiązaniem. Być może ta metoda może być chroniona/prywatna?

Podczas gdy normalny kod Java byłby w stanie odróżnić od setterów o tej samej nazwie w oparciu o różne typy argumentów, JPA złoży skargę, jeśli nie nazwiesz ich inaczej.

To denerwujące, że JPA nie obsługuje Konwerterów przy identyfikatorach lub że nie jest zgodne z konwencją JAXB, która nie wymaga konwerterów dla klas ze standardowymi metodami konwersji (np. ToString/fromString, intValue/parseInt, itp.).

Powiązane problemy