Cześć, mam dwa modele; User i Company
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "company")
public class Company {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToMany(mappedBy = "followedCompany")
private Set<User> followedUsers = new HashSet<>();
}
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST,CascadeType.REFRESH}, fetch = FetchType.EAGER)
@JoinTable(
name = "user_company_follow",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "company_id")
)
private Set<Company> followedCompany = new HashSet<>();
public boolean addFollowedCompany(Company company){
if(followedCompany.contains(company)){
return false;
}
company.getFollowedUsers().add(this);
followedCompany.add(company);
return true;
}
}
Kod mocno uprościłem do najważniejszych elementów, aby był bardziej czytelny.
Jak widać, stworzona też jest relacja ManyToMany.
Problem pojawia się w momencie korzystania z endpointa:
@ResponseBody
@RequestMapping(value = "/follow/{id}", method = RequestMethod.POST)
public ResponseEntity<String> followCompany(@PathVariable("id") Long id) {
User user = userSession.getUser();
if (user == null) {
//TODO Obsługa braku zalogowanego użytkownika
return ResponseEntity.badRequest().body("User not logged in");
}
//user = userRepository.findById(user.getId()).get();
Optional<Company> companyOptional = repository.findById(id);
if (companyOptional.isPresent()) {
Company company = companyOptional.get();
boolean added = user.addFollowedCompany(company);
if (added) {
userRepository.save(user);
repository.save(company);
return ResponseEntity.ok("Company followed successfully.");
} else {
return ResponseEntity.badRequest().body("User already follows this company.");
}
} else {
return ResponseEntity.notFound().build();
}
}
Na początku wyciągam użytkownika z sesji, oraz sprawdzam czy obiekt user nie jest null - Czyli czy użytkownik jest zalogowany.
Sesja:
@Data
@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserSession {
private User user;
}
Problem pojawia się, gdy dam obserwacje najpierw jednej firmie, a próbuje dać drugiej. Gdy w między czasie się wyloguje (usunę użytkownika z sesji) i zaloguje ponownie (dodam użytkownika do sesji wyciągniętego z bazy danych), to wszystko działa.
Dostaje taki błąd:
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "user_company_follow_pkey"
Detail: Key (user_id, company_id)=(252, 1) already exists.
252 - ID użytkownika
1 - ID firmy.
Problemem jest to, że identyfikator firmy, którą właśnie zaobserwowałem jest równy 2. Aplikacja próbuje dodać ponownie firmę, którą wcześniej zaobserwowałem (z identyfikatorem = 1) i to powoduje błąd.
Aplikacja tak jakby nie jest świadoma tego, że umieściła już ten rekord wcześniej w tabeli i próbuje umieścić go ponownie.
Co ciekawe, jeśli pobiorę użytkownika ponownie z bazy danych, na podstawie ID użytkownika z sesji:
user = userRepository.findById(user.getId()).get();
to kod działa prawidłowo i nie wyrzuca wspomnianego błędu.
Moje pytanie brzmi. Czy muszę za każdym razem pobierać użytkownika z bazy danych i na nim operować, zamiast korzystać z obiektu wyciągniętego z sesji? Czy musze jakoś odświeżać obiekt w sesji, aby to działało?