@ManyToMany 保存关系在一个方向上只能使用一次

Posted

技术标签:

【中文标题】@ManyToMany 保存关系在一个方向上只能使用一次【英文标题】:@ManyToMany saving relations is available only one time in one direction 【发布时间】:2020-04-16 08:12:30 【问题描述】:

我将尝试在我的 SpringBoot 应用程序中创建 @ManyToMany 关系。 我使用 JPARespository,我创建了 2 个模型:

用户

@Entity(name = "User")
@Table(name = "users")
public class User 

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    @ManyToMany//(cascade = CascadeType.MERGE, CascadeType.PERSIST)
    @LazyCollection(value = LazyCollectionOption.FALSE)
    @JoinTable(
            name = "users_groups",
            joinColumns = @JoinColumn(name = "users_id"),
            inverseJoinColumns = @JoinColumn(name = "groups_id")
    )
    private Set<UGroup> groups;

    public User() 
        this.groups = new HashSet<>();
    

    public void addGroup(UGroup uGroup) 
        this.groups.add(uGroup);
        uGroup.users.add(this);
    

    public void removeGroup(UGroup uGroup) 
        this.groups.remove(uGroup);
        uGroup.users.remove(this);
    

UGroups

@Data
@Entity(name = "UGroup")
@Table(name = "groups")
public class UGroup 

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    @ManyToMany(mappedBy = "groups", cascade = CascadeType.MERGE, CascadeType.PERSIST)
    @LazyCollection(value = LazyCollectionOption.FALSE)
    public Set<User> users;

    public UGroup() 
        this.users = new HashSet<>();
    

    public void addUser(User user) 
        this.users.add(user);
        user.getGroups().add(this);
    

    public void removeUser(User user) 
        this.users.remove(user);
        user.getGroups().remove(this);
    

我通过 DataConfiguration 类加载启动数据:

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) 
        User user = new User();
        user.setName("Damian");
        userRepository.save(user);

        user = new User();
        user.setName("Marta");
        userRepository.save(user);

        user = new User();
        user.setName("Natalia");
        userRepository.save(user);

        UGroup uGroup = new UGroup();
        uGroup.setName("Mieszkanie");
        uGroup = uGroupRepository.save(uGroup);

        user = userRepository.findById(1).get();
        user.addGroup(uGroup);
        user = userRepository.save(user);

        // unable to execute
        /*
        user = userRepository.findById(2).get();
        user.addGroup(uGroup);
        user = userRepository.save(user);
         */

        // unable to execute 2
        /*
        uGroup = uGroupRepository.findById(4).get();
        uGroup.addUser(userRepository.findById(3).get());
        uGroup = uGroupRepository.save(uGroup);
         */
    

当我只为 1 个用户执行插入组时,一切都很好。 虽然当我尝试为 1 和 2 个用户执行插入组或为组插入 3 个用户时,它会导致 java.lang.***Error: null 异常。

我不知道哪里出错了。谁能帮助我并解释我应该在哪里以及为什么要更改某些内容? 提前致谢。

【问题讨论】:

您需要提及连接表和列以使您的 manyTomany 工作 如果我知道正确@JoinTable 是没有必要的,但我尝试使用它。仍然无法正常工作,同样的异常。 通过堆栈跟踪发布您遇到的错误/异常,以便更容易解释/调试问题。 【参考方案1】:

通过Join Table,可以映射ManyToMany双向关系。这些是修改后的实体类

import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity(name = "User")
@Table(name = "users")
public class User 

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    public Integer getId() 
        return id;
    

    public String getName() 
        return name;
    

    public Set<UGroup> getGroups() 
        return groups;
    

    public void setGroups(Set<UGroup> groups) 
        this.groups = groups;
    

    public void setName(String name) 
        this.name = name;
    

    @ManyToMany(mappedBy = "users",cascade = CascadeType.ALL)
    @LazyCollection(value = LazyCollectionOption.FALSE)
    private Set<UGroup> groups=new HashSet<UGroup>();

    public User() 
    

    public void addGroup(UGroup uGroup) 
        this.groups.add(uGroup);
        uGroup.users.add(this);
    

    public void removeGroup(UGroup uGroup) 
        this.groups.remove(uGroup);
        uGroup.users.remove(this);
    


