简体   繁体   中英

JPA @ManyToOne join table issue

I am working on an authentication model that would suit GlassFish's JDBC Realm requirements.

In this model I have one group which can contain multiple users, but each user can only be in one group (eg su, admin, etc.).

I have two entities: Groups.java (for groups) and Credential.java (for users) and intend to feed the generated join table to Glassfish's "Group Table" property.

I am able to persist both Groups and Credential instances, but the required middle table (credential_groups) is not even created, let alone updated.

Below are my entities:

Credential.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);
    }
}
}

As I said persisting both works (for Groups I have also tried updating, querying), but this error keeps on firing on every operation with either entity:

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)")

Important update: This is the error that is being thrown even before the pasted above exception:

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))

As requested by Francois Motard, here's my jUnit method that triggers the error (the group is created in another test class):

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));
    }

I based my code on the instructions from the EJB 3.0 in Action by Manning and tried to resolve the join table issue based on this stackoverflow answer: How to create join table with JPA annotations?

Can anyone help me have the join table created and updated? Thank you very much.

Resolved by removing the mappedBy attribute from the @OneToMany annotation of the owned entity (this fixed the non-creation of the join table) and by adding the unique attribute (set to true - this solved the " PRIMARY KEY (credential, group) " issue) to the @JoinColumn annotation inside the @JoinTable of the same property:

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

Main take away don't ever ever combine a "mappedBy" with the @JoinTable in a ManyToOne/OneToMany entities pair.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM