adnotacje JPA w SpringBoot

0

Cześć,

w restowej aplikacji z użyciem Spring Boota mam taką sytuację:

a) klasa User, gdzie unikalne jest pole String email
b) klasa AbsenceCase, zawierająca m.in. dwa pola typu User:

User user;
User headTeacher;

Podczas zapisywania nowego case'a do tabeli absence_case chcę uniknąć dublowania rekordów w tabeli users i w tym celu korzystam z adnotacji na klasie User @Table(name = "users", uniqueConstraints={@UniqueConstraint(columnNames={"email"})})

Jakie adnotacje hibernate'owe powinny znaleźć się na obydwóch polach typu User w klasie AbsenceCase, żeby wszystko działało poprawnie i równocześnie usunięcie case'a z tabeli absence_case nie powodowało usunięcia usera z tabeli users?

Doszedłem to tego, że powinno to być @ManyToOne, ale nie jestem tego do końca pewien i nie mogę dojść do tego, jak mają wyglądać opcje cascade i fetch.

Główny problem polega na tym, że dodając case'a z już istniejącymi userami Hibernate odmawia współpracy (w różny sposób w zależności od opcji podanych w adnotacji).


Dla jasności wygląd klas:

@Entity
public class AbsenceCase {

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

    private final User user;
    private final User headTeacher;
    private final LocalDate startDate;
    private final LocalDate endDate;
    ...

i

@Entity
@Table(name = "users", uniqueConstraints={@UniqueConstraint(columnNames={"email"})})
public class User {

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

    private final String name;
    private final String surname;
    private final String email;
    private final String password;
    private final String jobTitle;
    private final Boolean isActive;
    private final Position position;
    private final String role;
1

Po pierwsze nasuwa się pytanie czy korzystasz z Hibernate czy JPA? Skoro masz Springa to pewnie masz Spring Data JPA, a ponieważ JPA != Hibernate (Hibernate jest vendorem JPA) to powinienes korzystać z adnotacji JPA.

0

Czy w AbsanceCase masz referecję do wyciągniętych encji z JPA, czy tworzysz referencje na nowych użytkowników?
W ogóle to pokaż kod jak robisz tą aktualizacje, a najlepiej jakbyś miał cały kod na GH :P

0

@scibi92: Aha, kłopoty zaczęły się po zmianach w klasie Initializer (ma mi ona służyć do testowania na bieżąco front-endu). Chciałem osiągnąć usunięcie poprzednich wpisów z bazy i użyłem do tego obydwóch serwisów. No i wyszło mi, że ewidentnie jest kłopot w zapisie do bazy, wcześniej ten błąd nie był widoczny. Zaraz wrzucę na GitHuba aktualną wersję tej klasy.

0

Dobra chyba już widze
1)Przesyłasz encje JPA kontrolerem (brak DTOsów). AbsenceCase nie ma referencji na istniejących userów.
Przy JPA na ogół najlepszym sposbem jest pobrać z spring data-owego repozytorium referencję na instniejących użytkowników, np.


User user = userRepository.findBySomething(something);
OtherEntity otherEntity = new OrderEntity();
otherEntity.setUser(user);

2)Te wyjątki sa tragiczne


@PostMapping(produces = "application/json", consumes = "application/json")
    public ResponseEntity<?> addCase(@RequestBody (required = false) AbsenceCase aCase) throws ServiceOperationException {
        if (aCase == null) {
            logger.error("Attempt to add null case.");
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Attempt to add null case.");
        }
        if (aCase.getId() != null && caseService.caseExists(aCase.getId())) {
            logger.error("Attempt to add case already existing in database.");
            throw new ResponseStatusException(HttpStatus.CONFLICT, "Attempt to add case already existing in database.");
        }
        List<String> validations = AbsenceCaseValidator.validate(aCase);
        if (validations.size() > 0) {
            logger.error("Attempt to add invalid case to database.");
            logger.error(String.valueOf(validations));
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Attempt to add invalid case to database.");
        }
        AbsenceCase addedCase = caseService.addCase(aCase);
        logger.debug("New case added with id: {}.", addedCase.getId());
        Optional<User> headTeacher = userService.getUserById(aCase.getHeadTeacher().getId());
        if (headTeacher.isEmpty()) {
            logger.error("Attempt to send email to Cover Supervisor with details of Head Teacher, who does not exist in database.");
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Attempt to send email to Cover Supervisor with details of Head Teacher, who does not exist in database.");
        }
        Optional<User> user = userService.getUserById(aCase.getUser().getId());
        if (user.isEmpty()) {
            logger.error("Attempt to send email to Cover Supervisor with details of user, who does not exist in database.");
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Attempt to send email to Cover Supervisor with details of user, who does not exist in database.");
        }
        emailService.sendEmailToCoverSupervisor(headTeacher.get(), user.get(), aCase);
        URI location = URI.create(String.format("/api/cases/%d", addedCase.getId()));
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setLocation(location);
        return new ResponseEntity<>(addedCase, httpHeaders, HttpStatus.CREATED);
    }

Jeżeli np. obiekt przesyłany jest nieprawidłowy, to nie trzeba rzucać wyjątkiem, tylko zwrócić inne ResponeEntity (ze statusem błędu i informacją ewentualnie)

Po 3
W ogóle najpierw nauczyć się Javy, jakiegoś OOP, bo to strasznie wygląda. Logika w kontrolerach. wszędzie wyjątki, encje JPA zwracane frontem...

0

@scibi92: Dzięki za uwagi, popracuję nad tym.

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