Skip to content

Commit bb06731

Browse files
authored
BAEL-8174 - Implementing Unions in Hibernate (#18577)
* initial code * bael-8174 - article ready for review * bael-8174 - review 1: lecturer/researcher * bael-8174 - review 2
1 parent 3d8ff37 commit bb06731

File tree

9 files changed

+415
-0
lines changed

9 files changed

+415
-0
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.baeldung.hibernate.union;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class UnionApplication {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(UnionApplication.class, args);
11+
}
12+
13+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.baeldung.hibernate.union.dto;
2+
3+
import java.util.Objects;
4+
5+
public class PersonDto {
6+
7+
private Long id;
8+
private String name;
9+
private String role;
10+
11+
public PersonDto(Long id, String name) {
12+
this.id = id;
13+
this.name = name;
14+
}
15+
16+
public PersonDto(Long id, String name, String role) {
17+
this(id, name);
18+
this.role = role;
19+
}
20+
21+
public Long getId() {
22+
return id;
23+
}
24+
25+
public void setId(Long id) {
26+
this.id = id;
27+
}
28+
29+
public String getName() {
30+
return name;
31+
}
32+
33+
public void setName(String name) {
34+
this.name = name;
35+
}
36+
37+
public String getRole() {
38+
return role;
39+
}
40+
41+
public void setRole(String role) {
42+
this.role = role;
43+
}
44+
45+
@Override
46+
public int hashCode() {
47+
return Objects.hash(id, name);
48+
}
49+
50+
@Override
51+
public boolean equals(Object obj) {
52+
if (this == obj)
53+
return true;
54+
if (obj == null)
55+
return false;
56+
if (getClass() != obj.getClass())
57+
return false;
58+
PersonDto other = (PersonDto) obj;
59+
return Objects.equals(id, other.id) && Objects.equals(name, other.name);
60+
}
61+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.baeldung.hibernate.union.model;
2+
3+
import jakarta.persistence.Entity;
4+
import jakarta.persistence.Id;
5+
6+
@Entity
7+
public class Lecturer {
8+
9+
@Id
10+
private Long id;
11+
private String name;
12+
private Integer facultyId;
13+
14+
public Lecturer() {
15+
}
16+
17+
public Lecturer(Long id, String name) {
18+
this.id = id;
19+
this.name = name;
20+
}
21+
22+
public Long getId() {
23+
return id;
24+
}
25+
26+
public void setId(Long id) {
27+
this.id = id;
28+
}
29+
30+
public String getName() {
31+
return name;
32+
}
33+
34+
public void setName(String name) {
35+
this.name = name;
36+
}
37+
38+
public Integer getFacultyId() {
39+
return facultyId;
40+
}
41+
42+
public void setFacultyId(Integer facultyId) {
43+
this.facultyId = facultyId;
44+
}
45+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.baeldung.hibernate.union.model;
2+
3+
public interface PersonView {
4+
5+
Long getId();
6+
7+
String getName();
8+
9+
String getRole();
10+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.baeldung.hibernate.union.model;
2+
3+
import jakarta.persistence.Entity;
4+
import jakarta.persistence.Id;
5+
6+
@Entity
7+
public class Researcher {
8+
9+
@Id
10+
private Long id;
11+
private String name;
12+
private boolean active;
13+
14+
public Researcher() {
15+
}
16+
17+
public Researcher(Long id, String name) {
18+
this.id = id;
19+
this.name = name;
20+
}
21+
22+
public Long getId() {
23+
return id;
24+
}
25+
26+
public void setId(Long id) {
27+
this.id = id;
28+
}
29+
30+
public String getName() {
31+
return name;
32+
}
33+
34+
public void setName(String name) {
35+
this.name = name;
36+
}
37+
38+
public boolean isActive() {
39+
return active;
40+
}
41+
42+
public void setActive(boolean active) {
43+
this.active = active;
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.baeldung.hibernate.union.repository;
2+
3+
import java.util.List;
4+
5+
import org.springframework.data.jpa.repository.JpaRepository;
6+
import org.springframework.data.jpa.repository.Query;
7+
import org.springframework.stereotype.Repository;
8+
9+
import com.baeldung.hibernate.union.model.Lecturer;
10+
import com.baeldung.hibernate.union.model.PersonView;
11+
12+
@Repository
13+
public interface LecturerRepository extends JpaRepository<Lecturer, Long> {
14+
15+
@Query(value = "select e.id, e.name, e.role from person_view e", nativeQuery = true)
16+
List<PersonView> findPersonView();
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.baeldung.hibernate.union.repository;
2+
3+
import org.springframework.data.jpa.repository.JpaRepository;
4+
import org.springframework.stereotype.Repository;
5+
6+
import com.baeldung.hibernate.union.model.Researcher;
7+
8+
@Repository
9+
public interface ResearcherRepository extends JpaRepository<Researcher, Long> {
10+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package com.baeldung.hibernate.union.service;
2+
3+
import java.util.List;
4+
import java.util.Set;
5+
import java.util.stream.Collectors;
6+
import java.util.stream.Stream;
7+
8+
import org.hibernate.Session;
9+
import org.springframework.stereotype.Service;
10+
11+
import com.baeldung.hibernate.union.dto.PersonDto;
12+
import com.baeldung.hibernate.union.model.Lecturer;
13+
import com.baeldung.hibernate.union.model.Researcher;
14+
import com.baeldung.hibernate.union.repository.LecturerRepository;
15+
import com.baeldung.hibernate.union.repository.ResearcherRepository;
16+
17+
import jakarta.persistence.EntityManager;
18+
import jakarta.persistence.PersistenceContext;
19+
import jakarta.persistence.criteria.CriteriaQuery;
20+
import jakarta.persistence.criteria.Root;
21+
22+
@Service
23+
public class UnionService {
24+
25+
@PersistenceContext
26+
private EntityManager em;
27+
28+
private LecturerRepository lecturerRepository;
29+
30+
private ResearcherRepository researcherRepository;
31+
32+
public UnionService(LecturerRepository lecturerRepository, ResearcherRepository researcherRepository) {
33+
this.lecturerRepository = lecturerRepository;
34+
this.researcherRepository = researcherRepository;
35+
}
36+
37+
public List<PersonDto> fetch() {
38+
return em.createQuery("""
39+
select new PersonDto(l.id, l.name) from Lecturer l
40+
union
41+
select new PersonDto(r.id, r.name) from Researcher r
42+
""", PersonDto.class)
43+
.getResultList();
44+
}
45+
46+
public List<PersonDto> fetchAll() {
47+
return em.createQuery("""
48+
select new PersonDto(l.id, l.name) from Lecturer l
49+
union all
50+
select new PersonDto(r.id, r.name) from Researcher r
51+
""", PersonDto.class)
52+
.getResultList();
53+
}
54+
55+
public List<PersonDto> fetchWithDiscriminator() {
56+
return em.createQuery("""
57+
select new PersonDto(l.id, l.name, 'LECTURER') from Lecturer l
58+
union
59+
select new PersonDto(r.id, r.name, 'RESEARCHER') from Researcher r
60+
""", PersonDto.class)
61+
.getResultList();
62+
}
63+
64+
public List<PersonDto> fetchManually() {
65+
List<Lecturer> lecturers = lecturerRepository.findAll();
66+
List<Researcher> researchers = researcherRepository.findAll();
67+
68+
return Stream.concat(lecturers.stream()
69+
.map(l -> new PersonDto(l.getId(), l.getName(), "LECTURER")),
70+
researchers.stream()
71+
.map(r -> new PersonDto(r.getId(), r.getName(), "RESEARCHER")))
72+
.toList();
73+
}
74+
75+
public Set<PersonDto> fetchSetManually() {
76+
List<Lecturer> lecturers = lecturerRepository.findAll();
77+
List<Researcher> researchers = researcherRepository.findAll();
78+
79+
return Stream.concat(lecturers.stream()
80+
.map(l -> new PersonDto(l.getId(), l.getName(), "LECTURER")),
81+
researchers.stream()
82+
.map(r -> new PersonDto(r.getId(), r.getName(), "RESEARCHER")))
83+
.collect(Collectors.toSet());
84+
}
85+
86+
@SuppressWarnings("unchecked")
87+
public List<PersonDto> fetchView() {
88+
return em.createNativeQuery("select e.id, e.name, e.role from person_view e", PersonDto.class)
89+
.getResultList();
90+
}
91+
92+
public List<PersonDto> fetchWithCriteria() {
93+
var session = em.unwrap(Session.class);
94+
var builder = session.getCriteriaBuilder();
95+
96+
CriteriaQuery<PersonDto> lecturerQuery = builder.createQuery(PersonDto.class);
97+
Root<Lecturer> lecturer = lecturerQuery.from(Lecturer.class);
98+
lecturerQuery.select(builder.construct(PersonDto.class, lecturer.get("id"), lecturer.get("name"), builder.literal("LECTURER")));
99+
100+
CriteriaQuery<PersonDto> researcherQuery = builder.createQuery(PersonDto.class);
101+
Root<Researcher> researcher = researcherQuery.from(Researcher.class);
102+
researcherQuery.select(builder.construct(PersonDto.class, researcher.get("id"), researcher.get("name"), builder.literal("RESEARCHER")));
103+
104+
var unionQuery = builder.unionAll(lecturerQuery, researcherQuery);
105+
106+
return session.createQuery(unionQuery)
107+
.getResultList();
108+
}
109+
}

0 commit comments

Comments
 (0)