【问题标题】:JPA @ManyToOne join table issueJPA @ManyToOne 连接表问题
【发布时间】:2016-12-27 06:11:32
【问题描述】:

我正在研究一种适合 GlassFish 的 JDBC 领域要求的身份验证模型。

在这个模型中,我有一个可以包含多个用户的组,但每个用户只能在一个组中(例如 su、admin 等)。

我有两个实体:Groups.java(用于组)和 Credential.java(用于用户)并打算将生成的连接表提供给 Glassfish 的“组表”属性。

我能够持久化 Groups 和 Credential 实例,但是甚至没有创建所需的中间表 (credential_groups),更不用说更新了。

以下是我的实体:

凭据.java:

@Entity
@Access(AccessType.PROPERTY)
@Table(name = "credential")
public class Credential extends MetaInfo implements Serializable {
private String username;
private String passwd;
private Groups group;
private boolean blocked;
private boolean suspended;

public Credential() {
    super();
}

public Credential(String createdBy) {
    super(Instant.now(), createdBy);
}

public Credential(String createdBy, String username, String passwd) {
    this(createdBy);
    this.username = username;
    this.passwd = passwd;
}

@Id
@Column(name = "username")
public String getUsername() {
    return username;
}

public void setUsername(String username) {
    this.username = username;
    updateModified();
}

@Column(name = "passwd")
public String getPasswd() {
    return passwd;
}

public void setPasswd(String passwd) {
    this.passwd = passwd;
    updateModified();
}

@ManyToOne(fetch = FetchType.LAZY)
public Groups getGroups() {
    return group;
}

public void setGroups(Groups group) {
    this.group = group;
    group.getCredentials().add(this);
    updateModified();
}

@Column(name = "is_blocked")
public boolean isBlocked() {
    return blocked;
}

public void setBlocked(boolean blocked) {
    this.blocked = blocked;
    updateModified();
}

@Column(name = "is_suspended")
public boolean isSuspended() {
    return suspended;
}

public void setSuspended(boolean suspended) {
    this.suspended = suspended;
    updateModified();
}
}

Groups.java:

@Entity
@Access(AccessType.PROPERTY)
@Table(name = "groups")
@NamedQueries({
    @NamedQuery(name = "findAllGroups",
            query = "SELECT g FROM Groups g order by g.modifiedDate DESC")
})
public class Groups extends MetaInfo implements Serializable {

private String groupName;
private Set<Credential> credentials;

public Groups() {
    super();
    credentials = new HashSet();
}

public Groups(String groupName) {
    this();
    this.groupName = groupName;
}

public Groups(String createdBy, String groupName) {
    this();
    setCreatedBy(createdBy);
    this.groupName = groupName;
}

@Id
@Column(name = "group_name")
public String getGroupName() {
    return groupName;
}

public void setGroupName(String groupName) {
    this.groupName = groupName;
    updateModified();
}

@OneToMany(mappedBy = "groups")
@JoinTable(
        name = "credential_groups",
        joinColumns = @JoinColumn(name = "group_name"),
        inverseJoinColumns = @JoinColumn(name = "username")
)
public Set<Credential> getCredentials() {
    return credentials;
}

public void setCredentials(Set<Credential> credentials) {
    this.credentials = credentials;
}

public void addCredential(Credential c) {
    credentials.add(c);
    if (c.getGroups() != this) {
        c.setGroups(this);
    }
}
}

正如我所说,坚持这两项工作(对于组,我也尝试过更新、查询),但是这个错误在任何一个实体的每个操作上都会持续触发:

Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Can't write; duplicate key in table '#sql-4d2_54'
Error Code: 1022
Call: ALTER TABLE credential ADD CONSTRAINT FK_credential_GROUPS_group_name FOREIGN KEY (GROUPS_group_name) REFERENCES groups (group_name)
Query: DataModifyQuery(sql="ALTER TABLE credential ADD CONSTRAINT FK_credential_GROUPS_group_name FOREIGN KEY (GROUPS_group_name) REFERENCES groups (group_name)")

重要更新: 这是在粘贴上述异常之前引发的错误:

Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'group VARCHAR(255) NOT NULL, PRIMARY KEY (credential, group))' at line 1
Error Code: 1064
Call: CREATE TABLE credential_groups (credential VARCHAR(255) NOT NULL, group VARCHAR(255) NOT NULL, PRIMARY KEY (credential, group))

根据 Francois Motard 的要求,这是我的触发错误的 jUnit 方法(该组是在另一个测试类中创建的):

public class CredentialRepositoryTests {

private final OCGroupsRepository groupRepo;
private final OCCredentialRepository credentialRepo;

public CredentialRepositoryTests() {
    groupRepo = new OCGroupsRepository();
    credentialRepo = new OCCredentialRepository();
}
...
@Test
    public void createCredentialTest(){
        //retrieve the group
        Groups admin = groupRepo.getByGroupName("admin");
        Credential rimma = new Credential("user_creator", "sample_user", "abc123");
        admin.addCredential(rimma);
        assertTrue(groupRepo.updateGroups(admin));
    }

我的代码基于 Manning 的 EJB 3.0 in Action 中的说明,并尝试根据这个 stackoverflow 答案解决连接表问题:How to create join table with JPA annotations?

谁能帮我创建和更新连接表?非常感谢。

【问题讨论】:

    标签: java mysql jpa glassfish eclipselink


    【解决方案1】:

    通过从拥有实体的@OneToMany 注释中删除mappedBy 属性来解决(这修复了连接表的非创建)通过添加unique 属性(设置为真 - 这解决了“PRIMARY KEY (credential, group)”问题)到同一属性的@JoinTable 内的@JoinColumn 注释:

    @OneToMany
    @JoinTable(
            name = "credential_groups",
            joinColumns = @JoinColumn(name = "group_name"),
            inverseJoinColumns = @JoinColumn(name = "username", unique=true)
    )
    public Set<Credential> getCredentials() {
        return credentials;
    }
    

    主要内容永远不要在 ManyToOne/OneToMany 实体对中将“mappedBy”与 @JoinTable 结合起来。

    【讨论】:

    • 通过删除 mappedBy,您可以解除 OneToMany 与 ManyToOne 的关联。如果这就是你所做的一切,那么它们现在是独立的关系; ManyToOne 映射将默认使用凭证表中的外键,该外键完全独立于 credential_groups 表中的条目。
    • 谢谢你,克里斯。 'CredenitalRepository' 有一个通过测试的 'List getByGroups(String group)' 方法。返回值我没有打印出来,但是返回的大小是正确的。
    猜你喜欢
    • 2011-10-14
    • 2018-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-15
    • 2014-08-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多