Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
无法使用Java完全递归树层次结构_Java_Spring Boot_Recursion_Tree_Spring Data Jpa - Fatal编程技术网

无法使用Java完全递归树层次结构

无法使用Java完全递归树层次结构,java,spring-boot,recursion,tree,spring-data-jpa,Java,Spring Boot,Recursion,Tree,Spring Data Jpa,我们已经创建了一个SpringBootMicroService,它发出一个HTTP GET来从MySQL数据库中提取数据(每个节点),MySQL数据库的数据设置在一个表中 我能够在特定级别获取节点的子节点,但还需要能够查看所有子节点(即使它需要不同的REST调用和服务方法) 我在技术堆栈中使用Java1.8、SpringBoot1.5.6.RELEASE、JPA和MySQL 5 pom.xml: <parent> <groupId>org.springframew

我们已经创建了一个SpringBootMicroService,它发出一个HTTP GET来从MySQL数据库中提取数据(每个节点),MySQL数据库的数据设置在一个表中

我能够在特定级别获取节点的子节点,但还需要能够查看所有子节点(即使它需要不同的REST调用和服务方法)

我在技术堆栈中使用Java1.8、SpringBoot1.5.6.RELEASE、JPA和MySQL 5

pom.xml:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.6.RELEASE</version>
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.0</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>
NodeRepository:

@Repository
public interface NodeRepository extends JpaRepository<Node, Long> {

    @Query(value = "SELECT * FROM NODE WHERE parent_id = ?", nativeQuery = true)
    List<Node> findNodesByParentId(Long parentId);

    @Query(value = "SELECT * FROM NODE WHERE name = ?", nativeQuery = true)
    Node findByName(String name);
}
意见/问题:

通过我的RestController,我可以通过调用此REST端点来获取第一级记录:

http://localhost:8080/myapp/api/nodes?name=Products
但是,它只提供了第一个级别(不是Books&Coffee和Latte下面的子节点):

不要在书中列出恐怖、浪漫、幻想,在咖啡中列出摩卡咖啡、拿铁咖啡(以及拿铁咖啡下的浓缩咖啡)

现在,如果我使用parentNode(例如Books),它确实会显示子级(但仅显示第一级):

JSON响应负载:

[
  {
    "id": 3,
    "name": "Horror",
    "parentId": 2
  },
  {
    "id": 4,
    "name": "Romance",
    "parentId": 2
  },
  {
    "id": 5,
    "name": "Fantasy",
    "parentId": 2
  }
]   
{
    "id": 9,
    "name": "Espresso",
    "parentId": 8
}
尝试列出咖啡的所有子项时:

http://localhost:8080/myapp/api/nodes?name=Coffee
JSON响应负载:

[
  {
    "id": 7,
    "name": "Mocha",
    "parentId": 6
  },
  {
    "id": 8,
    "name": "Latte",
    "parentId": 6
  }
]
看,这一个没有显示浓缩咖啡,必须将拿铁作为父项调用才能显式查看:

http://localhost:8080/myapp/api/nodes?name=Latte
JSON响应负载:

[
  {
    "id": 3,
    "name": "Horror",
    "parentId": 2
  },
  {
    "id": 4,
    "name": "Romance",
    "parentId": 2
  },
  {
    "id": 5,
    "name": "Fantasy",
    "parentId": 2
  }
]   
{
    "id": 9,
    "name": "Espresso",
    "parentId": 8
}
我可以获取特定子级的节点

如何使用递归获取所有级别的所有节点(我知道这将是一个不同的REST GET调用/REST端点)


需要使用递归来获取所有子级/子级,但不知道如何在这两种情况下(获取子节点和删除节点)都这样做。

不知道为什么在这里不充分利用JPA,首先是在实体级,然后是在使用本机SQL而不是JPQL的查询过程中

1)如果您按照以下方式更改实体:

@Entity
public class Node {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @NotNull
    private String name;

    @ManyToOne
    @JoinColumn(name = "parent_id")
    private Node parentNode;

    @OneToMany(mappedBy = "parentNode", 
               cascade = { CascadeType.DELETE, CascadeType.PERSIST} )
    private List<Node> children;
}
顶级节点检索

 @Override
 @Transactional(readOnly = true)
 public List<Node> getNodeHierarchy(Node inNode){
    Node node = repository.findByName(inNode.getName());

    traverseNodeAndFetchChildren(node);

    return node.getChildren();
 }
node.getChildren().size()
-这使得持久性上下文可以延迟加载
@OneToMany
依赖项


