Java 在公式字段中使用Hibernate注册函数

Java 在公式字段中使用Hibernate注册函数,java,hibernate,jpa,Java,Hibernate,Jpa,我试图在公式字段中使用自定义hibernate方言中的注册函数。问题是公式字段没有使用我注册的函数。我在想我做错了什么 作为背景,我有一个应用程序,我正在为Oracle和Postgresql开发一个功能强大的应用程序。不是同时,而是针对部署到哪个数据库。我的模型中有几个公式字段,用于将OneToMany映射实体的名称聚合到一个逗号分隔的列表中,以便于搜索和显示。这是利用Listag完成的,当时它纯粹是Oracle。这在Postgresql中不起作用,但考虑到它需要在两种环境中都起作用,我不能将公

我试图在公式字段中使用自定义hibernate方言中的注册函数。问题是公式字段没有使用我注册的函数。我在想我做错了什么

作为背景,我有一个应用程序,我正在为Oracle和Postgresql开发一个功能强大的应用程序。不是同时,而是针对部署到哪个数据库。我的模型中有几个公式字段,用于将OneToMany映射实体的名称聚合到一个逗号分隔的列表中,以便于搜索和显示。这是利用Listag完成的,当时它纯粹是Oracle。这在Postgresql中不起作用,但考虑到它需要在两种环境中都起作用,我不能将公式的语法更改为STRING_AGG。因此,我试图为这两个数据库注册一个函数,该函数将为正在使用的数据库使用适当的格式

我正在使用自定义方言扩展并注册我的函数,但它没有使用我注册的函数。我不确定我做错了什么

如果这实际上是不可能的,而我从错误的方向来处理这个问题,有没有一个好的方法来动态定义公式字段?不是在运行时,而是在编译时设置方言时

public class CustomPostgresqlDialect extends PostgreSQL95Dialect {
    public CustomPostgresqlDialect() {
        super();

        registerFunction("MY_LISTAGG", new SQLFunctionTemplate( StandardBasicTypes.STRING, " STRING_AGG(?1 , ', ' ORDER BY ?1) "));
    }
}

这是我的模型,公式如下:

public class Contractor extends Object implements Serializable {
    ...
    
    @OneToMany(mappedBy = "contractor", fetch = FetchType.LAZY)
    private Set<ProjectManager> projectManagers;
    
    ...

    @Formula("(" +
                "SELECT\r\n" + 
                "    MY_LISTAGG(PM.NAME)\r\n" +
                "FROM\r\n" + 
                "    PROJECTMANAGERS PM\r\n" + 
                "    INNER JOIN CONTRACTORS C ON C.ID = PM.FK_CONTRACTOR\r\n" + 
                "WHERE\r\n" +
                "    C.ID = id" + 
                ")"
    )
    @NotAudited
    private String pmNames;

    ...
}
public类Contractor扩展对象实现可序列化{
...
@OneToMany(mappedBy=“contractor”,fetch=FetchType.LAZY)
私人项目经理;
...
@公式(“(”+
“选择\r\n”+
“我的列表标签(PM.NAME)\r\n”+
“来自\r\n”+
“项目经理PM\r\n”+
“C.ID上的内部连接承包商C=PM.FK\U承包商\r\n”+
“其中\r\n”+
“C.ID=ID”+
")"
)
@未经审核
私有字符串名称;
...
}

您不能为此目的使用
@Formula
。正如合同中所述:

您应该知道,
@Formula
注释采用了可能影响数据库可移植性的本机SQL子句


您可以尝试使用JPQL/Criteria查询来实现此目的。

就像SternK所写的那样,在
@Formula
中不可能使用JPQL/HQL函数,我还建议不要在一般的公式中使用子查询,因为即使您不需要数据,也会导致始终执行这些子查询的惩罚

我认为这是一个完美的用例

我创建了这个库,以便在JPA模型和自定义接口或抽象类定义的模型之间进行简单的映射,类似于类固醇上的Spring数据投影。其思想是以您喜欢的方式定义目标结构(域模型),并通过JPQL表达式将属性(getter)映射到实体模型

在Blaze持久性实体视图中,您的用例的DTO模型可能如下所示:

@EntityView(Contractor.class)
public interface ContractorDto {
    @IdMapping
    Long getId();
    @Mapping(value = "projectManagers.name", fetch = MULTISET)
    Set<String> getPmNames();
}
最好的是,它只会获取实际需要的状态!因此,在本例中,将创建如下所示的SQL查询:

select 
  c.id, 
  (
    select json_agg(json_object(c1, pm.name)) 
    from project_manager pm 
    where pm.fk_contractor = c.id
  )
 from contractor c
如果确实需要聚合字符串,还可以使用映射中的
GROUP\u CONCAT
函数:

@EntityView(Contractor.class)
public interface ContractorDto {
    @IdMapping
    Long getId();
    @Mapping("GROUP_CONCAT(projectManagers.name, 'SEPARATOR', ', ', 'ORDER BY', projectManagers.name,, 'ASC')")
    String getPmNames();
}

哦我认为它可以工作,因为在我将其注册为Oracle的关键字之前,inthein在公式中造成了问题。我希望它能用我应用于函数的本机SQL片段替换为该函数注册的名称。这看起来真是难以置信。仅拥有基于代码的“视图”的能力将非常有用!然而,我确实有一个关于小组讨论的问题。在您链接的文档部分中,它指出并非所有DBMS都支持这一点。快速搜索表明Oracle和Postgres都不支持内部的GROUP_CONCAT功能。我仍然需要阅读Blazebit文档,但是映射注释是否与公式字段(即,一个直接的SQL片段)做相同的事情?在这种情况下,您的示例对我来说不起作用,因为GROUP_CONCAT不存在。
GROUP_CONCAT
正是我们为此选择的名称,但许多DBMS都有实现,包括Oracle和PostgreSQL分别通过
listag
string_agg
实现。
@Mapping
中的表达式位于“JPQL.next”中,表达式文档将对此进行进一步解释。
select 
  c.id, 
  (
    select json_agg(json_object(c1, pm.name)) 
    from project_manager pm 
    where pm.fk_contractor = c.id
  )
 from contractor c
@EntityView(Contractor.class)
public interface ContractorDto {
    @IdMapping
    Long getId();
    @Mapping("GROUP_CONCAT(projectManagers.name, 'SEPARATOR', ', ', 'ORDER BY', projectManagers.name,, 'ASC')")
    String getPmNames();
}