@pasha_a
Люблю ставить перед собой цели и добиваться их.

Как делается правильная выборка данных со связанных таблиц БД при использовании JPA?

Добрый день!

Помогите пожалуйста понять как правильно строить архитектуру проекта и работу с БД.
Объясняю суть проблемы.

Есть приложение написанное на Java с использованием Spring, Hibernate.

Допустим есть БД по школе, состоящая из нескольких таблиц, связанных между собой:
//пользователи(преподаватели+директора)
CREATE TABLE appuser (
	appuser_id serial NOT NULL,
	fio VARCHAR(50) UNIQUE,
	PRIMARY KEY (appuser_id)
);

CREATE TABLE school (
	school_id serial NOT NULL,
	school_number VARCHAR(10) UNIQUE,
	director INT, /* директор */
	PRIMARY KEY (school_id)
);
ALTER TABLE school ADD CONSTRAINT school_fk0 FOREIGN KEY (director) REFERENCES appuser(appuser_id);

CREATE TABLE subject (
	subject_id serial NOT NULL,
	description VARCHAR(30) UNIQUE,
	PRIMARY KEY (subject_id)
);

//учитель может читать разные предметы в разной школе
CREATE TABLE teacher_school_subject (
	tss_id serial NOT NULL,
	teacher INT,
	school INT,
	subject INT,
	PRIMARY KEY (tss_id)
);
ALTER TABLE teacher_school_subject ADD CONSTRAINT teacher_school_subject_fk0 FOREIGN KEY (teacher) REFERENCES appuser(appuser_id);
ALTER TABLE teacher_school_subject ADD CONSTRAINT teacher_school_subject_fk1 FOREIGN KEY (school) REFERENCES school(school_id);
ALTER TABLE teacher_school_subject ADD CONSTRAINT teacher_school_subject_fk2 FOREIGN KEY (subject) REFERENCES subject(subject_id);


Сущности(привожу только часть параметров чтобы не загромождать страницу):
@Entity  
@Table(name = "teacher_school_subject")
public class TeacherSchoolSubject implements Serializable {
    @JoinColumn(name = "teacher", referencedColumnName = "appuser_id")
    @ManyToOne(fetch = FetchType.EAGER)
    private Appuser teacher;
	
    @JoinColumn(name = "school", referencedColumnName = "school_id")
    @ManyToOne(fetch = FetchType.EAGER)
    private School school;
	
    @JoinColumn(name = "subject", referencedColumnName = "subject_id")
    @ManyToOne(fetch = FetchType.EAGER)
    private Subject subject;
}

@Entity  
@Table(name = "school")
public class School implements Serializable {
    @JoinColumn(name = "director", referencedColumnName = "appuser_id")
    @ManyToOne(fetch = FetchType.EAGER)
    private Appuser director;	
}

public interface TeacherSchoolSubjectRepository 
                extends PagingAndSortingRepository<TeacherSchoolSubject, Integer> {
	@Query(value = "SELECT * FROM teacher_school_subject", nativeQuery = true)
	List<TeacherSchoolSubject> findAllTSS();
}

Например я хочу сделать выборку всех записей из teacher_school_subject.
При этом я получаю список объектов TeacherSchoolSubject.

Однако этот запрос вызывает за собой множество отдельных уточняющих запросов по каждой из таблиц после основного SELECT * запроса.
Видел что можно использовать @SqlResultSetMapping и запрос типа
@Query(value = "SELECT * FROM teacher_school_subject AS tss "
            + "INNER JOIN appuser au ON au.appuser_id=tss.teacher "
            + "INNER JOIN school s ON s.school_id=tss.school "
            + "INNER JOIN subject sub ON sub.subject_id=tss.subject ", nativeQuery = true)
    List<TeacherSchoolSubject> findAllTSSJoin();

Правильный ли это подход?
Или не переживать по поводу того что идет множество запросов?

Как вообще правильно работать осуществлять выборку данных с тем чтобы подхватывались и связанные данные из других таблиц?

Всегда ли стоит использовать FetchType.EAGER в связях ManyToOne на стороне Many?
Ведь у меня фактически получается глубина вложенности
TeacherSchoolSubject.school -> School.director -> Appuser
, но она ж может быть и больше. И получается что при выборе из верхней таблицы Hibernate вынужден делать по каждой строке уточняющие запросы на выборку более глубоколежащих данных.
  • Вопрос задан
  • 3917 просмотров
Решения вопроса 2
Maksclub
@Maksclub
maksfedorov.ru
Для удобства можете использовать Criteria API, по сути сможете собирать запросы довольно близко к SQL, но манипулируя своими сущностями и их свойствами, добавляя аргументы и условия сборки.

Примеры:
- LEFT JOIN: https://www.logicbig.com/tutorials/java-ee-tutoria...
- INNER JOIN: https://www.logicbig.com/tutorials/java-ee-tutoria...

Приджойните нужные сущности, определите как выбирать те или иные данные. И такие выборки можете делать удобными, через нужные условия в своем репозитории.
Ответ написан
ИМХО если хотите вытащить данные одним запросом - напишите один запрос. :-)
Если вам нужны не все данные из сущностей, а только часть колонок, то создаете сущность с нужными колонками (без @Table), и через Native Query ее делаете запрос.
Запрос будет один. :-)
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы