Spring Data R2DBC how to query hierarchical data
Asked Answered
N

1

5

I am new to Reactive programming. I have to develop a simple spring boot application to return a json response which contains the company details with all its sub companies and employees

Created a spring boot application (Spring Webflux + Spring data r2dbc )

Using following Database Tables to represent the Company and Sub Company and Employee relationship (it is a hierarchical relationship with Company and Sub Company where a company can have N number of sub companies, and each of these sub companies can have another N number of sub companies etc and so on)

Company

  • id
  • name
  • address

Company_SubCompany

  • id
  • sub_company_id (foreign key references id of above company table )

Employee

  • id
  • name
  • designation
  • company_id ( foreign key references id of above company table )

Following are the java model classes to represent the above tables

Company.java

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class Company  implements Serializable { 
    private int id;
    private String name;
    private String address;  
    
    @With
    @Transient
    private List<Company> subCompanies;
    
    @With
    @Transient
    private List<Employee> employees;
}

Employee.java

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class Employee  implements Serializable {    
    @Id
    private int id;
    private String name;
    private String designation;  
    
}

Following repositories are created

@Repository
public interface CompanyRepository extends ReactiveCrudRepository<Company, Integer> {

    @Query("select sub.sub_company_id from Company_SubCompany sub inner join  Company c on sub.sub_company_id = c.id   where sub.id = :id")
    Flux<Integer> findSubCompnayIds(int id);
    
    @Query("select * from   Company c  where id = :id")
    Mono<Company> findCompanyById(Integer id);

}

@Repository
public interface EmployeeRepository extends ReactiveCrudRepository<Employee, Integer> {

    @Query("select * from   Employee where company_id = :id")
    Flux<Employee> findEmployeeByCompanyId(Integer id);

}

In Company_SubCompany table, the super company is represented with id -1. So using this id we are now able to get the super parent company .

With the below service code i am now able to get first level of company and its employees. But i am not sure how to get all the nested sub companies and add to it

@Service
public class ComanyService {
    
    @Autowired
    CompanyRepository companyRepository;
    
    @Autowired
    EmployeeRepository employeeRepository;

    public Flux<Company> findComapnyWithAllChilds() {

        Flux<Integer> childCompanyIds = companyRepository.findSubCompnayIds(-1);
        Flux<Company> companies = childCompanyIds.flatMap(p -> {
            return Flux.zip(companyRepository.findCompanyById(p),
                    employeeRepository.findEmployeeByCompanyId(p).collectList(),
                    (t1, t2) -> t1.withEmployees(t2));
        });

        return companies;
    }
}

I am very new to reactive, functional programing and r2dbc, so please help me to how to resolve my problem. If there is any other better approach available will use that one also. The requirement is to get the company , all its employees and sub companies (upto N leavel) .

Nonunionism answered 14/4, 2020 at 15:39 Comment(4)
Just start here vladmihalcea.com/tutorials/hibernateStolon
Thanks @AhmetOZKESEK. Currently spring data R2DBC does not support relations. So that link seems not much useful f or this caseNonunionism
You right, I had not missed that. On the other hand your Employee class has now companyId field/attr. Is it intentional or just a copy/paste accident?Stolon
On the other hand, what I saw in the Spring Data R2DBC document was that it seems to me we can try to use a Converter<Row, YourClass> by implementing it our repository interface, docs.spring.io/spring-data/r2dbc/docs/1.0.x/reference/html/…, It is interesting, I will try this some time.Stolon
R
6

this code can help you, this approach fill the List objects from database calls

public Flux<Company> findComapnyWithAllChilds(){
    return companyRepository.findAll()
            .flatMap(companies ->
                    Mono.just(companies)
                    .zipWith(employeeRepository.findEmployeeByCompanyId(companies.getId()).collectList())
                    .map(tupla -> tupla.getT1().withEmployees(tupla.getT2()))
                        .zipWith(anotherRepository.findAnotherByCompanyId(companies.getId()).collectList())
                        .map(tupla -> tupla.getT1().withAnother(tupla.getT2()))
            );

}

Rave answered 13/7, 2020 at 2:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.