我想在适当的地方添加一些缓存,但是到目前为止,我看到的所有示例都只展示了缓存简单实体的示例(如Spring图书示例),当使用@Cacheable, @CachePut等时,这些示例可以完美地工作。
然而,给出这个简单的用户角色示例:
public class User {
private String username
private List<Role> roles
}
数据库侧,3个表
user
id | username
1 | bob
user_role
user_id | role_id
1 | 1
1 | 2
1 | 3
role
id | name
1 | role1
2 | role2
3 | role3
一个返回用户(及其角色)的简单服务,这就是我想缓存的
@Cacheable(....)
public User getUserByUsername(username) {
// SQL that joins the 3 tables and return a user with its roles
User user = dao.getUserByusername(username)
}
为用户添加新角色的其他服务(因此它间接修改了用户对象),因此需要更新缓存
//@CachePut(Yep.. this isn't going to work is it)
public void addRoleToUser(username, role_id) {
// SQL to insert a row into the user_role table
dao.addRoleToUser(username, role)
}
我的问题是:是否有一个模式/解决方案,让缓存直接更新添加角色后(如在简单实体上使用@CachePut时)?或者是拥有@CacheEvict的唯一解决方案在addRoleToUser
上强制下一次调用getUserByUsername
从数据库中检索数据?
我问这个问题是因为,在性能方面,我更喜欢在添加角色时直接更新缓存,而不是清除缓存并强制新的数据库命中。
我认为这是服务方法签名addRoleToUser(..)
的设计问题。
首先,服务方法简单地直接委托,或立即将控制传递给DAO, DAO的签名向上传递到适当应用缓存关注点的服务层,从而影响您有效地将更改缓存到域模型的能力。
第二,服务类的API应该只关注领域(在DDD中思考时),而不是如何表示和存储User
和Role
(领域的模型类)之间的关系(即username
到role_id
),无论如何,它已经在您的领域模型类(User
)上用Role
的List
表示。
最后,这种类型的数据操作(根据名称仅存储具有User
ID的新Role
)依赖于数据存储,并且在这方面是一种优化,因此应该适当地封装在DAO中。为User
确定新的Roles
将非常容易,而不必查询数据存储的持久状态。
对于DAO (Repository)来说,处理这种关系并对服务层隐藏实现细节并不罕见。如果您要更改持久存储(例如,像MongoDB这样的关系到文档存储),而不会对应用程序的服务层产生不利影响,这将是必不可少的。当然,我们都知道这种情况经常发生,;)
一种方法可能是,在调用代码(例如Web MVC控制器)做以下事情:
@Controller
public class UserController {
private UserService userService;
public ResponseEntity<User> assignUserToRole(User user, Role role) {
// do any input validation
user = userService.grantRoleToUser(role, user);
// return an appropriate response
}
}
然后,在UserService
:
class UserService {
private UserDAO userDao;
@CachePut("Users")
public User grantRoleToUser(Role role, User user) {
// validate the assignment is allowed
userDao.addRoleToUser(user.getName(), role.getId());
user.addRole(role);
return user;
}
显然,这个设计可能有其他的变化。
例如,在从服务类调用DAO/Repository保存操作之前,User
DAO/Repository可以简单地具有save(:User)
方法,User
可以分配Role
(user.addRole(role)
)。
无论哪种方式,这都可以处理您的缓存需求。
根据您当前的设计,那么在addRoleToUser(..)
服务方法上使用@CacheEvict(cacheNames = "Users", key = "#username")
是您最好的选择。是的,下次调用getUserByUsername(..)
服务方法时需要重新加载User
。
看起来您不能仅通过注释来做到这一点,但您绝对可以直接使用CacheManager
:
@Autowired
private CacheManager cacheManager;
...
public void addRoleToUser(username, role_id) {
// SQL to insert a row into the user_role table
dao.addRoleToUser(username, role)
Cache userCache = cacheManager.getCache("<your_User_cache_name>");
User user = userCache.getIfPresent(username);
if (user != null) {
// add role to the cached User
}
}
当然,要使其工作,您的缓存不应该启用读时复制特性(如果支持的话)。