【问题标题】:Spring MVC Handle Unique Constraint ViolationSpring MVC 处理唯一约束冲突
【发布时间】:2015-04-22 00:07:46
【问题描述】:

我有一个 Spring MVC 应用程序,它使用 Spring Data JPA 进行持久性,并将 Hibernate 作为我的 JPA 提供程序。我有一个对列具有唯一约束的数据库表,因此为什么保存相应的实体可能会导致违反唯一约束。我想检测这是否发生在我的服务层中,以便我可以向用户显示有意义的错误消息。下面是我的服务方式。

@Service
public class IndustryServiceImpl implements IndustryService {
    @Autowired
    private IndustryRepository industryRepository;

    @Override
    @Transactional
    public void add(Collection<Industry> industries) {
        this.industryRepository.save(industries);
        this.industryRepository.flush();
    }
}

我的 Spring Data JPA 存储库如下所示:

@Repository
public interface IndustryRepository extends JpaRepository<Industry, Integer> { }

现在,当发生唯一约束违规时,我得到以下异常(为简洁起见而缩短):

javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement
    ...

org.hibernate.exception.ConstraintViolationException: could not execute statement
    ...

org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "industry_name_unique_index"
  Detail: Key (name)=(SomeValue) already exists.
  ...

如您所见,“main”异常的类型为javax.persistence.PersistenceException,这是一个非常普遍的异常。理想情况下,我想捕获一个异常,告诉我错误是由于违反了唯一约束。因此我有几个问题:

  1. 异常应该是javax.persistence.PersistenceException 类型是否正确,还是因为没有启动Spring 异常转换?我可以配置 Spring 来给我更少的一般例外吗,例如DuplicateKeyException
  2. 如果此行为与缺少异常转换无关,那么您是否知道比调用e.getCause() 检测更具体错误的更好方法?我可以很容易地做到这一点,但这对我来说似乎不合适,原因有两个;首先,代码对我来说似乎更“脆弱”,其次,我还必须检查 JPA 提供程序特定的异常,因为嵌套异常的类型为 org.hibernate.exception.ConstraintViolationException。如果可能,我希望我的服务不知道我正在使用哪个 JPA 提供程序。

以下是我的持久性配置,以防万一。

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = { "com.example.company.repository", "com.example.account.repository" })
public class PersistenceConfig {
    @Autowired
    private LocalValidatorFactoryBean validator;

    @Value("${jndi.data.source}")
    private String dataSourceName;

    @Bean
    public DataSource dataSource() {
        final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
        dsLookup.setResourceRef(true);

        return dsLookup.getDataSource(this.dataSourceName);
    }

    @Bean
    public EntityManagerFactory entityManagerFactory() {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setGenerateDdl(false);
        vendorAdapter.setDatabase(Database.POSTGRESQL);
        vendorAdapter.setShowSql(true);

        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);
        String[] packagesToScan = new String[] { "com.example.company.entity", "com.example.account.entity" };
        factory.setPackagesToScan(packagesToScan);
        factory.setDataSource(this.dataSource());
        factory.setValidationMode(ValidationMode.NONE); // Prevents errors when using custom validators when persisting
        factory.afterPropertiesSet();

        return factory.getObject();
    }

    @Bean
    public JpaTransactionManager transactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(this.entityManagerFactory());

        return transactionManager;
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }
}

提前谢谢你!

【问题讨论】:

  • 我会说你应该明确地验证数据而不是依赖异常。
  • @AlanHay 是的,我实际上正在这样做,但是如果发生幻读,那么这种情况可能仍然会发生(尽管可能性很小) - 我不想锁定表.在其他情况下,我可能还需要这个问题的答案。 :-)
  • @AlanHay 你无法在繁忙的服务器中验证唯一的数据库约束
  • 很公平。无论如何,我已经在本地对此进行了测试,无论我是否在应用程序配置中实际注册了 PetPostProcessor,我都会返回以下内容,因此确实会发生翻译:org.springframework.dao.DataIntegrityViolationException > javax.persistence.PersistenceException > org.hibernate.exception .ConstraintViolationException
  • @AlanHay 嗯,奇怪的是这不是我得到的行为。如果您对可能出现的问题有任何想法,或者您希望我发布更多代码,请告诉我。非常感谢您抽出宝贵时间对此进行测试 - 谢谢!

标签: java spring spring-mvc exception-handling spring-data-jpa


【解决方案1】:

Spring 允许通过 @Repository 注释透明地应用异常转换。后处理器自动查找所有异常翻译器(PersistenceExceptionTranslator 接口的实现)并建议所有标有 @Repository 注释的 bean,以便发现的翻译器可以拦截并在抛出的异常上应用适当的翻译。

总而言之:您可以基于普通持久性技术的 API 和注解实现 DAO,同时仍然受益于 Spring 管理的事务、依赖注入和透明的异常转换(如果需要)到 Spring 的自定义异常层次结构。

`

<!-- Exception translation bean post processor -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

<bean id="myProductDao" class="product.ProductDaoImpl"/>

`

直接取自here

另外,你可以看看this

HTH。

【讨论】:

  • 是的,你是对的。但是我已经使用了@Repository注解,并且我还像上面的代码示例一样配置了异常翻译。所以它应该工作,对吧?
  • 是的,我想纯 JPADialect 符合您的需求。但话又说回来,我们认为正常的东西也是如此。将需要实施并查看。如果可能,会尝试这样做并回复您。
  • 谢谢,感谢您的帮助!
猜你喜欢
  • 2021-05-25
  • 1970-01-01
  • 1970-01-01
  • 2022-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-02
  • 1970-01-01
相关资源
最近更新 更多