1

I have this entity User and it has a roles collection(Set) which I want to redis cache so whenever it's calling getRoles(), it return a cached copy.

Here's the structure so far:

  1. User.roles is lazy="true" and access="field".
  2. User.getRoles() has a listener that calls a spring @Cacheable method which retrieves from redis cache rather than 2nd level cache.

Mapping information:

<set
            name="roles"
            table="user_role"
            lazy="true"
            cascade="none"
            access="field"
        >
            <key
                column="user_id"
            >
            </key>
            <many-to-many
                class="com.resolution.scheduler.model.Role"
                column="role_id"
                outer-join="auto"
             />
        </set>

Here is GetRoles():

public Set getRoles() {
        if(!rolesUpdated  &&  this.id!=null){
            ApplicationContextProviderNonManageBean.getApplicationContext().publishEvent(
                 new com.resolution.scheduler.dao.event.UserRoleGetEvent(this));
        }
                 return roles;
    }

Here's what's called when UserRoleGetEvent event fires:

@Cacheable(value="userRoleByUserId" , key="#userId", condition="#userId!=null")
    public PersistentSet getUserRoleByUserId(final Long userId){
        if(userId==null){
            return new PersistentSet();
        }
        log.info("USer Role Getting from Database for User Id : "+userId);
        //final List<Role> roles=(List<Role>)getHibernateTemplate().find("Select u.roles from User u  where u.id=?",userId);
        final PersistentSet[] set= new PersistentSet[1];
        getHibernateTemplate().executeWithNativeSession(new HibernateCallback<Object>(){
            public Object doInHibernate(Session session) throws HibernateException{
                List roles=session.createQuery("Select u.roles from User u  where u.id=:userId").setParameter("userId",userId).list();
                set[0]= new PersistentSet((SessionImpl)session, new HashSet(roles));
                return null;
        }
        });
        log.info(set);

        return set[0];
    }

Here's how UserRoleGetEvent works:

public void onApplicationEvent(UserRoleGetEvent event) {
        PersistentSet roles = userManager.getUserRoleByUserId(event.getUser().getId());
        event.getUser().setRoles((roles)); //**THIS_SET_MAKES_IT_DIRTY**

    }

Problem: When I do user.getRole(), as it set a new collection to user.roles and it makes session dirty. On this dirty session, whenever flush is called it delete all roles and it insert all roles again even when there was no actual change. What I'm looking for is how can I make hibernate to think my new collection is like session retrieved(which is not actually) and don't consider it as dirty.

Any workarounds or suggestion?

Louis Jacomet
  • 13,661
  • 2
  • 34
  • 43
Sachin Verma
  • 3,712
  • 10
  • 41
  • 74

1 Answers1

0

Hibernate doesn't like its collections to be changed. What might work is something like:

event.getUser().getRoles().clear();
event.getUser().getRoles().addAll(roles);

But then, I'm a bit puzzled by what you are trying to do. A getRoles on the User triggers an event that then changes the roles?

As a side note, I'm working by memory here but your code could be refactored to this:

@Cacheable(value="userRoleByUserId" , key="#userId", condition="#userId!=null")
public List<Role> getUserRoleByUserId(final Long userId){
    if(userId==null){
        return Collections.emptyList();
    }
    log.info("USer Role Getting from Database for User Id : "+userId);

    @SuppressWarning("unchecked")
    List<Role> result = (List<Role>) getHibernateTemplate().executeWithNativeSession(new HibernateCallback<Object>(){
        public Object doInHibernate(Session session) throws HibernateException{
            return session.createQuery("Select u.roles from User u  where u.id=:userId").setParameter("userId",userId).list();
        }
    });
    log.info(set);

    return result;
}

public void onApplicationEvent(UserRoleGetEvent event) {
    List<Role> roles = userManager.getUserRoleByUserId(event.getUser().getId());
    event.getUser().getRoles().clear(); // yes, I know, getRoles will throw an event and send you in an infinite loop...  Fixing that just means having another method
    event.getUser().getRoles().addAll(roles);
}
Henri
  • 5,551
  • 1
  • 22
  • 29
  • getRoles() has an event which on trigger, calls getUserRoleByUserId(). Again calling getRoles() will be stackoverflow. – Sachin Verma Mar 14 '17 at 07:51
  • Yes. That's why I said it will in the line comment. Just use a `getRolesThatDontTriggerAnEvent` method instead – Henri Mar 14 '17 at 13:39