OneToMany i zwracanie id

OneToMany i zwracanie id
SA
  • Rejestracja:ponad 3 lata
  • Ostatnio:ponad 2 lata
  • Postów:94
0

Mam dwie encję User i Task w relacji OneToMany. Chciałbym z metody, która dodaje taska do usera zwracać id tego taska, ale zwracany jest null (id w bazie zapisuje się normalnie).

Kopiuj
@RequiredArgsConstructor
class CreateTaskHandler {

private final UserRepository userRepository;
private final TaskContentValidator taskContentValidator;

@Transactional
Either<TaskError, TaskDto> handle(CreateTask command) {
   return userRepository.getByUsername(command.getUsername())
            .map(user -> taskContentValidator.validate(command.getContent())
                    .map(correctContent -> new Task(correctContent, command.getPriority(), command.getStatus(), user))
                    .map(task -> user.addTask(task))
                    .map(addedTask -> new TaskDto(addedTask.getId())))
            .getOrElseThrow(() -> new UserNotFoundException(command.getOwnerUsername()));
   }
}
edytowany 1x, ostatnio: cerrato
VD
  • Rejestracja:ponad 10 lat
  • Ostatnio:10 miesięcy
  • Postów:72
0

Jeśli nie masz konieczności generowania ID przez bazę proponowałbym robić to z poziomu aplikacji. Nie będziesz miał wtedy problemu, a encje będą też mogłby być niemutowalne.

Kopiuj
new Task(randomUUID(), correctContent, command.getPriority(), command.getStatus(), user)

Aktualnie tworzysz "ręcznie" obiekt, a Hibernate w tle wrzuci go do bazy i wygeneruje identyfikator. Tutaj magicznie nie dostaniesz niestety id.

SA
  • Rejestracja:ponad 3 lata
  • Ostatnio:ponad 2 lata
  • Postów:94
0

@VeloxDigitis: Chcę używać id generowanego przez bazę, bo to prostka apka i sensowniej w niej jest używać id typu 1,2 itd. niż kilkunastu znakowych uuidów.
Myślałem jeszcze, żeby użyć po prostu oddzielnego repo dla tasków. Wtedy przy zapisie zwracany byłby task z już wygenerowanym id. Co o tym sądzisz?

ZD
  • Rejestracja:ponad 3 lata
  • Ostatnio:ponad rok
  • Postów:2310
0
VeloxDigitis napisał(a):

Jeśli nie masz konieczności generowania ID przez bazę proponowałbym robić to z poziomu aplikacji. Nie będziesz miał wtedy problemu, a encje będą też mogłby być niemutowalne.

Kopiuj
new Task(randomUUID(), correctContent, command.getPriority(), command.getStatus(), user)

Aktualnie tworzysz "ręcznie" obiekt, a Hibernate w tle wrzuci go do bazy i wygeneruje identyfikator. Tutaj magicznie nie dostaniesz niestety id.

Bezpieczne wątkowo TYLKO przez miliardową przestrzeń UUID.
W przypadku ogólnym generowanie kluczy w aplikacji wcale miodem nie jest.

Sampeteq napisał(a):

@VeloxDigitis: Chcę używać id generowanego przez bazę, bo to prostka apka i sensowniej w niej jest używać id typu 1,2 itd. niż kilkunastu znakowych uuidów.
Myślałem jeszcze, żeby użyć po prostu oddzielnego repo dla tasków. Wtedy przy zapisie zwracany byłby task z już wygenerowanym id. Co o tym sądzisz?

Pozwól mi wejść miedzy wódkę i zakąski ...
Robiłem nie takie małe projekty w JPA, i nigdy nie pojawiła się POTRZEBA znajomości ID (oczywiście jest znana po zapisie)
JPA całkiem dobrze zapisuje sieci powiązanych OBIEKTOWO obiektów, wystarczy mu nie przeszkadzać.

Może się wcinam między j/w, ale nie ściągnąłeś sobie problemu sam?
Na przykład przez niby używanie Hibernate z myśleniem SQL-owym (zawsze dziwne dla mnie) , w szczególności property wskazujące na "ten drugi obiuekt" nie obiektowe, a integer ?
Mówię o

Kopiuj
class Task {
   int userId;
}

vs

Kopiuj
class Task {
   User user;;
}

Wyższym stadium tej samej choroby są zbyt poszatkowane repozytoria, to tylko sposób na pogłębienie prblemu.

Jak chcieć kompletować grupy rekordów ręcznie, panować nad tym na sposób SQL-a - ja bym użył innego toola niż Hibernate, niskopoziomowego mappera


If you put a million monkeys at a million keyboards, one of them will eventually write a Java program - the rest of them will write Perl
ZD
O ILE używam JPA, to używam ortodksyjnie, wtedy inwestycja się zwraca. Użycie w sposób nie-natywny daje same wady i żadnych zalet.
ZD
ELSE robię inną biblioteką
S9
  • Rejestracja:ponad 4 lata
  • Ostatnio:ponad 2 lata
  • Lokalizacja:Warszawa
  • Postów:1092
2
Kopiuj
.map(correctContent -> new Task(correctContent, command.getPriority(), command.getStatus(), user))
.map(task -> user.addTask(task))
.map(addedTask -> new TaskDto(addedTask.getId())))

Ale skąd Ty chcesz mieć to id? Przeciez to nie może działać. Przecież w czasie transakcji task będzie widziany jako obiekt w stanie Transient. Ten spoósb nie działa, po prostu.


Charles_Ray
Nie ma takiego stanu jak Transient :) w czasie transakcji obiekt będzie w stanie New przed „persist()” i Managed po. Gdyby w tej transakcji było jakieś query, to context byłby zflushowany do bazy i w teorii mógłby się ten ID wygenerować.
LU
  • Rejestracja:ponad 11 lat
  • Ostatnio:5 dni
  • Lokalizacja:Gdańsk
0

Spróbuj zrobić tak:

Kopiuj
.map(task -> { 
  var addedTask = user.addTask(task);
  userRepository.flush();
  return addedTask;
})
...

Możesz też spróbować zwrócić encje Task i gdzieś “wyżej” zrobić konwersję do dto. Możesz również spróbować użyć TransactionTemplate i ręcznie wykonać transakcję.


edytowany 1x, ostatnio: lukascode

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.