使用 Simple-Spring-Memcached: SingleCache

上一篇大概讲了一下 SSM anotation。这章详细看一下 SingleCache 的使用。

首先是接下来的几个内容都会用到的两个 POJO

Role Model:

public class Role implements Serializable {

    private static final long serialVersionUID = -4708064835003250669L;

    private Long id;

    private String name;

    @CacheKeyMethod
    public String cacheKey() {
        return id.toString();
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean equals(Object obj) {
        return EqualsBuilder.reflectionEquals(
                this, obj);
    }

    public int hashCode() {
        return HashCodeBuilder
                .reflectionHashCode(this);
    }

    public String toString() {
        return ToStringBuilder.reflectionToString(
                this, ToStringStyle.MULTI_LINE_STYLE);
    }
}

User Model:

public class User implements Serializable {

    private static final long serialVersionUID = -822125371522084989L;

    private Long id;

    private String name;

    private Role role;

    @CacheKeyMethod
    public String cacheKey() {
        return id.toString();
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Role getRole() {
        return role;
    }

    public void setRole(Role role) {
        this.role = role;
    }

    public boolean equals(Object obj) {
        return EqualsBuilder.reflectionEquals(
                this, obj);
    }

    public int hashCode() {
        return HashCodeBuilder
                .reflectionHashCode(this);
    }

    public String toString() {
        return ToStringBuilder.reflectionToString(
                this, ToStringStyle.MULTI_LINE_STYLE);
    }
}

场景一:根据某个 user ID 查询某个 User。在更新时,更新缓存中的这个 User。

Method:

@Override
@ReadThroughSingleCache(namespace = "user", expiration = 600)
public User getUser(@ParameterValueKeyProvider Long id) {
    return (User) sqlMapClientTemplate.queryForObject("getUser", id);
}

@Override
@UpdateSingleCache(namespace = "user", expiration = 60)
public void updateUser(@ParameterValueKeyProvider @ParameterDataUpdateContent User user) {
    sqlMapClientTemplate.update("updateUser", user);
}

SQL:

<update id="updateUser" parameterClass="user">
    UPDATE user
    SET name = #name#
    WHERE id = #id#
</update>

<select id="getUser" parameterClass="java.lang.Long" resultClass="user">
    SELECT * FROM user WHERE id = #id#
</select>

只要有相同的 namespace

  • 在 getUser 时,会根据 @ParameterValueKeyProvider 找到 User 对象的 @CacheKeyMethod 方法,到 Memcached 中 get user:id。
  • 在 updateUser 时,会根据 @ParameterValueKeyProvider 找到 User 对象的 @CacheKeyMethod 方法,到 Memcached 中 set user:id

场景二:根据某个 role ID 查询所有的 User。查询关联两个以上的 Model(User,Role),在更新 Role 时,需要让相关的缓存失效。

Method:

@ReadThroughSingleCache(namespace = "user/list", expiration = 600)
public List<User> getUsersByRoleId(@ParameterValueKeyProvider Long id) {
    return (List<User>) sqlMapClientTemplate.queryForList("getUsersByRoleId", id);
}

SQL:

<select id="getUsersByRoleId" parameterClass="java.lang.Long" resultClass="user">
    SELECT u.id, u.name, r.id as "role.id", r.name as "role.name"
      FROM user u, user_role ur, role r
     WHERE u.id = ur.userid and r.id = ur.roleid and ur.roleid = #id#
</select>     
 

当更新 Role 时:

@UpdateSingleCache(namespace = "role", expiration = 60)
public void updateRole(@ParameterValueKeyProvider @ParameterDataUpdateContent Role role) {
    sqlMapClientTemplate.update("updateRole", role);
} 

需要让 getUsersByRoleId 的缓存失效。这时最简单的办法是直接使用 annotation @InvalidateSingleCache

@UpdateSingleCache(namespace = "role", expiration = 60)
@InvalidateSingleCache(namespace = "user/list")
public void updateRole(@ParameterValueKeyProvider @ParameterDataUpdateContent Role role) {
    sqlMapClientTemplate.update("updateRole", role);
}

但是,如果有多个类似的 Cache 需要清除,那这种办法就不适用了。这时可以每个 POJO 定义一个专门用来 invalidate 的类:

@Component
public class UserCache {

    @InvalidateSingleCache(namespace = "user/list")
    public void invalidateGetUsersByRoleId(@ParameterValueKeyProvider Long id){
    }
    
}

在 Service 中调用相关的方法:

@Service
public class RoleManagerImpl implements RoleManager {

    @Autowired
    private RoleDao roleDao;

    @Autowired
    private UserCache userCache;

    @Override
    public void updateRole(Role role) {
        roleDao.updateRole(role);
        userCache.invalidateGetUsersByRoleId(role.getId());
        groupCache.invalidate(role.getId());
        ...
    }
} 

下一篇会讲一下 MultiCache 的使用。