java - 如何在 Spring Boot 中完全禁用 Hibernate 异常
问题描述
我有一个情况。我正在创建一个 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 {
...
解决方案
异常表明hibernate拒绝初始化自己,所以这不是一个异常处理问题,但是你必须以某种方式说服hibernate进行初始化,即使它觉得鉴别器是“危险的”。
搜索hibernate的源代码发现这个异常被抛出
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 总是会抛出这样的异常。没有办法防止更改休眠的源代码。
此方法从以下位置调用:
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 也不能与缺少鉴别器值的实体一起使用。(顺便说一句,在过去的 7 年里,hibernate 团队没有对此采取任何行动)
解决此问题的唯一方法是更改实体、更改休眠或使用另一个 JPA 实现。
推荐阅读
- mysql - MySql如何根据同一行中的主节点获取名称?
- python - 如何修复 tensorflow 中的“ValueError:空训练数据”错误
- javafx - 如何在 JavaFX 中的多个地方使用相同的组件?
- rust - 为什么我的 FFI 函数的第二次调用无法匹配字符串比较?
- firebase - Firestore 描述语言
- tensorflow - 2 个 IndexedSlices 不支持高效的 allreduce
- node.js - 我无法从 Watson Discovery 收到任何答复
- javascript - 有没有办法将 CSS 类应用于 Openlayers 5.3 功能?
- ios - 我可以将斜体和/或粗体文本上传到我的 firebase 数据库吗?
- google-apps-script - 在函数中实现 vlookup 和 match