Java 重复的键值违反了唯一约束,但它不应';t[Spring Boot,PostgreSQL]
代码的简短描述: 用户可以创建一个类别。然后,其他用户可以看到这一点和其他类别。然后,他们可以按类别上的“follow”,然后将其保存到数据库中。下面的图片显示了它在数据库中的外观。到目前为止,一切正常 用户可以创建多个类别,类别只能有一个所有者 类别可以有许多追随者,用户可以跟随许多类别 问题 然而,当用户在另一个类别上按“follow”(使用不同的类别ID和all(请参见下面的HTML和浏览器屏幕截图))时,我的程序崩溃,我得到了这个错误(摘录): 还表明它在以下位置崩溃: (这指向控制器中的Java 重复的键值违反了唯一约束,但它不应';t[Spring Boot,PostgreSQL],java,postgresql,spring-boot,hibernate,model-view-controller,Java,Postgresql,Spring Boot,Hibernate,Model View Controller,代码的简短描述: 用户可以创建一个类别。然后,其他用户可以看到这一点和其他类别。然后,他们可以按类别上的“follow”,然后将其保存到数据库中。下面的图片显示了它在数据库中的外观。到目前为止,一切正常 用户可以创建多个类别,类别只能有一个所有者 类别可以有许多追随者,用户可以跟随许多类别 问题 然而,当用户在另一个类别上按“follow”(使用不同的类别ID和all(请参见下面的HTML和浏览器屏幕截图))时,我的程序崩溃,我得到了这个错误(摘录): 还表明它在以下位置崩溃: (这指向控制器中
userService.updateUser(用户);
)
尽管调试器和html都显示我正在传递用户\u id=37,类别\u id=6。
然而如果我停止我的IDE并再次启动它(DB仍然显示first follow,如图中所示),然后我使用同一用户(同一用户id)登录并在另一个类别上按follow,则一切正常,数据库更新(见下图)
然后,若我尝试遵循另一个类别,同样的崩溃再次发生,我将不得不重新启动IDE使其再次工作
目标
用户应该能够跟踪多个类别而不会发生崩溃
我尝试过的内容(没有任何帮助):
我尝试在用户类中添加cascade={CascadeType.PERSIST,CascadeType.MERGE}
重写两个实体类中的Equals和Hashcode
实体
- 使用者
- 用户服务
- 分类服务
- 用户存储库
@存储库
公共接口用户存储库扩展了JpaRepository{
可选findUserByUsername(字符串用户名);
}
- 类别存储库
@存储库
公共接口类别repository扩展了JpaRepository{
}
相关HTML代码(带thymeleaf)
类别:
跟随
它在浏览器中的外观:
DB如何处理以下问题
DB如何关注一次、重新启动IDE,然后关注另一个类别
问题很可能是您没有为
类别
实现equals/hashCode,因此将其添加到集合将添加一个副本,然后尝试将其保留。(编辑)解决了:
找到了实际的答案和解决方案:
不需要从控制器传递用户,而需要从存储库通过其id获取用户
2020-10-09 20:12:21.526 WARN 25512 --- [io-8080-exec-10] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 23505
2020-10-09 20:12:21.526 ERROR 25512 --- [io-8080-exec-10] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: duplicate key value violates unique constraint "followed_categories_pkey"
Detail: Key (user_id, category_id)=(37, 5) already exists.
2020-10-09 20:12:21.526 INFO 25512 --- [io-8080-exec-10] o.h.e.j.b.internal.AbstractBatchImpl : HHH000010: On release of batch it still contained JDBC statements
2020-10-09 20:12:21.529 ERROR 25512 --- [io-8080-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [followed_categories_pkey]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "followed_categories_pkey"
Detail: Key (user_id, category_id)=(37, 5) already exists.
at com.blog.reviewwebsite.controller.CategoryController.followCategory(CategoryController.java:59) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_252]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_252]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_252]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_252]
@Getter
@Setter
@Entity
@Table(name = "users")
public class User implements UserDetails {
//other fields
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Long id;
//This one is used when a user makes a new category
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set<Category> categories;
//This one is used when a user follows existing category
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "followedCategories",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "category_id")
)
private Set<Category> followedCategories = new HashSet<Category>();
}
@Entity
@Getter
@Setter
@Table(name = "Category")
public class Category {
//other fields
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "category_id")
private Long id;
//This one is used when a user makes a new category
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id", nullable = false)
private User user;
//This one is used when a user follows existing category
@ManyToMany(mappedBy = "followedCategories")
private Set<User> users;
}
@Controller
@RequestMapping("/categories")
public class CategoryController {
private CategoryService categoryService;
private UserService userService;
public CategoryController(CategoryService categoryService, UserService userService) {
this.categoryService = categoryService;
this.userService = userService;
}
//other methods
@PostMapping("/follow/{id}")
public String followCategory(@PathVariable Long id, @AuthenticationPrincipal User user) {
Category category = categoryService.getOneById(id);
user.getFollowedCategories().add(category);
userService.updateUser(user);
return "redirect:/categories/all";
}
}
@Service
public class UserService implements UserDetailsService {
private UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
//other methods
public User updateUser(User user) {
return userRepository.save(user);
}
}
@Service
public class CategoryService {
private CategoryRepository categoryRepository;
public CategoryService(CategoryRepository categoryRepository) {
this.categoryRepository = categoryRepository;
}
//other methods
public Category getOneById(Long id) {
return categoryRepository.getOne(id);
}
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findUserByUsername(String username);
}
@Repository
public interface CategoryRepository extends JpaRepository<Category, Long> {
}
<div th:each="category: ${categories}">
<p> Category:
<span><a th:href="@{/reviews/(categoryId=${category.id})}" th:text="${category.name}"></a>
<span th:text="${category.user.username}"></span>
<span th:text="${category.id}"></span>
</span>
</p>
<form method="post" th:action="@{/categories/follow/{id}/(id=${category.id})}">
<span>follow</span>
<input type="submit">
</form>
</div>