【问题标题】:JPA ManyToMany with extra ManyToMany relation具有额外多对多关系的 JPA 多对多
【发布时间】:2019-09-19 10:39:18
【问题描述】:

我需要支持涉及以下实体的场景(使用 JPA):

  1. 用户
  2. 帐户
  3. 角色

一个用户可以有多个帐户,一个帐户可以在多个用户之间共享,这是迄今为止的标准@ManyToMany 关系。

一个用户可以为每个帐户拥有一组不同的角色,一个角色可以在多个用户之间共享。

我关注了this practice,它解释了一种将多对多关联与额外列映射的方法,但我不确定我是否明白。

用户实体:

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

    @Id
    @Column(name = "id", unique = true, nullable = false)
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
    private String id;

    @Column(name = "email", nullable = false)
    private String email;

    @Column(name = "full_name", nullable = false)
    private String fullName;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<UserAccount> usersAccounts;

    public User() {
        usersAccounts= Sets.newHashSet();
    }

    public User(String email, String fullName) {
        this();
        this.email = email;
        this.fullName = fullName;
    }



    public void addAccont(Account account) {
        UserAccount userAccount = new UserAccount(this, account);
        accounts.add(userAccount);
        account.getUsers().add(userAccount);
        this.accounts.add(userAccount);
    }

    public void removeAccont(Account account) {
        for (Iterator<UserAccount> iterator = accounts.iterator(); iterator.hasNext(); ) {
            UserAccount userAccount = iterator.next();

            if (userAccount.getUser().equals(this) &&
                    userAccount.getAccount().equals(account)) {
                iterator.remove();
                userAccount.getAccount().getUsers().remove(userAccount);
                userAccount.setUser(null);
                userAccount.setAccount(null);
            }
        }
    }

    //Getters  Setters..

}

账户实体:

@Entity
@Table(name = "accounts")
public class Account implements Serializable {

    @Id
    @Column(name = "id", unique = true, nullable = false)
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
    private String id;

    @Column(name = "name", nullable = false)
    private String name;

    @Column(name = "billing_address", nullable = false)
    private String billingAddress;

    @OneToMany(mappedBy = "account", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<UserAccount> usersAccounts;


    public Account() {
        usersAccounts= Sets.newHashSet();
    }

    //Getters  Setters..
}

UserAccount 实体:

@Entity
@Table(name = "users_accounts")
public class UserAccount implements Serializable {

    @EmbeddedId
    private UserAccountId id;

    @ManyToOne(
        fetch = FetchType.LAZY,
        cascade = {
                CascadeType.PERSIST,
                CascadeType.MERGE
        })
    private User user;

    @ManyToOne(
        fetch = FetchType.LAZY,
        cascade = {
                CascadeType.PERSIST,
                CascadeType.MERGE
        })
    private Account account;

    @ManyToMany(
        fetch = FetchType.LAZY,
        cascade = {
                CascadeType.PERSIST,
                CascadeType.MERGE
        })
    @JoinTable(
        name = "users_accounts_roles",
        joinColumns = {
                @JoinColumn(name = "user_id", referencedColumnName = "user_id"),
                @JoinColumn(name = "account_id", referencedColumnName = "account_id"),
        },
        inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")
)
private Set<Role> roles;


    private UserAccount() {}

    public UserAccount(@NotNull User user, @NotNull Account account) {
        this.user = user;
        this.account = account;
        roles = Sets.newHashSet();
        this.id = new UserAccountId(user.getId(), account.getId());
    }

    //Getters  Setters..

}

用户帐户 ID:

@Embeddable
public class UserAccountId implements Serializable {

    @Column(name = "user_id")
    private String userId;

    @Column(name = "account_id")
    private String accountId;

    private UserAccountId() {
    }

    public UserAccountId(
            String userId,
            String accountId) {
        this.userId = userId;
        this.accountId = accountId;
    }

    //Getters  Setters..
}

我正在创建一个新用户并尝试将其保存到数据库:

User user = new User("some.email@mail.com", "John Doe");
userRepository.save(savedEntity);

我收到 JpaSystemException:

Caused by: java.sql.SQLException: Field 'user_account_account_id' doesn't have a default value

查看hibernate的create table语句我看到了:

create table users
(
  id                      varchar(255) not null
    primary key,
  email                   varchar(255) not null,
  full_name               varchar(255) not null,
  user_account_account_id varchar(255) not null,
  user_account_user_id    varchar(255) not null,
  constraint UK_exxyrhm7e34pwn8dvem8wxuxu
  unique (user_account_account_id, user_account_user_id),
  constraint FKhti2663qxk7qo15f7gfnnaj7r
  foreign key (user_account_account_id, user_account_user_id) references users_accounts (account_id, user_id)
)
  engine = InnoDB;

我不清楚标记为not nulluser_account_account_iduser_account_user_id 列,据我所知,可以在没有帐户的情况下创建用户。

为什么要以这种方式创建表?我怎样才能让它发挥作用?

【问题讨论】:

    标签: hibernate jpa orm spring-data-jpa


    【解决方案1】:

    这对/有意义吗?

    您的解决方案会奏效,只是它听起来是一个非常复杂的模型。您确定多对多关联不应该在RoleAccount(而不是UserAccount)之间吗?换句话说,Account 会根据哪个User 使用不同的Roles 吗?

    另外,我在您建议的映射中发现了一个问题。你可能想要:

    @JoinTable(
                name = "users_accounts_roles",
                joinColumns = {
                    @JoinColumn(name = "user_id", referencedColumnName = "user_id"),
                    @JoinColumn(name = "account_id", referencedColumnName = "account_id"),
                },
                inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")
        )
    

    (因为在UserAccount 中没有要引用的id 列;而是有两个主键列)

    【讨论】:

    • 关于您的问题“帐户是否会根据使用它的用户而具有不同的角色?” - 是的,用户 A 可能在账户 A' 中有一组角色,而用户 B 在账户 A' 中可能有一组不同的角色。关于您的建议 - 已修复,谢谢。
    【解决方案2】:

    我的感觉是user_account_account_iduser_account_user_id 列都是您之前尝试的剩余部分。您可以安全地删除它们,一切都会正常工作。

    为了安全起见,您可以完全删除 users 表并从头开始重新创建它。

    【讨论】:

    • 嗯,我们清楚地看到 HN 认为你有从 User 到 UserAccount 的 FK。我们可以看到您的实体声明中没有这样的 FK。
    猜你喜欢
    • 2013-11-14
    • 2017-08-24
    • 2023-03-21
    • 1970-01-01
    • 2023-02-02
    • 2019-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多