Java 重复的键值违反了唯一约束,但它不应';t[Spring Boot,PostgreSQL]

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和浏览器屏幕截图))时,我的程序崩溃,我得到了这个错误(摘录): 还表明它在以下位置崩溃: (这指向控制器中

代码的简短描述:

用户可以创建一个类别。然后,其他用户可以看到这一点和其他类别。然后,他们可以按类别上的“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>