【问题标题】:hibernate validation not triggering on persist?休眠验证不会触发持久化?
【发布时间】:2020-07-21 13:02:45
【问题描述】:

通读这篇文章

https://thorben-janssen.com/automatically-validate-entities-with-hibernate-validator/#Automatic_Validation_upon_Lifecycle_Events

有这样的引用“当 Hibernate 或任何其他 JPA 实现触发预持久、预更新或预删除生命周期事件时,验证会自动执行”

所以,我期待失败会持续存在。我添加了 hibernate-validator 但没有添加 el 的东西(没有它似乎不会失败......不知道为什么它需要 el)。

在某处是否有一个运行中的简单 github 示例我可以比较以跟踪此问题?

版本:

  • org.hibernate:hibernate-core:5.4.10.Final
  • org.hibernate:hibernate-validator:5.4.3.Final
  • org.glassfish:javax.el:3.0.1-b09

我只是从 javax.validation.constraints 中添加了一堆这样的注释。并且预计 entityManager.persist 会失败,但它可以工作并将 bean 插入数据库中。

@NotNull
@NotBlank
private String phone;
@NotNull
@NotBlank
private String name;
@NotNull
@NotBlank
private String firstName;

我使用一个类

public class DbSettingsInMemory implements PersistenceUnitInfo

public class DbSettingsInProd implements PersistenceUnitInfo

而不是persistence.xml。不确定这是否真的很重要。我们这样做是为了避免在 IDE 等中出现的查找 persistence.xml 的类路径问题,并且这样的体验更加流畅。这是我们的完整文件,以防它无法工作

public class DbSettingsInMemory implements PersistenceUnitInfo {

    private static final Logger log = LoggerFactory.getLogger(DbSettingsInMemory.class);
    
    private Properties properties = new Properties();

    private HikariDataSource dataSource;
    
    @Inject
    public DbSettingsInMemory(MeterRegistry metrics) {
        //<!-- property name="javax.persistence.jdbc.driver" value="org.h2.Driver" /-->
        //properties.setProperty("javax.persistence.jdbc.driver", "net.sf.log4jdbc.DriverSpy");
        //properties.setProperty("javax.persistence.jdbc.url", "jdbc:log4jdbc:h2:mem:test");
        //properties.setProperty("javax.persistence.jdbc.user", "sa");
        //properties.setProperty("javax.persistence.jdbc.password", "");
        
        properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
        properties.setProperty("hibernate.hbm2ddl.auto", "update");
        properties.setProperty("hibernate.show_sql", "false");
        properties.setProperty("hibernate.format_sql", "false");
        properties.setProperty("hibernate.transaction.flush_before_completion", "true");
        //properties.setProperty("hibernate.connection.provider_class", "org.hibernate.hikaricp.internal.HikariCPConnectionProvider");
        
        HikariConfig config = new HikariConfig();
        config.setDriverClassName("net.sf.log4jdbc.DriverSpy");
        config.setJdbcUrl("jdbc:log4jdbc:h2:mem:test");
        config.setUsername("sa");
        config.setPassword("");
        config.setMetricRegistry(metrics);
         
        dataSource = new HikariDataSource(config);
        
    }
    
    @Override
    public String getPersistenceUnitName() {
        return "inmemory";
    }

    @Override
    public String getPersistenceProviderClassName() {
        return "org.hibernate.jpa.HibernatePersistenceProvider";
    }

    @Override
    public PersistenceUnitTransactionType getTransactionType() {
        return PersistenceUnitTransactionType.RESOURCE_LOCAL;
    }

    @Override
    public DataSource getJtaDataSource() {
        return null;
    }

    @Override
    public DataSource getNonJtaDataSource() {
        return dataSource;
    }

    @Override
    public List<String> getMappingFileNames() {
        return null;
    }

    @Override
    public List<URL> getJarFileUrls() {
        return null;
    }