import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity(name = "UGroup")
@Table(name = "groups")
public class UGroup 

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String name;

    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name = "USER_GROUP_MAP" ,joinColumns = @JoinColumn(name="GROUP_ID",referencedColumnName = "ID")
            ,inverseJoinColumns = @JoinColumn(name="USER_ID",referencedColumnName = "ID"))
    @LazyCollection(value = LazyCollectionOption.FALSE)
    public Set<User> users=new HashSet<>();

    public UGroup() 
    

    public void addUser(User user) 
        this.users.add(user);
        user.addGroup(this);
    

    public void removeUser(User user) 
        this.users.remove(user);
        user.removeGroup(this);
    

    public Integer getId() 
        return id;
    

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public Set<User> getUsers() 
        return users;
    

    public void setUsers(Set<User> users) 
        this.users = users;
    

对于多对多关系,连接表是必要的。

SELECT * FROM USER_GROUP_MAP;
GROUP_ID    USER_ID  
4            1
4            2
4            3

这是您发布的代码的结果。您应该覆盖两个实体类中的 hashcode 和 equals 方法,以避免表中的数据重复。

用于测试上述关系映射的代码。

@Override
public void onApplicationEvent(ContextRefreshedEvent applicationEvent) 
    User user = new User();
    user.setName("Damian");
    userRepository.save(user);

    user = new User();
    user.setName("Marta");
    userRepository.save(user);

    user = new User();
    user.setName("Natalia");
    userRepository.save(user);

    UGroup uGroup = new UGroup();
    uGroup.setName("Mieszkanie");
    uGroup = uGroupRepository.save(uGroup);

    user = userRepository.findById(1).get();
    user.addGroup(uGroup);
    user = userRepository.save(user);

    // unable to execute

    user = userRepository.findById(2).get();
    user.addGroup(uGroupRepository.findById(4).get());
    user = userRepository.save(user);


    // unable to execute 2

    uGroup = uGroupRepository.findById(4).get();
    uGroup.addUser(userRepository.findById(3).get());
    uGroup = uGroupRepository.save(uGroup);


【讨论】:

我执行了你的代码。它对我不起作用... :-( 我重写了 equals 和 hashCode 方法,运行后我收到:Unique index or primary key violation: "PUBLIC.PRIMARY_KEY_C ON PUBLIC.USER_GROUP_MAP(GROUP_ID, USER_ID) VALUES 1"; SQL statement: insert into user_group_map (group_id, user_id) values (?, ?) [23505-199] 似乎您正在关联 USER_GROUP_MAP 表中的重复条目。Hibernate 正在尝试为相同的关联插入重复的行。还要从等号代码中排除 Id,因为在这种情况下 id 是代理键。 是的,我知道,我在评论之前搜索了这个错误,但是每次运行程序都会创建 H2 数据库,所以数据库肯定是空的。具有数据加载的类运行一次。我不知道我应该做什么。 由于onApplicationEvent尝试了spring的生命周期,数据可能会被插入两次。尝试调试此方法以检查它是否被调用了两次。 Hurra 它有效:-D 非常感谢。另外我观察到使用lookbook有时会导致***Error,所以我必须忘记使用它:-(

以上是关于@ManyToMany 保存关系在一个方向上只能使用一次的主要内容,如果未能解决你的问题,请参考以下文章

在 typeORM 中的 @ManyToMany 中保存关系会覆盖先前的条目

@ManyToMany 关系不保存

Symfony2-Doctrine:ManyToMany 关系未保存到数据库

多对多manytomany

保存方法manytomany

Symfony2 2.3.7 -Doctrine 2.4.1:ManyToMany 关系未保存到数据库