Scott 과 함께 - Outer Join 을 Java 로 구현하기.
Outer Join
이번 Post는 Outer Join을 Java로 구현한다. 이전 Inner Join Post와 동일하게 부서의 Job 별 평균급여를 구하지만, 이번에는 직원이 없는 부서도 같이 출력해야 한다.
SQL
![]() |
부서/Job 평균 급여에 Operations 부서도 추가. |
Operations 부서는 직원이 없는 부서라서, Job과 평균급여는 Null이다. Operations의 Job은 Null이고 평균급여는 0으로 Java에서 표현한다.
select d.dname, e.job, avg(e.sal) from dept d left outer join emp e on d.deptno = e.deptno where 1=1 group by d.dname, e.job;
Java
부서 Stream + 직원 Stream
처음 생각했던 방법은 부서 Stream의 map()에서 직원 Stream으로 Job별 평균급여를 가져오려고 했지만 실패했다. Operations의 직원이 없어 직원 Stream의 결과가 Null이면, Operation이 부서 Stream에서 사라졌다.
depts.stream() .map(department -> employees.stream() .filter(employee -> employee.getDepartment() == department) .collect(Collectors.groupingBy(...)) );
부서 List + 부서/Job 평균급여 Map
![]() |
Outer Join 전략 |
다음으로 생각한 방법은 DB의 Outer Join 방법이다. 먼저 Driving Table을
만들기 위해서 전체 부서 List를 만들었다. Driving Table은 조인을 주도하는
Table이다.
@Test public void outerJoin_Stream() { String sql; sql = "select d from Department d"; List<Department> depts = entityManager.createQuery(sql, Department.class) .getResultList(); ... }
다음은 Driven Table인 부서/Job 평균급여 Map을 만든다. Driven Table은 Driving Table의 조건으로 Scan하는 Table이다.
@Test public void outerJoin_Stream() { ... sql = "select e from Employee e"; List<Employee> employees = entityManager.createQuery(sql, Employee.class) .getResultList(); Map<Department, Map<EmployeeJob, Double>> results = employees.stream() .collect(Collectors.groupingBy(Employee::getDepartment, Collectors.groupingBy(Employee::getJob, Collectors.averagingDouble(Employee::getSalary)))); ... }
마지막으로 부서 List로 부서/Job 평균급여 Map을 조회한다. 여기서 문제점이 하나 있다. Map에는 Operation이 존재하지 않기 때문에, get() 호출 시 NullPointException이 발생한다. 그래서 Null처리를 위해 ifPresentOrElse()을 사용했다. ifPresentOrElse()은 Null 여부에 따른 처리를 다르게 할 수 있다.
@Test public void outerJoin_Stream() { ... for (Department dept : depts) { Optional.ofNullable(results.get(dept)) .ifPresentOrElse((employeeJobDoubleMap) -> { // .... }, () -> { System.out.printf("%10s | %10s : ", dept.getName(), "Null"); }); } }
결과
마무리
이번 Post는 Outer Join을 Java로 구현해봤다. Java로 DB와 다르게 구현할 수 있다고 생각했지만, 결국 똑같이 만들었다. 뭔가 추가적인 방법이 있다면 Update하겠다. 소스는 Github에 공유했다.
참조
- https://akageun.github.io/2019/08/06/java-stream-groupby.html
- https://www.baeldung.com/java-groupingby-collector
- https://dzone.com/articles/using-java-collectors
- https://mingggu.tistory.com/77
- https://blog.jooq.org/2015/08/13/common-sql-clauses-and-their-equivalents-in-java-8-streams/
- http://blog.weirdx.io/post/55568
댓글
댓글 쓰기