6

I have two table with many-to-many relations. I mapped this two entities when I am persisting users doesn't insert anything to join table. I am debugging till the persist, I see groups list is not null.

Ther is no error message just persist user.

users <--> user-group <--> groups

I am using netbeans 7.3, Glassfish 3.1.2.2, postgresql 9.1 and eclipselink 2

Also, I tried to show sql scripts that properties below doesn't work for me.

        <property name="eclipselink.logging.logger" value="ServerLogger"/>
        <property name="eclipselink.logging.level" value="FINE"/>
        <property name="eclipselink.logging.level.sql" value="FINE"/>
        <property name="eclipselink.logging.parameters" value="true"/>

Abstact DAO:

public abstract class GenericDAO<E> implements Serializable{

@PersistenceContext
EntityManager entityManager;

public void persist(E object){
    entityManager.persist(object);
}

public void merge(E object){
    entityManager.merge(object);
}

public void delete(E object){
    object = entityManager.merge(object);
    entityManager.remove(object);
}
}

Users Entity :

   @Entity
@Table(name = "users")
@XmlRootElement
@NamedQueries(
{
    @NamedQuery(name = "Users.findAll", query = "SELECT u FROM Users u"),
    @NamedQuery(name = "Users.findByUserId", query = "SELECT u FROM Users u WHERE u.userId = :userId"),
    @NamedQuery(name = "Users.findByName", query = "SELECT u FROM Users u WHERE u.name = :name"),
    @NamedQuery(name = "Users.findBySurname", query = "SELECT u FROM Users u WHERE u.surname = :surname"),
    @NamedQuery(name = "Users.findByEmail", query = "SELECT u FROM Users u WHERE u.email = :email"),
    @NamedQuery(name = "Users.findByUsername", query = "SELECT u FROM Users u WHERE u.username = :username"),
    @NamedQuery(name = "Users.findByPassword", query = "SELECT u FROM Users u WHERE u.password = :password")
})
public class Users implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "user_id")
    private Integer userId;
    @Size(max = 2147483647)
    @Column(name = "name")
    private String name;
    @Size(max = 2147483647)
    @Column(name = "surname")
    private String surname;
    // @Pattern(regexp="[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", message="Invalid email")//if the field contains email address consider using this annotation to enforce field validation
    @Size(max = 2147483647)
    @Column(name = "email")
    private String email;
    @Size(max = 2147483647)
    @Column(name = "username")
    private String username;
    @Size(max = 2147483647)
    @Column(name = "password")
    private String password;
    @ManyToMany(mappedBy = "usersList")
    private List<Groups> groupsList;
    @OneToMany(mappedBy = "userId")
    private List<Person> personList;

//Getters Setters

Groups Entity :

@Entity
@Table(name = "groups")
@XmlRootElement
@NamedQueries(
{
    @NamedQuery(name = "Groups.findAll", query = "SELECT g FROM Groups g"),
    @NamedQuery(name = "Groups.findByGroupId", query = "SELECT g FROM Groups g WHERE g.groupId = :groupId"),
    @NamedQuery(name = "Groups.findByGroupName", query = "SELECT g FROM Groups g WHERE g.groupName = :groupName"),
    @NamedQuery(name = "Groups.findByGroupDescription", query = "SELECT g FROM Groups g WHERE g.groupDescription = :groupDescription")
})
public class Groups implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "group_id")
    private Integer groupId;
    @Size(max = 2147483647)
    @Column(name = "group_name")
    private String groupName;
    @Size(max = 2147483647)
    @Column(name = "group_description")
    private String groupDescription;
    @JoinTable(name = "user_group", joinColumns =
    {
        @JoinColumn(name = "group_id", referencedColumnName = "group_id")
    }, inverseJoinColumns =
    {
        @JoinColumn(name = "user_id", referencedColumnName = "user_id")
    })
    @ManyToMany(fetch = FetchType.EAGER)
    private List<Users> usersList;
//Getters Setters

User DAO :

public class UserDAO extends GenericDAO<Users> implements Serializable {

    public List<Users> getAllUsers()
    {
        Query query = entityManager.createNamedQuery("Users.findAll");
        List<Users> users = query.getResultList();
        return users;
    }
}
erdoganonur
  • 98
  • 1
  • 1
  • 10

2 Answers2

7

You need to enable cascade for merge (using PERSIST) or all operations with ALL.

@ManyToMany(mappedBy = "usersList", cascade = CascadeType.PERSIST)
private List<Groups> groupsList;

If I set CascadeType.PERSIST, it insert data to groups table too. I want to add date users table and user_group table (pk_user, pk_group)

The way a mapping/join table works is that user_group would have a foreign key constraint on user and group table. That's why a new row in group has to be inserted for its primary key to be used to add a new row to user_group.

This has nothing to do with JPA and the same would apply to you even if you were using plain JDBC instead. This is how Entity-Relationships work in database.

Also I dropped all tables and they were generated by eclipse link automatically. They are same but don't insert any row to 'user_group'.

This behaviour is controlled by the eclipselink.ddl-generation property specified for your persistence-unit. When specified as drop-and-create-tables, EclipseLink recreates the whole database schema (deleting any existing data in the process).

<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>

This, however, is enabled just to ease your development. This isn't supposed to be used in production environments where its disabled by not specifying this property or by setting its value as none.

<property name="eclipselink.ddl-generation" value="none"/>
Ravi K Thapliyal
  • 51,095
  • 9
  • 76
  • 89
  • If I set CascadeType.PERSIST, it insert data to groups table too. I want to add date users table and user_group table (pk_user, pk_group) – erdoganonur Oct 19 '13 at 12:05
  • That's very unusual. `user_group` would have a foreign key constraint dependent on `group` table. That's why `group` is inserted and then its primary key used to add a new row to `user_group`. This won't be possible otherwise. – Ravi K Thapliyal Oct 19 '13 at 12:10
  • 'user_group' has fk with 'group' and 'user' table. Also I dropped all tables and they were generated by eclipse link automatically. They are same but don't insert any row to 'user_group' – erdoganonur Oct 19 '13 at 13:53
  • The 'user_group' table has foreign key with 'users' and 'groups'. I understand jpa's behavior when cascade strategy is persist. But I still can't handle this. You can check my project at git : https://github.com/erdoganonur/MkmTurizm.Thanks for your help. – erdoganonur Oct 24 '13 at 06:55
1

For your first question about to show sql scripts, i've added this code in the persistence.xml

<properties>
  <property name="eclipselink.logging.level.sql" value="FINE"/>
  <property name="eclipselink.logging.parameters" value="true"/>
</properties>

After that, clean and build and then deploy. The sql executed by JPA will be displayed in the glassfish domain log of the netbeans.

For your second issue about the many-to-many relationship.

i was having the same issue in the same scenario, beacuse i've to implement web security and i need the same relationship you are using. Anyway, i figured out that the problem wasn't in the entities.

You have to check (for my case) the insert and edit methods on the UserJPAController.java, those methods some how knows how the entities are referenced each other, so i implemented this code and it works fine.

List<Grupo> attachedGrupoList = new ArrayList<Grupo>();
        for (Grupo grupoListGrupoToAttach : usuario.getGrupoList()) {
            grupoListGrupoToAttach = em.getReference(grupoListGrupoToAttach.getClass(),      grupoListGrupoToAttach.getIdGrupo());
            attachedGrupoList.add(grupoListGrupoToAttach);
        }
        usuario.setGrupoList(attachedGrupoList);
        em.persist(usuario);
for (Grupo grupoListGrupo : usuario.getGrupoList()) {
            grupoListGrupo.getUsuarioList().add(usuario);
            grupoListGrupo = em.merge(grupoListGrupo);
        }

After executing that code and the transaction has ended you will see the sql scripts executed by JPA in the Data Base.

Hope it helps!

Mike