用 PostgreSQL 查询替换 Java 循环

huangapple go评论125阅读模式
英文:

replace java loop with query in postgresql

问题

我使用了PostgreSQL 10和Spring Boot。

我尝试成功地使用Java中的循环加载树。

在循环中,我每次调用查询。

但是它会在应用程序服务器和数据库服务器上消耗CPU,并且加载包含5000个单元的树需要时间。

我希望只使用一个查询来加载树,而不需要在Java中使用循环。
Java中的结果是ResponseEntity<List<UnitDTO>>

这是我的代码:


@GetMapping("/unitsBook")
@Timed
public ResponseEntity<List<UnitDTO>> getAllUnitsBook(Pageable pageable, @RequestParam(name="lang", required=false) String lang,
@RequestParam(name="emp", required=false) String empID) {
log.debug("REST request to get a page of Units");
Page<UnitDTO> page = unitService.findAllUnitBook(pageable, lang,empID);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/unitsBook");
return ResponseEntity.ok().headers(headers).body(page.getContent());
}

包含循环的Java代码是:

public List<UnitDTO> getUnitBookList(Pageable pageable, String lang,String empID) {

List<UnitDTO> list=unitRepository.findUnitList(pageable, lang,empID);
List<UnitDTO> unitChildList=getChildrenUnitList(list,lang,pageable,empID);

return unitChildList;
}

private List<UnitDTO> getChildrenUnitList(
List<UnitDTO> unitList, String lang,Pageable pageable,String empID) {

for(UnitDTO UnitDTO : unitList) {

List<UnitDTO> childrenListEntity = unitRepository.findUnitByParentId(pageable, lang,UnitDTO.getValue(),empID);
UnitDTO.setChildren(getChildrenUnitList(childrenListEntity,lang,pageable,empID));

}

return unitList;
}

调用查询的代码是:

public List<UnitDTO> findUnitList(Pageable pageable, String lang,String empID) {

String querystr = "SELECT ";
querystr += " unl.name AS text ,";
querystr += " un.id AS value ,";
querystr += " ,cast( 0 as varchar(10) ) as favoriteNbr,cast( null as varchar(10) ) as favoriteId ";
querystr += " FROM public.unit un ";
querystr += " LEFT OUTER JOIN public.unitlang unl ON unl.unit_id = un.id ";
querystr += " Where unl.lang = :lang  parentid is null  order by app_order asc";

log.debug("-- Query:" + querystr);

Query query = em.createNativeQuery(querystr, "UnitDTOMap");
query.setParameter("lang", lang);

List<UnitDTO> unitDTOs = query.getResultList();

if (pageable.isUnpaged()) {
return unitDTOs;
}

return unitDTOs;
}

@Override
public List<UnitDTO> findUnitByParentId(Pageable pageable, String lang, String idParent,String empID) {
log.debug("-- pageable:" + pageable.getPageNumber() + ", Size:" + pageable.getPageSize() + ", isUnpaged:" + pageable.isUnpaged() + ", lang:" + lang);
lang = lang.toUpperCase();
String querystr = "SELECT ";

querystr += " unl.name AS text ,";
querystr += " un.id AS value ,"; 
querystr += " (case when cast((select count(*) from employee where employee.unit_id = un.id) as varchar(10)) != '0'  then cast(1 as Boolean)  else cast(0 as BOOLEAN) end) as disabled";
querystr += " ,cast( 0 as varchar(10) ) as favoriteNbr,cast( null as varchar(10) ) as favoriteId ";
querystr += " FROM unit un ";
querystr += " LEFT OUTER JOIN unitlang unl ON unl.unit_id = un.id ";
querystr += " Where unl.lang = :lang  and un.parentid = :idParent order by app_order asc ";

log.debug("-- Query:" + querystr);

Query query = em.createNativeQuery(querystr, "UnitBookDTOMap");
query.setParameter("lang", lang);
query.setParameter("idParent", idParent);
List<UnitDTO> unitDTOs = query.getResultList();

log.debug("-- unitDTOs Size:" + unitDTOs.size());

if (pageable.isUnpaged()) {
return unitDTOs;
}

return unitDTOs;
}

