我正在尝试使用Spring Boot和JPA实现水平分片。具体来说,我正在使用AbstractRoutingDataSource,如下所述:http://fedulov.website/2015/10/14/dynamic-datasource-routing-with-spring/我有一个有效的解决方案,以防我在同一个http请求上下文中切换数据源。
例如,我有这个RestController方法,它工作正常。根据playerName,为请求选择了正确的数据源。
@RequestMapping(value = "/players/register/{playerName}", method = RequestMethod.POST)
public String register(@PathVariable String playerName) {
try {
ShardingContext.setCurrentShardForPlayer(playerName);
return playerService.registerPlayer(playerName);
}
finally {
ShardingContext.clearShard();
}
}
但是当我尝试运行这个尝试访问同一请求中不同数据源中的玩家的方法时,找不到第二个播放器,因为数据源开关没有生效,AbstractRoutingDataSource中的数据源似乎被正确选择但是Spring那时候无法辨认出来。
@RequestMapping(value = "/get/{player1}/{player2}", method = RequestMethod.GET)
public List<Player> getPlayers(@PathVariable String player1, @PathVariable String player2){
try {
ShardingContext.setCurrentShardForPlayer(player1);
Player player1data = playerService.loadUserByUsername(player1);
ShardingContext.setCurrentShardForPlayer(player2);
Player player2data = playerService.loadUserByUsername(player2);
return Arrays.asList(player1data, player2data);
} finally {
ShardingContext.clearShard();
}
}
我希望数据源开关生效(我认为数据源的getConnection方法应该由Spring调用),然后再启动第二个事务,但事实并非如此。在第二次方法调用playerService.loadUserByUsername()之前,如何强制Spring调用datasource的getConnection方法?
玩家服务方法定义如下:
@Transactional( propagation = Propagation.REQUIRES_NEW )
public Player loadUserByUsername(String username) {
Player player = playerRepository.findOneByName(username);
return player;
}
ShardingContext代码:
private static ThreadLocal<String> currentShard = new ThreadLocal<>();
public static void setCurrentShardForPlayer(String playerId) {
int hashCode = playerId.hashCode();
int shard = hashCode % numberOfShards;
shard = ((shard+numberOfShards) % numberOfShards) + 1;
currentShard.set(shard+"");
log.info("Current shard set to {} - {} for player {}", shard, currentShard.get(), playerId);
}
public static void clearShard(){
currentShard.remove();
}