加载spring-boot和spring-data-jpa时,Hibernate无法加载JPA 2.1转换器 [英] Hibernate fails to load JPA 2.1 Converter when loaded with spring-boot and spring-data-jpa

查看:83
本文介绍了加载spring-boot和spring-data-jpa时,Hibernate无法加载JPA 2.1转换器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用于UUID的自定义转换器,将其转换为字符串而不是二进制:

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


$ / code $ / pre
$ b $转换器(我还有一些特别的时间/日期处理)驻留在库.jar文件中。



然后我在.jar文件中有实体。像这样:

  package de.kaiserpfalzEdv.office.core.security; 

导入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

公共类SecurityTicket实现Serializable {
private final static ZoneId TIMEZONE = ZoneId.of(UTC);
私有最终静态长DEFAULT_TTL = 600L;
私人最终静态长DEFAULT_RENEWAL = 600L;

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

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name =account_id_,nullable = false,updatable = false,unique = true)
私人账户;

@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(){
}

$ b公共SecurityTicket(@NotNull最终帐户){
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 after + now.toString());

返回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()));

返回Collections.unmodifiableSet(result);
}

public Set< String> getEntitlements(){
返回Collections.unmodifiableSet(new HashSet<>());


$ b @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;
返回新的EqualsBuilder()
.append(this.id,rhs.id)
.isEquals();

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


@覆盖
公共字符串的ToString(){
返回新ToStringBuilder(这一点,ToStringStyle.SHORT_PREFIX_STYLE)
.append( id,id)

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






$ b当通过maven和testng运行集成测试时,很好。但是当我启动应用程序时(第三个.jar文件),我得到了一个令人讨厌的异常,它归结为:

  :org.hibernate.HibernateException:列id_中的kpoffice.tickets中的列类型错误。实测值:VARCHAR,预期:二进制(50)
。在org.hibernate.mapping.Table.validateColumns(Table.java:372)
。在org.hibernate.cfg.Configuration.validateSchema(Configuration.java: 1338)
。在org.hibernate.tool.hbm2ddl.SchemaValidator.validate(SchemaValidator.java:175)
。在org.hibernate.internal.SessionFactoryImpl<初始化>(SessionFactoryImpl.java:525)$在org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1859)b
$ b。在org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl $ 4.perform(EntityManagerFactoryBuilderImpl.java:852)
在org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl $ 4.perform(EntityManagerFactoryBuilderImpl.java:845)$ b $在org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:398)$ b b $ b在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在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1562)

... 120更

转换的autoApply不起作用。我尝试将转换器注释到类和属性本身。但转换器没有使用。但是当我通过hibernate特定注释hibernate添加hibernate UUID类型时,抱怨它不能为同一个属性提供转换器和hibernate类型定义。所以hibernate读取转换器配置。



使用envers时,JPA 2.1转换器不起作用。但我在我的软件中不使用envers。



我希望有人知道我在做什么错误...

解决方案



JPA 2.1转换器不适用于 @Id 带注释的属性。



谢谢Andy。


I have a custom converter for UUID to transfer it to a string instead a binary:

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

The converters (i have some other espacially for time/date handling) reside in a library .jar file.

Then I have entities in a .jar file. Like this one:

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

When running integration tests via maven and testng the database works quite fine. But when I start the application (the third .jar file), I get a nasty exception which boils down to:

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

The autoApply of convert does not work. I tried to annotate the converter to the class and to the attribute itself. But the converter is not used. But when I added the hibernate UUID type via hibernate specific annotation hibernate complaint that it can't have a converter and a hibernate type definition for the same attribute. So hibernate reads the converter configuration.

When using envers, the JPA 2.1 converter don't work. But I don't use envers in my software.

I hope there is someone out there who knows what I'm doing wrong ...

解决方案

Andy Wilkinson gave the correct answer. Reading the spec helps in a lot of times.

JPA 2.1 Converters are not applied to @Id annotated attributes.

Thank you Andy.

这篇关于加载spring-boot和spring-data-jpa时,Hibernate无法加载JPA 2.1转换器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