【问题标题】:how to completely disable Hibernate exceptions within Spring Boot如何在 Spring Boot 中完全禁用 Hibernate 异常
【发布时间】:2018-11-05 07:28:24
【问题描述】:

我有一个情况。我正在创建一个 Spring Boot 项目来与一堆旧版 OpenJPA 实体进行交互。

问题在于,由于 Spring Boot Starter Data JPA 使用 Hibernate 作为基础,Spring 应用程序会抛出 Hibernate 异常,例如:

org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]
: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: 
Using default @DiscriminatorValue for a discriminator of type CHAR is not safe

现在,虽然可以通过更新实体代码轻松解决此问题,但我们无法触摸或更改该代码。我们必须保持原样 - 不符合 Hibernate 标准。

那么,在 Spring 中是否有任何方法来禁用这样的 Hibernate 异常,以便我们可以接受不符合 Hibernate 标准的实体并抛出 Hibernate 异常?

我们的代码设计很简单,是标准的 Service-Repository 设置。例如:

PersonRepository.java:

public interface PersonRepository extends CrudRepository<Person, Long> {

    Person findFirstByPersonSsnSsn(String ssn);

    Person findFirstByPersonId(Integer personId);

}

PersonDomainService.java:

public interface PersonDomainService {

    Person findPersonBySsn(String ssn); 

    public Person findPerson(Integer id);
}

PersonDomainServiceImpl.java:

@Service
@Transactional
public class PersonDomainServiceImpl implements PersonDomainService {

    private PersonRepository personRepository;

    @Autowired
    public PersonDomainServiceImpl(PersonRepository personRepository) {
        this.personRepository = personRepository;
    }

    @Transactional(readOnly=true)
    public Person findPersonBySsn(String ssn) {
        Person person = personRepository.findFirstByPersonSsnSsn(ssn);

        System.out.println(person.getAddresses().size());
        System.out.println(person.getPhones().size());

        return person;

    }

    @Transactional(readOnly=true)
    public Person findPerson(Integer id) {
        // TODO Auto-generated method stub
        Person person = personRepository.findFirstByPersonId(id);

        System.out.println(person.getAddresses().size());
        System.out.println(person.getPhones().size());

        return person;
    }   
}

而且实体非常标准:

@Entity
@Table(name = "PERSON", schema = "SCHEMA")
public class Person implements Serializable, EntityWithAuditInfo {
...

【问题讨论】:

标签: java spring hibernate spring-boot openjpa


【解决方案1】:

异常表明hibernate拒绝初始化自己,所以这不是一个异常处理问题,但你必须以某种方式说服hibernate进行初始化,即使它觉得鉴别器是“危险的”。

搜索hibernate's source code 发现这个异常是thrown at

public void bindDiscriminatorValue() {
    if ( StringHelper.isEmpty( discriminatorValue ) ) {
        Value discriminator = persistentClass.getDiscriminator();
        if ( discriminator == null ) {
            persistentClass.setDiscriminatorValue( name );
        }
        else if ( "character".equals( discriminator.getType().getName() ) ) {
            throw new AnnotationException(
                    "Using default @DiscriminatorValue for a discriminator of type CHAR is not safe"
            );
        }
        else if ( "integer".equals( discriminator.getType().getName() ) ) {
            persistentClass.setDiscriminatorValue( String.valueOf( name.hashCode() ) );
        }
        else {
            persistentClass.setDiscriminatorValue( name ); //Spec compliant
        }
    }
    else {
        //persistentClass.getDiscriminator()
        persistentClass.setDiscriminatorValue( discriminatorValue );
    }
}

如你所见,如果没有为 char 类型的鉴别器提供 discriminatorValue,hibernate 总是会抛出这样的异常。没有办法防止更改休眠的源代码。

这个方法is invoked from

public void bindEntity() {
    persistentClass.setAbstract( annotatedClass.isAbstract() );
    persistentClass.setClassName( annotatedClass.getName() );
    persistentClass.setJpaEntityName(name);
    //persistentClass.setDynamic(false); //no longer needed with the Entity name refactoring?
    persistentClass.setEntityName( annotatedClass.getName() );
    bindDiscriminatorValue();

    persistentClass.setLazy( lazy );
    if ( proxyClass != null ) {
        persistentClass.setProxyInterfaceName( proxyClass.getName() );
    }
    persistentClass.setDynamicInsert( dynamicInsert );
    persistentClass.setDynamicUpdate( dynamicUpdate );

    ... and 170 more lines of code to parse all other annotations in this class
}

从缺少 catch 块我们可以看出,如果无法确定鉴别器值,hibernate 会中止解析该实体。

因此,当鉴别器是 char 类型时,即使是最新版本的 hibernate 也不能与缺少鉴别器值的实体一起使用。 (顺便说一句,有一个 open issue about this 在过去 7 年里没有被 hibernate 团队采取行动)

解决此问题的唯一方法是更改​​实体、更改休眠或使用其他 JPA 实现。

【讨论】:

  • 不幸的是,我相信这是最正确的答案。由于我们无法更改实体,而且更改 Hibernate 并不明智,我将不得不使用不同的 Spring JPA 实现。
猜你喜欢
  • 2020-03-07
  • 1970-01-01
  • 1970-01-01
  • 2019-02-27
  • 2015-01-02
  • 2020-02-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多