项目功能实现以上一篇博客为基础。
新闻管理页面如下
在controller层建立一个NewsController对象,用来处理与新闻有关的请求。显示新闻列表这一部分,和上一篇博客,显示type列表、tag列表类似不再进行介绍。先从新增和修改新闻部分开始,当页面发出一个id等于-1的请求时,表明正在新增新闻,需要产生要给news对象。
id不为-1的请求,表明正在修改新闻。
此时需要通过news的id调用service的方法,获取到一个news对象。在获取到news对象时,由于news对象中有以list形式存贮tag的id,无法直接用到前端页面,所以需要,将list形式tag的id转化为String类型,存储在news对象的tagIds的属性中,传给页面用来告诉用户已有的标签。同时也要将所有的types和tags传给页面进行添加或修改新的type和tag。
@RequestMapping("/input/{id}")
public String toInput(@PathVariable Long id, Model model) {
if (id == -1) {
model.addAttribute("news", new News());
}else{
News news = newsService.findNewsById(id);
String tagIds = tagService.getTagIds(news.getTags());
news.setTagIds(tagIds);
model.addAttribute("news",news);
}
List<Type> types= typeService.listType();
model.addAttribute("types",types);
List<Tag> tags = tagService.listTag();
model.addAttribute("tags",tags);
return "admin/news-input";
}
接着是将数据存入数据库,由于从新闻编辑页面传来的news对象,是没有user属性的,所以需要通过session获取到当前用户,并将用户的信息存入到news对象中,而news对象中的tag信息也是以String类型存储在tagIds中,需要将其取出并转化为list类型的tags,tag信息总要经过String和list类型的转化,是因为页面需要的是String类型,存储到数据库是又是调用JpaRepository接口下的方法用的是list类型的数据,两者没做到统一,所以就要进行一个转化了,最后通过调用service层的方法存入到数据库中,并返回news的界面。
@RequestMapping("/input")
public String input(News news, HttpSession session){
User user = (User) session.getAttribute("user");
news.setUser(user);
List<Tag> tags = tagService.findTagByTagId(news.getTagIds());
news.setTags(tags);
newsService.input(news);
return "redirect:/admin/news";
}
可以看到界面还有一个搜索框,因而需要根据页面的查询条件进行sql查询,查询条件通过newQuery对传入,通过newQuery查询条件和pageable页面信息,来获取在查询条件下的page,并将得到的page添加到model中,来传给前端页面,应为不需要修改其他部分,所以只需要加载newList(即表格信息)。
@RequestMapping("search")
public String search(@PageableDefault(size = 5, sort = {"updateTime"}, direction = Sort.Direction.DESC) Pageable pageable,
NewQuery newQuery,
Model model){
Page<News> page = newsService.searchNews(pageable,newQuery);
model.addAttribute("page",page);
return "admin/news :: newsList";
}
}
接着是服务层接口对象方法的实现,首先新增或修改新闻,当news的id为空时,也就是新增的新闻,这时,需要设置新闻产生的时间,然后调用newDao接口保存新闻信息到数据库中。如果news的id不为空,也就是更新新闻的信息,这时需要设置新闻修改的时间,同时页面传来的news对象中的数据不包含一些未被修改的数据(如创建的时间)这时需要对缺失的数据进行补全,再存入数据库防止数据丢失。
@Override
public void input(News news) {
if(news.getId()==null){
news.setCreateTime(new Date());
newsDao.save(news);
}else{
news.setUpdateTime(new Date());
News n = newsDao.getOne(news.getId());
BeanUtils.copyProperties(news,n, MyBeanUtils.getNullPropertyNames(news));
newsDao.save(n);
}
}
通过id获取新闻对象,这个比较简单直接调用newDao接口下的方法就行了。
@Override
public News findNewsById(Long id) {
return newsDao.getOne(id);
}
接着是进行较为复杂的搜索查询,JpaRepository接口只能实现一些简单的sql查询,如果要进行多重条件的sql查询,则还需要继承JpaSpecificationExecutor接口。
public interface NewsDao extends JpaRepository <News,Long> , JpaSpecificationExecutor<News> {
}
继承接口后newsDao层findAll又有了新的实现方法。通过Specification接口和pageable对象实现多个条件的sql查询,而Specification接口下需要重写Predicate接口,并需要通过Root,CriteriaQuery,CriteriaBuilder 进行实例化,Root可用查询的表实例化,CriteriaQuery查询的字段, CriteriaBuilder 构造字段之间的关系。
@Override
public Page<News> searchNews(Pageable pageable, NewQuery newQuery) {
Page<News> news = newsDao.findAll(new Specification<News>() {
@Override
public Predicate toPredicate(Root<News> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
if(!StringUtils.isEmpty(newQuery.getTitle())){
predicates.add(criteriaBuilder.like(root.<String>get("title"),"%"+newQuery.getTitle()+"%"));
}
if(!StringUtils.isEmpty(newQuery.getTypeId())){
predicates.add(criteriaBuilder.equal(root.<Type>get("type").get("id"),newQuery.getTypeId()));
}
if(!StringUtils.isEmpty(newQuery.getRecommend())){
predicates.add(criteriaBuilder.equal(root.<Boolean>get("recommend"),newQuery.getRecommend()));
}
criteriaQuery.where(predicates.toArray(new Predicate[predicates.size()]));
return null;
}
},pageable);
return news;
}
前端页面传递查询条件代码如下
代码已上传GitHub