    /**
     * root of where to scan from.  MAKE this a very small scope so scanning is very very quick
     */
    @Override
    public URL getPersistenceUnitRootUrl() {
        String name = DbSettingsInMemory.class.getSimpleName() + ".class";
        URL url = DbSettingsInMemory.class.getResource(name);
        String file = url.getFile();
        int length = file.length() - name.length(); 
        String root = file.substring(0, length);
        try {
            URL rootUrl = new URL(url.getProtocol(), url.getHost(), root);
            log.info("RootURL for scanning="+rootUrl);
            return rootUrl;
        } catch (MalformedURLException e) {
            throw new RuntimeException("Bug", e);
        }
    }

    @Override
    public List<String> getManagedClassNames() {
        return null;
    }

    @Override
    public boolean excludeUnlistedClasses() {
        return false;
    }

    @Override
    public SharedCacheMode getSharedCacheMode() {
        return SharedCacheMode.ENABLE_SELECTIVE;
    }

    @Override
    public ValidationMode getValidationMode() {
        return ValidationMode.NONE;
    }

    @Override
    public Properties getProperties() {
        return properties;
    }

    @Override
    public String getPersistenceXMLSchemaVersion() {
        return null;
    }

    @Override
    public ClassLoader getClassLoader() {
        throw new UnsupportedOperationException("Not supported.  webpieces needs to pass in Development Classloader");
    }

    @Override
    public void addTransformer(ClassTransformer transformer) {
    }

    @Override
    public ClassLoader getNewTempClassLoader() {
        return null;
    }
    
    
    
}

编辑: 我想到了要添加的其他内容,至少要确保验证在像这样调用持久/刷新之前正常工作

    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    Validator validator = factory.getValidator();
    Set<ConstraintViolation<UserDbo>> list = validator.validate(entity);
    
    log.info("list="+list);
    
    Em.get().persist(entity);
    Em.get().flush();

我在列表中正确地看到了 4 个违规行为,但仍然存在并且刷新成功 :(.

从调试器...

深入研究休眠代码,我找到了一个 BeanValidationEventListener.java 但它似乎只是通过依赖注入连接的??不太确定,但由于某种原因它没有连接到休眠,但需要以某种方式休眠。

谢谢, 院长

【问题讨论】:

  • 您使用的是 spring-boot,如果是,是什么版本?您声明了哪些与验证相关的依赖项?
  • @KavithakaranKanapathippillai 不,只在应用程序中使用休眠。 'org.hibernate:hibernate-core:5.4.10.Final'、'org.hibernate:hibernate-validator:6.1.5.Final'和'org.glassfish:javax.el:3.0.1-b09',

标签: hibernate hibernate-validator


【解决方案1】:

首先,我建议您在 EMF 的属性中将 javax.persistence.validation.mode 设置为 callback

这会强制集成 Bean Validation,并可能会为您提供正确的错误消息。否则,如果 Bean Validation 初始化失败,您不会收到任何错误消息。

话虽如此,它很有可能无法解决您的问题。你如何初始化你的SessionFactoryImpl

因为如果你没有 CDI 或 Spring 集成,你必须手动连接 Hibernate ORM 和 Hibernate Validator。

一个好的入口点是 SessionFactoryOptionsBuilder#applyValidatorFactory()。

【讨论】:

  • 我终于想通了,并发布了答案@GuillaumeSmet。尽管我已经尝试过,但我对您的尝试表示赞成,并且一旦发现该宝石就忘记了编辑我的帖子。谢谢!!!
【解决方案2】:

我终于明白了。事实证明,在上面的文件中,即使在我将属性设置为 callback 或 auto 或 ddl 甚至 auto, ddl 之后,也有这个方法杀死了它。

@Override
public ValidationMode getValidationMode() {
    return ValidationMode.NONE;
}

在休眠文档中,它说你可以做回调,ddl 所以你得到两种类型,但我猜做一个 PU 只允许一种类型:(。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多