2015-02-05 6 views
14

У меня есть собственный конвертер для UUID, чтобы передать его в строку вместо двоичного файла:Hibernate не удается загрузить JPA 2.1 конвертера при загрузке с пружинным ботинке и пружинным-данными 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); 
    } 
} 

преобразователей (I имеют некоторые другие особенности для обработки времени/даты) находятся в файле .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 

autoApply конвертируя не работает. Я попытался аннотировать конвертер к классу и самому атрибуту. Но конвертер не используется. Но когда я добавил тип UUID спящего режима через жалобную запись с гибернацией, которая не может иметь конвертер и определение типа гибернации для того же атрибута. Таким образом, 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 2.1] (https://jcp.org/aboutJava/communityprocess/final/jsr338/index.html) глава 11.1.10 Convert Annotation, _ Convert annotation не следует использовать для указания преобразования следующего: ** Идентификационные атрибуты ** (включая атрибуты встроенных идентификаторов и полученных идентификаторов), атрибуты версии, отношения и_ –

+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

Если вы прочитали спецификации (цитируется Томом), вы увидите, что спецификации не включают атрибуты встроенного идентификатора. Так что если это сработало, это проприетарное расширение, на которое нельзя рассчитывать. – klenkes74

0

Другой вариант встроить логику преобразования в альтернативных геттеры/сеттеры, например, так:

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 не поддерживает Преобразователи на идентификаторах или что это не соответствует соглашению JAXB, не требующему преобразований для классов со стандартными методами преобразования (то есть toString/fromString, intValue/parseInt и т. Д.).

 Смежные вопросы

  • Нет связанных вопросов^_^