Spring 启动 session.enableFilter 导致 AssertionFailure:table 未找到

Spring boot session.enableFilter cause AssertionFailure: table not found

我正在尝试使用 spring 引导和实体管理器访问 mysql 数据库。但是如果我使用 session.enableFilter 它会导致奇怪的异常。在互联网上尝试了很多搜索,但 none 有帮助。

DataSourceContext.java

@Configuration
@EnableTransactionManagement
public class DataSourceContext {
    public final static String DB_PREFIX = "P_";

    @Autowired
    Environment env;

    @Bean
    @Qualifier("dataSource")
    public DataSource dataSource(Properties hibernateProperties) {
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setUsername(env.getProperty("jdbc.username"));
        ds.setPassword(env.getProperty("jdbc.password"));
        ds.setUrl(env.getProperty("jdbc.url"));
        ds.setDriverClassName(env.getProperty("jdbc.driverClassName"));
        ds.setConnectionProperties(hibernateProperties);
        return ds;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(@Qualifier("dataSource") DataSource dataSource, Properties hibernateProperties) {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setGenerateDdl(true);

        final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource);
        em.setPackagesToScan("com.example.model");
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaProperties(hibernateProperties);
        em.setPersistenceUnitName("persistanceUnit");
        em.setPersistenceProviderClass(HibernatePersistenceProvider.class);
        em.afterPropertiesSet();

        return em;
    }

    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager tm = new JpaTransactionManager();
        tm.setEntityManagerFactory(entityManagerFactory);
        return tm;
    }

    @Bean
    public Properties hibernateProperties() {
        final Properties properties = new Properties();

        properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
        properties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
        properties.put("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
        properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
        properties.put("hibernate.default_schema", env.getProperty("hibernate.default_schema"));

        return properties;
    }
}

Aentity.java

@Entity
@Table(name = DataSourceContext.DB_PREFIX + "AENTITY")
@FilterDef(name = "statusFilter", defaultCondition = "STATUS = :status ", parameters = @ParamDef(name = "status", type = "string"))
@Filters({@Filter(name = "statusFilter", condition = "STATUS = :status")})
public class Aentity implements Serializable {
    private Long id;
    private EntityStatus status;
    private Date createdDate;
    private Date updatedDate;
    private Date deletedDate;
    private Long version;

    // getters and setter with column annotations.
}

Adao.java

@Transactional(rollbackFor = Exception.class)
@Repository
public class Adao {

    @PersistenceContext
    protected EntityManager em;

    public EntityManager getEm() {
        /* exception lines */
        em.unwrap(Session.class)
            .enableFilter("statusFilter")
            .setParameter("status", EntityStatus.ACTIVE.name());

        return em;
    }

    public List<Aentity> findAll() {
        List resultList = getEm().createQuery("select distinct aentity from Aentity aentity")
                .getResultList();

        return resultList;
    }
}

pom.xml

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

日志;

----unrelated logs----
2017-04-26 13:32:36.145  INFO 60417 --- [           main] o.h.h.i.QueryTranslatorFactoryInitiator  : HHH000397: Using ASTQueryTranslatorFactory
2017-04-26 13:32:36.187 ERROR 60417 --- [           main] org.hibernate.AssertionFailure           : HHH000099: an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: Table myschema.P_AENTITY not found
----unrelated logs----

----some logs----
Caused by: org.hibernate.AssertionFailure: Table myschema.P_AENTITY not found
    at org.hibernate.persister.entity.AbstractEntityPersister.getTableId(AbstractEntityPersister.java:5181) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.internal.DynamicFilterAliasGenerator.getAlias(DynamicFilterAliasGenerator.java:31) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.internal.FilterHelper.render(FilterHelper.java:109) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.internal.FilterHelper.render(FilterHelper.java:96) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.persister.entity.AbstractEntityPersister.filterFragment(AbstractEntityPersister.java:3587) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.engine.internal.JoinSequence.toJoinFragment(JoinSequence.java:175) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.hql.internal.ast.util.JoinProcessor.addJoinNodes(JoinProcessor.java:147) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.hql.internal.ast.util.JoinProcessor.processJoins(JoinProcessor.java:141) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.hql.internal.ast.HqlSqlWalker.processQuery(HqlSqlWalker.java:694) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:673) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:309) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:257) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:262) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:190) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:142) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:115) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:76) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:150) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.internal.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:302) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:240) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:1894) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:291) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_121]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_121]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_121]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121]
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298) ~[spring-orm-4.3.8.RELEASE.jar:4.3.8.RELEASE]
    at com.sun.proxy.$Proxy80.createQuery(Unknown Source) ~[na:na]
    at com.example.dao.Adao.findAll(Adao.java:27) ~[classes/:na]
    at com.example.dao.Adao$$FastClassBySpringCGLIB$$c40f7d5e.invoke(<generated>) ~[classes/:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) ~[spring-core-4.3.8.RELEASE.jar:4.3.8.RELEASE]
----some logs----

如果我在 Adao.java class 中删除启用过滤器,那么它就可以正常工作。

另一种情况是如果我使用SessionFactory而不是entitymanager,结果是一样的。

我该如何解决这个问题?

基本上,DynamicFilterAliasGenerator 有一个问题,它不尊重 hibernate.default_schema(它知道有 table P_AENTITY,但因为它没有关于默认方案无法将运行时 table 名称 myschema.P_AENTITY 与其拥有的名称进行比较。

因此,如果您需要快速修复,只需从您的休眠配置中删除 hibernate.default_schema 并继续 jdbc.url。 所以不要有这样的东西

properties.put("hibernate.default_schema", "myschema");

JDBC URL 中的架构像这样

DriverManagerDataSource ds = new DriverManagerDataSource();
....
ds.setUrl("jdbc:mysql://localhost:3306/myschema");
....

首先你能检查一下你的休眠配置是否有这些属性:

hibernate.dialect=org.hibernate.dialect.MySQLDialect  //you should use mysql dialect from pom I see that you use mysql
#hibernate.default_schema=myCustomName                //commented out in case you want use default schema, else you can use this property to create your custom schema
hibernate.show_sql=true                               //this will help so see what queries are executed
hibernate.format_sql=true                             //this will print formatted sql
hibernate.hbm2ddl.auto=create                         //this will generate database tables if they don't exist in DB, also you can change to "update" this property then database will get updates.

第二件事我怀疑引号可能有问题,它们是 JPA 样式但在休眠中没有尝试(是否有理由在过滤器中将 STATUS 设为大写?):

@Entity
@Table(name = DataSourceContext.DB_PREFIX + "AENTITY")
@FilterDef(name = "statusFilter", defaultCondition = "`status` = :status ", parameters = @ParamDef(name = "status", type = "string"))
@Filter(name = "statusFilter", condition = "`status` = :status")
public class Aentity implements Serializable {
    private Long id;
    private EntityStatus status;
    private Date createdDate;
    private Date updatedDate;
    private Date deletedDate;
    private Long version;

    // getters and setter with column annotations.
}