4
votes

I use these two entities to create a new user and groups it associates:

@Entity  
@Table(name="usertable")  
@SuppressWarnings("serial")  
@Searchable  
public class User implements Serializable {  

    @GeneratedValue(generator="userIdSeq")  
    @SequenceGenerator(name="userIdSeq", sequenceName="usertable_id_seq")      
    @SearchableId  
    @Id  
    int id;  

    @SearchableProperty  
    String userId;                      // 'Unique identifier. It is formed using this fashion: ''prefix:identifier''  
    String userName;                    // Name used to log in the application.  
    String email;                       // User's email  

    @ManyToMany  
    @JoinTable( name="usergroup",  
                joinColumns=@JoinColumn(name = "userid", referencedColumnName="userId"),  
                inverseJoinColumns=@JoinColumn(name = "groupid")  
    )  
    List<Group> groups;  

    ...  
}  

// Group  
@Entity  
@Table(name="grouptable")  
public class Group implements Serializable {  
    @Id  
    String groupId                  // Unique identifier for group.  
    String description              // Brief text describing the group purposes.  

    @ManyToMany(mappedBy="groups")  
    @Cascade(SAVE_UPDATE)  
    List<User> users  

    ....  
}  

On the database: usertable (id, userId, name) grouptable (id, description) usergroup(userid, groupid, groupkey)

It's very satisfying to add Group to list of groups, call save method on User and watch hibernate save the usergoup togehter. However, I have a big problem of hibernate automatically deleting entry from the usergroup table automatically when I retrieve the User after a save(User) operation.

Here is the test sequence that generation the deletion of usergroup

  1. save new user
  2. save new group
  3. And group to list in user
  4. save user
  5. get user from email
    @Test
    @Transactional
    public void testGetUserByEmail(){
        Session session = factory.getCurrentSession(); 

        String email = "[email protected]"; 
        User user = createUser(); 
        user.setWeCanContact(true); 
        user.setPartnersCanContact(false); 
        user.setAgreedTerms(false); 
        user.setReferer("Laura");

        String groupId = "AARP"; 
        String description = "Appletalk Address Resolution Protocol"; 
        Group group1 = new Group(); 
        group1.setGroupId(groupId); 
        group1.setDescription(description); 
        session.save(group1); 
        session.flush(); 
        user.getGroupLists().add(group1); 
        session.saveOrUpdate(user); 

        User testUser = userRepository.getUserByEmail(email);
        assertNotNull(testUser); 
        assertEquals("It's not the correct email", email, testUser.getEmail()); 
    }
    private User createUser(){
        Session session = factory.getCurrentSession(); 
        String userId = "UB:2010"; 
        String userPassword = "sarah"; 
        String realName = "Sarah Silverman"; 
        String email = "[email protected]"; 

        User user = new User();
        user.setUserId(userId); 
        user.setUserName(email); 
        user.setUserPassword(userPassword); 
        user.setRealName(realName); 
        user.setEmail(email); 

        List<Group> emptyGroupLists = new ArrayList<Group>(); 
        user.setGroupLists(emptyGroupLists); 
        Group group = new Group(); 
        group.setGroupId("ABC"); 
        group.setDescription("A for apple");
        user.getGroupLists().add(group);
        session.save(group); 
        session.save(user); 

        session.flush();
        return user; 
    }
// Repository Method to get user from email
// 
//  @NamedQuery(name="user.findByEmail",
//       query="SELECT u FROM User u " +
//    "LEFT JOIN FETCH u.groupLists " +
//              "WHERE upper(u.email) = :email")

public User getUserByEmail(String email) {
    Session session = sessionFactory.getCurrentSession(); 
    Query query = session.getNamedQuery("user.findByEmail");
    query.setParameter("email", email.toUpperCase());
    User user = (User)query.uniqueResult();
    return user; 
}

Before executing step 5, usergroup with automatically removed like // SQL output

Hibernate:
delete
from
usergroup
where
userid=? 

A few thing I to note; 1. userid is NOT the pk of user object. Would that be the problem? 2. The manyTomany mapping in usergroup mapped by userid and groupid 3. It happens not just in Test. It's in development too.

How can I stop the automatic deletion of usergroup. Usergroup is very important table and should not be deleted. Any help would be much appreciated. Thank you.

1
@JohnnyBeGood Unless you show your Test case, we can not help you.Arthur Ronald
@Arthur Thank you for your response. Test case is added. Please let me know if I can clarify anything else. I have been trying with different @Casade option too. Thanks again.JohnnyBeGood
You are a new user. Welcome, but if you see a useful answer, UPvote. Thanks.Arthur Ronald

1 Answers

2
votes

Let's see User

public class User {

    @Id
    @GeneratedValue
    private Integer id;

    @ManyToMany
    @JoinTable(name="USER_GROUP",  
        joinColumns=@JoinColumn(name="USER_ID", referencedColumnName="USER_ID"),
        inverseJoinColumns=@JoinColumn(name="GROUP_ID")
    )
    private List<Group> groupList = new ArrayList<Group>();

}

Now our Group

public class Group {

    @Id
    private String id;

    @ManyToMany(mappedBy="groupList")
    @Cascade(SAVE_UPDATE)
    private List<User> userList = new ArrayList<User>();

}

Before going on, Let's see userList field in a nutshell

// Group.java

@ManyToMany(mappedBy="groupList")
@Cascade(SAVE_UPDATE)
private List<User> userList = new ArrayList<User>();

@Cascade(SAVE_UPDATE) means

Save Or Update each referenced User

mappedBy="groupList" means

For each REFERENCED user, see whether it has a reference to me (Group object). If so, set up our bidirectional relationship. So if you want to link User and Group through USER_GROUP table, each REFERENCED user has to have a reference to a Group object.

Now let's see what is happening in testGetUserByEmail method

Group group = new Group(); 
group.setId(groupId);

// group.getUserList(); returns a empty List, right ?
// As there is no User with reference to group in group.getUserList();
// So NO LINK between Group and User through USER_GROUP table
session.save(group);

// It explains why your link is missing
// You need to set up both sides
user.getGroupLists().add(group);

// Just save a User
// Nothing else
session.saveOrUpdate(user);

In createUser method, you have the same scenario as shown above

So my advice is

When using bidirectional relationship, ALWAYS use a add convenience method

// Group.java
public void addUser(User user) {
    user.getGroupList().add(this);

    getUserList().add(user);
}

// User.java
public void addGroup(Group group) {
    group.getUserList().add(this);

    getGroupList().add(group);
}

regards,