2015-02-05 9 views
14

Ho un convertitore personalizzato per UUID per trasferirla ad una stringa invece un binario:Hibernate non carica JPA 2.1 convertitore quando caricato con molla-avvio e la primavera-data-APP

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

I convertitori (i avere un altro espacially per la gestione di data/ora) risiedere in un file .jar libreria.

poi ho entità in un file .jar. Come questo:

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

Durante l'esecuzione di test di integrazione con Maven e TestNG il database funziona abbastanza bene. Ma quando inizio l'applicazione (il terzo file .jar), ottengo un'eccezione brutto, che si riduce a:

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 

La stazionamento automatico del convertito non funziona. Ho provato ad annotare il convertitore alla classe e all'attributo stesso. Ma il convertitore non è usato. Ma quando ho aggiunto il tipo UUID di ibernazione tramite hibernate hibernate, ho notato che non può avere un convertitore e una definizione del tipo di ibernazione per lo stesso attributo. Così ibernato legge la configurazione del convertitore.

Quando si utilizza envers, il convertitore JPA 2.1 non funzionano. Ma io non uso envers nel mio software.

spero che ci sia qualcuno là fuori che sa quello che sto facendo male ...

+6

La specifica JPA afferma esplicitamente che la conversione non verrà applicata all'attributo ID di un'entità. Forse puoi rimuovere le annotazioni di conversione JPA e provare invece ad utilizzare un approccio specifico di Hibernate? –

+0

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

risposta

10

Andy Wilkinson gave the correct answer. Leggendo le specifiche aiuta in un sacco di volte.

JPA 2.1 convertitori non sono applicati ai @Id attributi annotati.

Grazie Andy.

+0

[JSR-000.338 JavaTM Persistence 2.1] (https://jcp.org/aboutJava/communityprocess/final/jsr338/index.html) capitolo 11.1.10 Converti annotazione, _La annotazione Converti non deve essere utilizzata per specificare la conversione di quanto segue: ** Id attributi ** (compresi gli attributi di id incorporati e identità derivate), attributi di versione, relazione attributi e_ –

+0

Secondo http: //www.nailedtothex.org/roller/kyle/entry/using-jpa-2-1-attributeconverter, i convertitori dovrebbero funzionare con EmbeededId. Tuttavia, nel mio caso, fallisce anche: https://stackoverflow.com/questions/48188365/hibernate-no-type-name-with-attributeconverter-on-map Qualche suggerimento? – nimo23

+0

Se si leggono le specifiche (citate da Tom), si vede che le specifiche non includono gli attributi di un ID incorporato. Quindi, se ha funzionato, questa è un'estensione proprietaria su cui non devi fare affidamento. – klenkes74

0

Un'altra opzione è quella di incorporare la logica di conversione in getter alternativi/setter, in questo modo:

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

... 

Il @Transient annotazione dirà APP a ignorare questo getter in modo che non credo che ci sia una proprietà UUID separata. È inelegante, ma ha funzionato con me usando JPA su classi con un UUID come PK. Corri il rischio che altri codici impostino valori errati tramite il metodo setId (String), ma sembra l'unica soluzione. Potrebbe essere possibile proteggere questo metodo/privato?

Mentre normale codice Java sarebbe in grado di distinguere al setter con lo stesso nome in base a diversi tipi di argomento, APP si lamentano se non li nome diverso.

È fastidioso che JPA non supporta convertitori in Ids o che non segue la convenzione JAXB di non richiedere convertitori per classi con metodi di conversione standard (es. ToString/fromstring, intValue/parseInt, ecc).

Problemi correlati