在开发Halo的时候,有很多设置需要保存在数据库里,比如站点标题,关键字等等。那么这时候问题就来了,怎么样设计表结构呢?设置选项是比较多的,不可能把每个设置选项都当成数据表字段吧?后来决定使用key-value键值对的形式来保存数据。
实现思路
数据表设计
CREATE TABLE halo_options
(
option_name VARCHAR(255) NOT NULL PRIMARY KEY, -- key
option_value LONGTEXT -- value
);
思路简述
整体思路是这样子的:在数据表中只设置两个字段,options_name
用来保存设置选项名称,如site_title(网站标题)
,options_value
用来保存设置选项对应的值,如Ryan0up'S Blog
。那么怎么来查询设置选项呢?分两种,第一种是根据key(设置选项名称)来查询所对应的值。第二种是查询所有设置选项,这里就要用到Map来保存所有的设置选项以及对应的值了。
实现代码
Options(实体类):
@Data
@Entity
@Table(name = "halo_options")
public class Options implements Serializable {
/**
* 设置项名称
*/
@Id
private String optionName;
/**
* 设置项的值
*/
@Lob
private String optionValue;
}
OptionsServiceImpl(Service实现类):
@Service
public class OptionsServiceImpl implements OptionsService {
@Autowired
private OptionsRepository optionsRepository;
private static final String OPTIONS_KEY = "'options_key'";
private static final String OPTIONS_CACHE_NAME = "options_cache";
/**
* 批量保存设置
*
* @param options options
*/
@CacheEvict(value = OPTIONS_CACHE_NAME,key = OPTIONS_KEY)
@Override
public void saveOptions(Map<String,String> options){
if(null != options && !options.isEmpty()){
options.forEach((k,v) -> saveOption(k,v));
}
}
/**
* 保存单个设置选项
*
* @param key key
* @param value value
*/
@CacheEvict(value = OPTIONS_CACHE_NAME,key = OPTIONS_KEY)
@Override
public void saveOption(String key,String value){
Options options = null;
if("".equals(value)){
options = new Options();
options.setOptionName(key);
this.removeOption(options);
}else {
if (HaloUtil.isNotNull(key)) {
//如果查询到有该设置选项则做更新操作,反之保存新的设置选项
if (null == optionsRepository.findOptionsByOptionName(key)) {
options = new Options();
options.setOptionName(key);
options.setOptionValue(value);
optionsRepository.save(options);
} else {
options = optionsRepository.findOptionsByOptionName(key);
options.setOptionValue(value);
optionsRepository.save(options);
}
}
}
}
/**
* 移除设置项
*
* @param options options
*/
@CacheEvict(value = OPTIONS_CACHE_NAME,key = OPTIONS_KEY)
@Override
public void removeOption(Options options) {
optionsRepository.delete(options);
}
/**
* 获取设置选项
*
* @return map
*/
@Cacheable(value = OPTIONS_CACHE_NAME,key = OPTIONS_KEY)
@Override
public Map<String, String> findAllOptions() {
Map<String,String> options = new HashMap<String,String>();
List<Options> optionsList = optionsRepository.findAll();
if(null != optionsList){
optionsList.forEach(option -> options.put(option.getOptionName(),option.getOptionValue()));
}
return options;
}
/**
* 根据key查询单个设置选项
*
* @param key key
* @return String
*/
@Cacheable(value = OPTIONS_CACHE_NAME,key = "#key+'options'")
@Override
public String findOneOption(String key) {
Options options = optionsRepository.findOptionsByOptionName(key);
if(null!=options){
return options.getOptionValue();
}
return null;
}
}
OptionsController(控制器):
@Controller
@RequestMapping("/admin/option")
public class OptionController {
@Autowired
private OptionsService optionsService;
/**
* 请求跳转到option页面并完成渲染
*
* @return freemarker
*/
@GetMapping
public String options(Model model){
model.addAttribute("options", optionsService.findAllOptions());
return "admin/admin_option";
}
/**
* 保存设置选项
*
* @param options options
*/
@PostMapping(value = "/save")
@ResponseBody
public String saveOptions(@RequestParam Map<String,String> options){
try {
optionsService.saveOptions(options);
HaloConst.OPTIONS.clear();
HaloConst.OPTIONS = optionsService.findAllOptions();
log.info("所保存的设置选项列表:"+options);
return RespStatus.SUCCESS;
}catch (Exception e){
log.error("未知错误:",e.getMessage());
return RespStatus.ERROR;
}
}
}
页面上获取设置选项的值:
由于是使用Map来保存的数据,所以在页面(freemarker模板,其他模板引擎类似)上直接使用${options.设置选项的名称}
就可以了,如${options.blog_title}
。
总结
由于之前没有做过类似的功能,所以最开始做的时候简直毫无头绪,通过在v2ex和SegmentFault上提问才有了思路。非常感谢v2ex和SegmentFault上的大佬解答。