对具有一对多关系的实体使用Spring缓存抽象



我想在适当的地方添加一些缓存,但是到目前为止,我看到的所有示例都只展示了缓存简单实体的示例(如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中思考时),而不是如何表示和存储UserRole(领域的模型类)之间的关系(即usernamerole_id),无论如何,它已经在您的领域模型类(User)上用RoleList表示。

最后,这种类型的数据操作(根据名称仅存储具有UserID的新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保存操作之前,UserDAO/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
}
}

当然,要使其工作,您的缓存不应该启用读时复制特性(如果支持的话)。

最新更新