【问题标题】:Calling User Defined SQL function in JPQL with hibernate使用休眠在 JPQL 中调用用户定义的 SQL 函数
【发布时间】:2019-02-10 14:14:00
【问题描述】:

我需要将我的 Spring Data JPA/Hibernate 应用程序迁移到

MS SQL Server (Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (X64) 
Enterprise Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1) (Hypervisor)) 

来自

Oracle(Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production).

环境

org.springframework.data:spring-data-jpa:2.0.9.RELEASE
org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final
org.hibernate:hibernate-entitymanager:5.2.17.Final
Java version: 1.8.0_91, vendor: Oracle Corporation
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

现在在 orm.xml 的很多地方,我们都使用了 to_char。例如,生成每月汇总报告等。

在 SQL Server 架构中,我们定义了 to_char,期望 Hibernate 注册它或留下一些钩子来注册用户定义的函数:

CREATE FUNCTION to_char(@dateVal DATE, @dateFormat varchar) RETURNS VARCHAR(20) 
  BEGIN       
    RETURN CONVERT(varchar, date, 1)
  END  

我研究过Write & Call user-defined function in JPQL?。但不幸的是,我不能重新定义一种方言。

对于 JPQL,

<query>
         select new com.entity.MyEntity(
            c.Id,
            to_char(c.transactionDate,'Mon/yyyy'),
            sum(c.disputeAmount))
            FROM   ChargeBack c join c.chargebackReason rcd
            GROUP BY c.Id, to_char(c.transactionDate,'Mon/yyyy'),
</query>

由于未注册 SQL 函数,我收到以下错误。

Caused by: java.lang.NullPointerException
at org.hibernate.hql.internal.NameGenerator.generateColumnNames(NameGenerator.java:27)
at org.hibernate.hql.internal.ast.util.SessionFactoryHelper.generateColumnNames(SessionFactoryHelper.java:434)
at org.hibernate.hql.internal.ast.tree.SelectClause.initializeColumnNames(SelectClause.java:269)
at org.hibernate.hql.internal.ast.tree.SelectClause.finishInitialization(SelectClause.java:259)
at org.hibernate.hql.internal.ast.tree.SelectClause.initializeExplicitSelectClause(SelectClause.java:254)
at org.hibernate.hql.internal.ast.HqlSqlWalker.useSelectClause(HqlSqlWalker.java:1013)
at org.hibernate.hql.internal.ast.HqlSqlWalker.processQuery(HqlSqlWalker.java:781)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:677)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:313)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:261)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:266)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:189)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:141)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:115)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:77)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:153)
at org.hibernate.query.spi.NamedQueryRepository.checkNamedQueries(NamedQueryRepository.java:157)
at org.hibernate.internal.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:533)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:323)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:462)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:892)
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390)
... 67 more

【问题讨论】:

    标签: sql-server hibernate spring-data-jpa


    【解决方案1】:

    您的参考资料很旧,不再有效。从 JPA 2.1 开始,您可以调用您的客户函数,例如:

    function('to_char', c.transactionDate,'Mon/yyyy')
    

    所以你的查询应该是这样的

    <query>
         select new com.entity.MyEntity(
            c.Id,
            function('to_char', c.transactionDate,'Mon/yyyy'),
            sum(c.disputeAmount))
            FROM   ChargeBack c join c.chargebackReason rcd
            GROUP BY c.Id, function('to_char', c.transactionDate,'Mon/yyyy'),
     </query>
    

    【讨论】:

    • 感谢您的快速回复。我一定会尽快尝试的。同时,您能否在记录的地方提供一些参考资料?
    • 您可以在此处thoughts-on-java.org/database-functions 或此处mastertheboss.com/jboss-frameworks/hibernate-jpa/… 阅读有关此内容的信息
    • 经过一些调试,我观察到Hibernate为每个函数分配了org.hibernate.hql.internal.ast.tree.MethodNode,然后尝试解析org.hibernate.hql中的返回数据类型。来自所选方言的 internal.ast.tree.MethodNode.dialectFunction(AST)。但是 to_char 没有在 SQLServer2008Dialect 中注册。因此,MethodNode 的数据类型保持为空。这会导致 NullPointer,如问题的 SatckTrace 所示。
    猜你喜欢
    • 1970-01-01
    • 2015-11-07
    • 2013-02-24
    • 2014-04-27
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多