Rest API Spring + ORM - niespójne modele danych

0

Witam, pracuje nad aplikacją. Problem mam złożony więc postaram się bardziej ogólnie. Model danych w relacyjnej bazie jest niespójny z obiektowym modelem danych. Nie mogę obsłużyć na poziomie REST API relacji @ManyToOne. Niby wszystko wydaje się okej, póki nie próbuje uzyskać kolekcji, gdzie tworzy mi się nieskończony ciąg wywołań, gdyż podczas mapowania posłużyłem się referencją do obiektu, która posiada kolekcję...

Owner

@Entity
@Table(name = "OWNER")
public class Owner implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Integer id;

    @OneToMany(mappedBy = "ownerId",
            fetch = FetchType.EAGER,
            cascade = CascadeType.ALL)
    private List<Payments> payments;

Payment

@Entity
@Table(name = "PAYMENTS")
public class Payments implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Integer id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "OWNER_ID",
            foreignKey=@ForeignKey(name="OWNER_ID"),
            nullable = true) // TO FIXED: tymczasowe usunięcie ograniczenia z bazy danych
    private Owner ownerId;

Chciałbym móc dodawać do tabeli klucze obce, móc powiązać płatność z jej posiadaczem. Na razie działa mi to tylko częściowo.

@PostMapping("/insert")
    void insertOwner(@RequestBody Payments body) {
        message.getInfo("insertPayment()", body);
        body.setDate(new Timestamp(System.currentTimeMillis()));
        paymentsRepository.save(body);
    }

Płatność mogę dodać ale nie mogę wskazać na relację klucz obcy bo przy budowania ORM-a posłużyłem się obiektem, w tej sytuacji wystarczyłoby przekazanie liczby ale się nie da. Obsłużyłem co prawda dodanie płatności do tabeli z kluczem obcym ale tym działaniem popsuła mi się metody do wywoływania kolekcji, bo wszystko się zapętla.

* owner

        +---------+---------+------+-----+---------+----------------+
        | Field   | Type    | Null | Key | Default | Extra          |
        +---------+---------+------+-----+---------+----------------+
        | id      | int(11) | NO   | PRI | NULL    | auto_increment |
        | name    | text    | NO   |     | NULL    |                |
        | surname | text    | NO   |     | NULL    |                |
        +---------+---------+------+-----+---------+----------------+
        
* payments

       +----------+-----------+------+-----+-------------------+-------------------+
        | Field    | Type      | Null | Key | Default           | Extra             |
        +----------+-----------+------+-----+-------------------+-------------------+
        | id       | int(11)   | NO   | PRI | NULL              | auto_increment    |
        | amount   | double    | NO   |     | NULL              |                   |
        | date     | timestamp | YES  |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED |
        | kind     | tinytext  | NO   |     | NULL              |                   |
        | owner_id | int(11)   | YES  | MUL | NULL              |                   |
        +----------+-----------+------+-----+-------------------+-------------------

Proszę o wskazówki

1

Dodaj

@JsonManagedReference
@OneToMany

i

@JsonBackReference
@ManyToOne
0

Dziękuje to rozwiązanie działa na tym etapie produkcji mnie zadowala. Nie da się konstruktora zrobić w klasie @Entity i nie widać tych kluczy obcych w tym co zwraca kontroler ale dane się zapisują na poziomie tabeli.

@PostMapping("/insert/{id}")
    void insertOwner(@RequestBody Payments body, @PathVariable Integer id) {
        message.getInfo("insertPayment()", body);
        Owner owner = new Owner();
        owner.setId(id);
        body.setOwnerId(owner);
        body.setDate(new Timestamp(System.currentTimeMillis()));
        paymentsRepository.save(body);
    }
``` 
Gratuluje intuicji :) bo w zasadzie przedstawiłem tylko zarys tego co chce zrobić.
0

Nie, nie, nie. Dlaczego pchasz model bazy danych przez Resta? W ten sposób upubliczniłeś swój wewnętrzny model i nie jesteś go w stanie łatwo zmienić w razie potrzeby. Osobna klasa na encję JPA i osobna na jsona.

1

Jeszcze lepszym pomysłem będzie skorzystanie z adnotacji @JsonIgnore, którą umieścisz po stronie ChildEntity (Payments w Twoim przypadku, pole ownerId).
Poniżej link ze świetnym wyjaśnieniem.
https://stackoverflow.com/a/37394318
PS: Uniezależnij model od twojego Rest API, przyjmuj Dto oraz zwracaj Dto.

2
Charles_Ray napisał(a):

Nie, nie, nie. Dlaczego pchasz model bazy danych przez Resta? W ten sposób upubliczniłeś swój wewnętrzny model i nie jesteś go w stanie łatwo zmienić w razie potrzeby. Osobna klasa na encję JPA i osobna na jsona.

Może tak, może nie.
W niektórych projektach "Encja na twarz i pchasz" jest wystarczającym rozwiązaniem. Np jeśli Encja jest mała. Gdy się rozrośnie zawsze można przerobić Encje na DTO.
Nie bardzo widzę sens w pisaniu nadmiarowych DTO. Przecież to może nigdy się nie przydać. To trochę tak jak robienie interfejsów do wszystkich klas bo może kiedyś będziemy mieć drugą implementację i wtedy będzie łatwiej

2

To wtedy masz klasy typu:

@Entity
@JsonCostam
@XmlElement
@Data
public class Person { ... }

Nie wygląda to przystępnie. Jak encja jest mała, to... niedługo może by większa. Przy takim designie łatwo coś zepsuć, muszę pamiętać, że na klasie jest zapięte wiele różnych mechanizmów mapowania. Czy to nie łamie SRP?

0

To taka bardzo akademicka aplikacja, bez skomplikowanego modelu danych aby pokazać wszystkie poziomy dojrzałości REST API. Jak zawsze podszedłem ambitniej więc myślę musi być także baza oraz widok, bo samo API to nie aplikacja. Przydadzą mi się informacje teoretyczne odnośnie wzorców, bo w zasadzie chodzi o to by pokazać jak robić dobrze przy większych projektach także. DTO warte jest uwagi z poziomu użyteczności API. Uparłem się na dane i mam więcej roboty, a przecież sam opis tworzenia kontrolera można zrobić na wiele rozdziałów. Aplikacja przyda mi się jako wzór, nie chodzi mi o 5+ robię to dla siebie.

Dziękuje za pomoc.

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