更新:

我尝试使用递归查询,但问题是树没有正确显示。所有单元都在同一级别。

我认为问题在这行中:WHERE id = :idParent,我将其注释掉,因为我没有找到如何传递它。

public List<UnitDTO> getUnitBookList(Pageable pageable, String lang) {

List<UnitDTO> list=unitRepository.findUnitList(pageable, lang);
// List<UnitBookDTO> unitChildList=getChildrenUnitList(list,lang,pageable);

return list;
}

@Override
public List<UnitDTO> findUnitList(Pageable pageable, String lang) {
log.debug("-- pageable:" + pageable.getPageNumber() + ", Size:" + pageable.getPageSize() + ", isUnpaged:" + pageable.isUnpaged() + ", lang:" + lang);
lang = lang.toUpperCase();
String querystr = "WITH RECURSIVE un_id AS ( ";

querystr += " SELECT id";
querystr += "  FROM unit ";
// querystr += " WHERE id = :idParent";
querystr += " UNION";
querystr += " SELECT unit.id";

querystr += " FROM unit JOIN un_id ON unit.parentid = un_id.id )  ";
querystr += " SELECT unl.name                          AS text, un.id                             AS value, (case   when cast((select count(*) from employee where employee.unit_id = un.id) as varchar(10)) != '0' ";
querystr += " then cast(1 as Boolean)   else cast(0 as BOOLEAN) end) as disabled , cast(0 as varchar(10))            as favoriteNbr,";
querystr += " cast(null as varchar(10))         as favoriteId FROM un_id JOIN unit un USING (id) LEFT OUTER JOIN unitlang unl ON unl.unit_id = un.id Where unl.lang = :lang order by app_order asc";

log.debug("-- Query:" + querystr);

Query query = em.createNativeQuery(querystr, "UnitDTOMap");
query.setParameter("lang", lang);

List<UnitDTO> unitDTOs = query.getResultList();

log.debug("-- unitDTOs Size:" + unitDTOs.size());

if (pageable.isUnpaged()) {
return unitDTOs;
}

return unitDTOs;
}
英文:

I used postgresql 10 and spring boot

I try with success to load tree using loop in java.

in the loop I call each time the query

but it consumes cpu in application server and cpu in database server and takes time to load the tree which contain 5000 unit.

I want using only one query to load the tree without loop in java.
the result from java is ResponseEntity&lt;List&lt;UnitDTO&gt;&gt;

this is my code :

  @GetMapping(&quot;/unitsBook&quot;)
@Timed
public ResponseEntity&lt;List&lt;UnitDTO&gt;&gt; getAllUnitsBook(Pageable pageable, @RequestParam(name=&quot;lang&quot;, required=false) String lang,
@RequestParam(name=&quot;emp&quot;, required=false) String empID) {
log.debug(&quot;REST request to get a page of Units&quot;);
Page&lt;UnitDTO&gt; page = unitService.findAllUnitBook(pageable, lang,empID);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, &quot;/api/unitsBook&quot;);
return ResponseEntity.ok().headers(headers).body(page.getContent());
}

the java code which contains the loop is :

public List&lt;UnitDTO&gt; getUnitBookList(Pageable pageable, String lang,String empID) {
List&lt;UnitDTO&gt; list=unitRepository.findUnitList(pageable, lang,empID);
List&lt;UnitDTO&gt; unitChildList=getChildrenUnitList(list,lang,pageable,empID);
return unitChildList;
}
private List&lt;UnitDTO&gt; getChildrenUnitList(
List&lt;UnitDTO&gt; unitList, String lang,Pageable pageable,String empID) {
for(UnitDTO UnitDTO : unitList) {
List&lt;UnitDTO&gt; childrenListEntity = unitRepository.findUnitByParentId(pageable, lang,UnitDTO.getValue(),empID);
UnitDTO.setChildren(getChildrenUnitList(childrenListEntity,lang,pageable,empID));
}
return unitList;
}

and the code which call query is :

  public List&lt;UnitDTO&gt; findUnitList(Pageable pageable, String lang,String empID) {
String querystr = &quot;SELECT &quot;;
querystr += &quot; unl.name AS text ,&quot;;
querystr += &quot; un.id AS value ,&quot;;
querystr += &quot; ,cast( 0 as varchar(10) ) as favoriteNbr,cast( null as varchar(10) ) as favoriteId &quot;;
querystr += &quot; FROM public.unit un &quot;;
querystr += &quot; LEFT OUTER JOIN public.unitlang unl ON unl.unit_id = un.id &quot;;
querystr += &quot; Where unl.lang = :lang  parentid is null  order by app_order asc&quot;;
log.debug(&quot;-- Query:&quot; + querystr);
Query query = em.createNativeQuery(querystr, &quot;UnitDTOMap&quot;);
query.setParameter(&quot;lang&quot;, lang);
List&lt;UnitDTO&gt; unitDTOs = query.getResultList();
if (pageable.isUnpaged()) {
return unitDTOs;
}
return unitDTOs;
}
@Override
public List&lt;UnitDTO&gt; findUnitByParentId(Pageable pageable, String lang, String idParent,String empID) {
log.debug(&quot;-- pageable:&quot; + pageable.getPageNumber() + &quot;, Size:&quot; + pageable.getPageSize() + &quot;, isUnpaged:&quot; + pageable.isUnpaged() + &quot;, lang:&quot; + lang);
lang = lang.toUpperCase();
String querystr = &quot;SELECT &quot;;
querystr += &quot; unl.name AS text ,&quot;;
querystr += &quot; un.id AS value ,&quot;; 
querystr += &quot; (case when cast((select count(*) from employee where employee.unit_id = un.id) as varchar(10)) != &#39;0&#39;  then cast(1 as Boolean)  else cast(0 as BOOLEAN) end) as disabled&quot;;
querystr += &quot; ,cast( 0 as varchar(10) ) as favoriteNbr,cast( null as varchar(10) ) as favoriteId &quot;;
querystr += &quot; FROM unit un &quot;;
querystr += &quot; LEFT OUTER JOIN unitlang unl ON unl.unit_id = un.id &quot;;
querystr += &quot; Where unl.lang = :lang  and un.parentid = :idParent order by app_order asc &quot;;
log.debug(&quot;-- Query:&quot; + querystr);
Query query = em.createNativeQuery(querystr, &quot;UnitBookDTOMap&quot;);
query.setParameter(&quot;lang&quot;, lang);
query.setParameter(&quot;idParent&quot;, idParent);
List&lt;UnitDTO&gt; unitDTOs = query.getResultList();
log.debug(&quot;-- unitDTOs Size:&quot; + unitDTOs.size());
if (pageable.isUnpaged()) {
return unitDTOs;
}
return unitDTOs;
}

Updated :

I try to use the recursive query but the problem is that the tree does not display correctly.
all the unit are in same level.
I think the problem in this line WHERE id = :idParent I comment it because I didn't find how can I send it

  public List&lt;UnitDTO&gt; getUnitBookList(Pageable pageable, String lang) {
List&lt;UnitDTO&gt; list=unitRepository.findUnitList(pageable, lang);
//  List&lt;UnitBookDTO&gt; unitChildList=getChildrenUnitList(list,lang,pageable);
return list;
}
@Override
public List&lt;UnitDTO&gt; findUnitList(Pageable pageable, String lang) {
log.debug(&quot;-- pageable:&quot; + pageable.getPageNumber() + &quot;, Size:&quot; + pageable.getPageSize() + &quot;, isUnpaged:&quot; + pageable.isUnpaged() + &quot;, lang:&quot; + lang);
lang = lang.toUpperCase();
String querystr = &quot;WITH RECURSIVE un_id AS ( &quot;;
querystr += &quot; SELECT id&quot;;
querystr += &quot;  FROM unit &quot;;
//  querystr += &quot; WHERE id = :idParent&quot;;
querystr += &quot; UNION&quot;;
querystr += &quot; SELECT unit.id&quot;;
querystr += &quot; FROM unit JOIN un_id ON unit.parentid = un_id.id )  &quot;;
querystr += &quot; SELECT unl.name                          AS text, un.id                             AS value, (case   when cast((select count(*) from employee where employee.unit_id = un.id) as varchar(10)) != &#39;0&#39; &quot;;
querystr += &quot; then cast(1 as Boolean)   else cast(0 as BOOLEAN) end) as disabled , cast(0 as varchar(10))            as favoriteNbr,&quot;;
querystr += &quot; cast(null as varchar(10))         as favoriteId FROM un_id JOIN unit un USING (id) LEFT OUTER JOIN unitlang unl ON unl.unit_id = un.id Where unl.lang = :lang order by app_order asc&quot;;
log.debug(&quot;-- Query:&quot; + querystr);
Query query = em.createNativeQuery(querystr, &quot;UnitDTOMap&quot;);
query.setParameter(&quot;lang&quot;, lang);
List&lt;UnitDTO&gt; unitDTOs = query.getResultList();
log.debug(&quot;-- unitDTOs Size:&quot; + unitDTOs.size());
if (pageable.isUnpaged()) {
return unitDTOs;
}
return unitDTOs;
}

答案1

得分: 2

使用递归公共表达式(CTE)是在SQL中检索树的常规方法:

WITH RECURSIVE un_id AS (
    SELECT id
    FROM unit
    WHERE id = :idParent -- 或者如果您想要排除父级本身,则使用 parentid

    UNION

    SELECT unit.id
    FROM unit
    JOIN un_id
        ON unit.parentid = un_id.id
)
SELECT unl.name                          AS text,
       un.id                             AS value,
       (case
            when cast((select count(*) from employee where employee.unit_id = un.id) as varchar(10)) != '0'
                then cast(1 as Boolean)
            else cast(0 as BOOLEAN) end) as disabled,
       cast(0 as varchar(10))            as favoriteNbr,
       cast(null as varchar(10))         as favoriteId
FROM un_id -- 对公共表达式的引用
JOIN unit un
    USING (id)
LEFT OUTER JOIN unitlang unl ON unl.unit_id = un.id
Where unl.lang = :lang
order by app_order asc

递归地检索所有ID,然后连接所需的其他数据。

英文:

The usual way to fetch a tree in SQL is to use a recursive Common Table Expression (CTE):

WITH RECURSIVE un_id AS (
    SELECT id
    FROM unit
    WHERE id = :idParent -- or parentid if you want to exclude the parent itself

    UNION

    SELECT unit.id
    FROM unit
    JOIN un_id
        ON unit.parentid = un_id.id
)
SELECT unl.name                          AS text,
       un.id                             AS value,
       (case
            when cast((select count(*) from employee where employee.unit_id = un.id) as varchar(10)) != &#39;0&#39;
                then cast(1 as Boolean)
            else cast(0 as BOOLEAN) end) as disabled
        ,
       cast(0 as varchar(10))            as favoriteNbr,
       cast(null as varchar(10))         as favoriteId
FROM un_id -- reference to the CTE
JOIN unit un
    USING (id)
LEFT OUTER JOIN unitlang unl ON unl.unit_id = un.id
Where unl.lang = :lang
order by app_order asc

Recursively fetch all the IDs and then join the rest of the needed data.

huangapple
  • 本文由 发表于 2020年10月17日 04:33:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/64396052.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定