Saving ManyToMany associations on inverse side using JPAContainer

This is a tricky issue to tackle, but perhaps someone can offer some suggestions. I have 2 entities: User and Group. They have a ManyToMany relationship to one another, and User owns the association. I have a “Manage Groups” window which has a TwinColSelect component to select which users are part of the group. Saving the entity does not work because I believe I have to change the User, and then save. I’m using JPAContainer, so I’m just trying to figure out if this is something that JPAContainer supports “out of the box”, or if I need to write code somewhere in the JPAContainer “layer”, or of this is something that should go completely outside of Vaadin. Thanks.

Ok, got it figured out and it is surprisingly easy. Basically, create a MutableEntityProvider subclass for the inverse class and follow the pattern below. I’m sure this can be improved (ie. using a BatchableEntityProvider), but I think it’s a great start.


package sandbox.vaadin.data.jpa;

import java.util.HashSet;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.vaadin.addon.jpacontainer.MutableEntityProvider;

import sandbox.entity.Group;
import sandbox.entity.User;

public class MutableLocalGroupEntityProvider extends MutableLocalGenericEntityProvider<Group> {

  @Autowired
  private MutableEntityProvider<User> userEntityProvider;
  
  public MutableLocalGroupEntityProvider() {
    super(Group.class);
  }

  @Override
  public Group updateEntity(Group entity) {
    updateUserGroups(entity.getId(), entity.getUsers());    
    return super.updateEntity(entity);
  }
  
  @SuppressWarnings("unchecked")
  @Override
  @Transactional
  public void updateEntityProperty(Object entityId, String propertyName, Object propertyValue) throws IllegalArgumentException {
    if ("users".equals(propertyName)) {
      updateUserGroups(entityId, (Set<User>) propertyValue);
    }
    
    super.updateEntityProperty(entityId, propertyName, propertyValue);
  }
  
  private void updateUserGroups(Object groupId, Set<User> newUsers) {
    Group group = getEntity(groupId);
    Set<User> currentUsers = group.getUsers();
    
    Set<User> allUsers = new HashSet<User>();
    allUsers.addAll(currentUsers);
    allUsers.addAll(newUsers);
    
    for (User user : allUsers) {
      boolean modified = false;
      
      if (currentUsers.contains(user) && !newUsers.contains(user)) {
        log.debug("Remove " + user.getId() + " from " + group.getId());
        user.getGroups().remove(group);
        modified = true;
      } else
        
      if (!currentUsers.contains(user) && newUsers.contains(user)) {
        log.debug("Add " + user.getId() + " to " + group.getId());
        user.getGroups().add(group);
        modified = true;
      }
      
      if (modified) {
        userEntityProvider.updateEntity(user);
      }
    }
  }
  
}