Relacja ManyToOne bez duplikatów

0

Cześć,
mój pierwszy techniczny temat na forum, bo do tej pory tylko o błędach w Coyocie.
Zacząłem sobie coś dłubać w springu czego nigdy nie robiłem i mam problem. W zasadzie bardziej z JPA.

Mam jakiegoś CSVa z różnymi operacjami.
Dla uproszczenia niech taka operacja ma datę i zawsze dokładnie jedną kategorię.
Kategorie są stringami, ale ich liczba jest ograniczona, powiedzmy, że mam 20 kategorii a samych operacji kilka tysięcy.
Chcę to zapisać w bazie w taki sposób, że będę miał

operation:
id
date
category_id

category:
id
name

Co ważne, chcę mieć każdą kategorię tylko 1 raz (raczej oczywiste).

No i teraz to co próbowałem, żeby nie było, że chce gotowca.
Wpierw chciałem zrobić 1 klasę

class Operation {
  private Long id;
  private LocalDate date;
  private String category;
}

Ale, że będę chciał mieć też same operacje i przydałaby mi się w sumie taka klasa, to sobie zrobiłem klasę Category:

public class Category {
    private Long id;
    private String name;
}

i zmieniłem category w Operation ze String na Category
Przy wczytywaniu dla każdego wiersza tworzę new Category (stringCategory) i ten obiekt przekazuję dalej do konstruktora klasy Operation.

Moja wersja z adnotacjami wygląda jakoś tak:

@Entity
@Table(name = "operation")
public class Operation {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private LocalDate date;

    @ManyToOne(cascade = CascadeType.ALL)
    private Category category;

   // ....
}
@Entity
public class Category {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "category")
    private Set<Operation> operations;

   // ...
}

Ale tutaj category_id będzie miało kolejne wartości a w tabeli category wartości dla name będą się powtarzać. Po prostu to co dostanę to faktycznie połączone operacje z kategoriami, ale chciałbym, żeby te kategorie były unikalne.

Wiem, że przy wczytywaniu z CSVa mógłbym sobie wczytać wpierw same kategorie do jakiegoś seta i przy tworzeniu obiektu Operation, wybrać odpowiednią i ją tam przekazywać. Miałem gdzieś taką wersję i faktycznie to działało, ale takie rozwiązanie problemu mi się nie podobało.
Stwierdziłem, że to niemożliwe, że nie da się tego rozwiązać przy pomocy odpowiednich adnotacji, ale mimo wielu prób, nie mam pojęcia jak to zrobić.
Jeśli to ważne, to potem wszystkie takie kategorie mam w liście i sobie przekazuję taką listę do saveAll jakiegoś CrudRepository.
Ktoś pomoże?

2

Jeśli chodzi tylko o pewność braku duplikatów, to unique constraint na danej kolumnie na bazie danych i potem w kodzie

@Column(unique=true)
String name;
0

Tak, wiem jak to działa, jednak to zupełnie nie rozwiązuje mojego problemu. Jak to teraz dodam to poleci jakiś exception przy zapisywaniu bo powie mi, że taka kategoria już jest. No a ja wiem, że jest i w takim przypadku właśnie chcę nie insertować nowej, tylko użyć istniejącej. Wziąć jej id i przypisać do category_id.
Jak tak myślę to może za bardzo kombinuję i powinienem do tego podejść inaczej w momencie czytania CSVa i tworzenia tych obiektów zamiast szukać rozwiązania na siłę w inny sposób. Nawet na stacku jedyny temat który w stu procentach odpowiada mojemu problemowi pozostał bez odpowiedzi. Więc może nie jest to takie proste.

2

No tak właśnie myślę że za mocno kombinujesz ;p Niech populowanie bazy za pomocą pliku CSV będzie osobnym krokiem, osobnym jobem, osobną czynnością, jakby w fazie tworzenia. I dopiero później komunikuj się już z bazą. Jeśli to ma być on-the-fly, no to trzeba by zrobić prostego ifa, że szukaj po name tego category, a jak nie ma w bazie, to zapisz i go pobierz.

Ogólnie też struktura encji byłaby łatwiejsza taka, że Operation miałoby gołego Long categoryId (bez żadnych adnotacji), a Category w ogóle nie miałoby w sobie nic powiązanego z Operation - wtedy jakbyś chciał pobrać wszystkie operacje z danej kategorii, to po prostu wołasz repo findAllByCategoryId(Long categoryId).

1 użytkowników online, w tym zalogowanych: 0, gości: 1