Самое быстрое решение (не самое лучшее) заключается в добавлении fecth = EAGER
к отношению OneToMany
изображений ... проблема с этим решением заключается в том, что всегда будет загружаться объект изображений (включая изображение байта []), когдаработа с объектами пользователей (что может быть проблемой производительности) ...
Следующее "лучшее" решение - это опустить конфигурацию EAGER
, описанную ранее, и создать новый метод в вашем хранилище ... такойметод должен выполнить запрос JPA следующим образом:
SELECT ua
FROM
UserAccount ua
LEFT JOIN FECTH ua.images img
WHERE
ua.id = :id
Это загрузит пользователя и связанные с ним изображения ... затем в вашем сервисе вы вызываете такой метод (проблема с этим решением заключается в том, что загружается байт[] image
, даже если вам нужны только другие атрибуты ImageEntity
)
Лучшее решение - расширить решение # 2, чтобы получить только те атрибуты, которые вам нужны для ImageEntity
, в результатев таком запросе:
SELECT
ua,
img.id, img.name, img.type
FROM
UserAccount ua
LEFT JOIN ua.images img
WHERE
ua.id = :id
Затем ваш метод хранилища должен вернуть JPA Tuple
, а в вашем методе обслуживания вы преобразуете этот кортеж в пользователя, которыйвы хотите вернуть (включая метаданные связанных изображений) ... (ОБНОВЛЕНИЕ) Пример (используя метод, который вы указали в ваших комментариях):
// @Transactional // Remove the transactional annotation to avoid cascade issues!
public User getUserById(Long id) {
List<ImageEntity> images;
List<Tuple> tuples;
User user;
tuples = repository.getUserById(id);
user = null;
if (!tuples.isEmpty()) {
user = tuples.get(0).get(0, User.class);
images = new ArrayList<>();
for (Tuple t : tuples) {
if (t.get(1) != null) {
images.add(new ImageEntity(
t.get(1, Long.class),
t.get(2, String.class)
));
}
}
user.setImages(images);
}
return user;
}
Для того чтобыДля работы вам необходимо:
- Изменить сигнатуру метода
getUserById
(в вашем хранилище), чтобы получить список Tuple - Создать метод конструкторав классе ImageEntity с этой подписью:
ImageEntity(long id, String name) { ... }
- Пользовательский объект должен иметь метод
setImages(List<ImageEntity> images) { ... }
UPDATE2: , чтобы сделать что-то вродедля этого при извлечении всех пользователей вам потребуется:
1) Создать (или переопределить) метод в репозитории User, запрос которого будет похож (давайте назовем его findAll):
SELECT
ua,
img.id, img.name, img.type
FROM
UserAccount ua
LEFT JOIN ua.images img
2) В вашем сервисе реализуйте такой метод:
public List<User> findAll(Long id) {
List<ImageEntity> images;
List<Tuple> tuples;
Map<Long, User> index;
tuples = repository.findAll();
index = new HashMap<>();
for (Tuple t : tuples) {
user = t.get(0, User.class);
if (!index.containsKey(user.getId()) {
images = new ArrayList<>();
user.setImages(images);
index.put(user.getId(), user)
} else {
user = index.get(user.getId());
images = user.getImages():
}
if (t.get(1) != null) {
images.add(new ImageEntity(
t.get(1, Long.class),
t.get(2, String.class)
));
}
}
return index.values();
}
ОБЪЯСНЕНИЕ: Ключевым моментом является то, что мы хотим получить пользователя с метаданными изображения (только код, имяи введите) избегая загрузки атрибута lob (потому что изображения могут быть МБ, и они выиграли 't / использоваться / сериализовано) ... вот почему мы выполняем запрос следующим образом:
SELECT
ua,
img.id, img.name, img.type
FROM
UserAccount ua
LEFT JOIN ua.images img
LEFT JOIN
заставляет извлекать всех пользователей (включая тех, у кого нет изображений)
ORM (т. Е. Реализация JPA, например, hibernate) отображает этот тип запроса в объект кортежа (всегда)!
Запроссоздает N x M кортежей ... где N - общее количество пользователей, а M - общее количество изображений ... например, если у вас есть только 1 пользователь с 2 изображениями ... результат будет 2 кортежа, где компонент первого кортежавсегда один и тот же пользователь, остальные компоненты будут атрибутами каждого изображения ...
Затем вам необходимо преобразовать объект кортежа в объект пользователя (это то, что мы делаемв методах обслуживания) ... ключевой момент здесь - использование нового ArrayList
для атрибута images перед добавлением в него нового ImageEntity ... мы должны сделать это, потому что ORM внедряет список прокси для каждогоПользователь загружен ... если мы добавим что-то к этому прокси, ORM execиспользует ленивую загрузку такого прокси, извлекая связанные изображения (чего мы хотим избежать) ...