4)将您的服务方法标记为
@Transactional(readOnly=true)

也可能是一个好主意,我建议您必须像建议一样实现

这是一个合乎逻辑的问题。而不是
返回getHierarchyPerNode(子节点),则必须添加节点

public List<Node> getHierarchyPerNode(Node node) {
        List<Node> nodes = new ArrayList<>();
        List<Node> children = new ArrayList<>();
        if (node != null) {
            Node aNode = repository.findByName(node.getName());
            nodes.add(aNode);
            Long parentId = aNode.getId();
            children = repository.findNodesByParentId(parentId);

            // Was trying this as recursion but kept throwing an NullPointerException.
            if (children != null) {
                for (Node child : children) {
                    List<Node> childList = getHierarchyPerNode(child);
                    if (childList != null && !childList.isEmpty()) {
                        nodes.addAll(childList);
                    }
                }
            }
        }
        return nodes;
    }
公共列表getHierarchyPerNode(节点){
列表节点=新的ArrayList();
List children=new ArrayList();
如果(节点!=null){
Node=repository.findByName(Node.getName());
添加(阳极);
长parentId=Anyone.getId();
children=repository.findNodesByParentId(parentId);
//尝试将其作为递归,但不断抛出NullPointerException。
如果(子项!=null){
用于(节点子节点:子节点){
List childList=getHierarchyPerNode(子节点);
if(childList!=null&!childList.isEmpty()){
nodes.addAll(childList);
}
}
}
}
返回节点;
}

您应该尝试中所述的具体化路径模型。您不需要手动添加任何子项,因为这些子项都在同一持久性上下文下运行。getChildren()实际上返回一个代理。在JPA/Hibernate中,当您对延迟加载的集合(例如size())调用任何方法时,持久性提供程序将自动加载该集合。我已经在getNodeHierarchy服务方法中添加了@Transactional,因为它遗漏了这一点。@Akash Shah关于这个问题有什么想法吗!
[
  {
    "id": 7,
    "name": "Mocha",
    "parentId": 6
  },
  {
    "id": 8,
    "name": "Latte",
    "parentId": 6
  }
]
http://localhost:8080/myapp/api/nodes?name=Latte
{
    "id": 9,
    "name": "Espresso",
    "parentId": 8
}
@Entity
public class Node {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @NotNull
    private String name;

    @ManyToOne
    @JoinColumn(name = "parent_id")
    private Node parentNode;

    @OneToMany(mappedBy = "parentNode", 
               cascade = { CascadeType.DELETE, CascadeType.PERSIST} )
    private List<Node> children;
}
@Query(value = "select n from Node n inner join n.parentNode p where p.id = ?")
List<Node> findNodesByParentId(Long parentId);
@RequestMapping(
    value = {"/api/nodes"}, 
    method = RequestMethod.GET, 
    produces = "APPLICATION/JSON"
)
public ResponseEntity<Object> getNodeHierarchy(Node node) {
    if (null == node) {
        return new ResponseEntity<Object>(HttpStatus.NOT_FOUND);
    }
    List<Node> nodes = myService.getNodeHierarchy(node);

    if (null == nodes) {
        return new ResponseEntity<Object>(HttpStatus.NOT_FOUND);
    }

    return new ResponseEntity<Object>(nodes, headers, HttpStatus.OK);
}
 @Override
 @Transactional(readOnly = true)
 public List<Node> getNodeHierarchy(Node inNode){
    Node node = repository.findByName(inNode.getName());

    traverseNodeAndFetchChildren(node);

    return node.getChildren();
 }
public void traverseNodeAndFetchChildren(Node node) {
   int size = node.getChildren().size();

   if(size > 0){
      for(Node childNode: node.getChildren()){
         traverseNodeAndFetchChildren(childNode);
      }
   }       
}
public List<Node> getHierarchyPerNode(Node node) {
        List<Node> nodes = new ArrayList<>();
        List<Node> children = new ArrayList<>();
        if (node != null) {
            Node aNode = repository.findByName(node.getName());
            nodes.add(aNode);
            Long parentId = aNode.getId();
            children = repository.findNodesByParentId(parentId);

            // Was trying this as recursion but kept throwing an NullPointerException.
            if (children != null) {
                for (Node child : children) {
                    List<Node> childList = getHierarchyPerNode(child);
                    if (childList != null && !childList.isEmpty()) {
                        nodes.addAll(childList);
                    }
                }
            }
        }
        return nodes;
    }