博客管理需要用到 分类 和 标签 所以先做分类管理功能
一、创建持久层(Dao层)
创建接口 TypeRepository 继承 JpaRepository
public interface TypeRepository extends JpaRepository<Type, Long> {
//根据名称查询Type
Type findByName(String name);
//根据每个分类大小由大到小排序取前6个
@Query("select t from Type t")
List<Type> findTop(Pageable pageable);
}
二、创建service层
1.定义分类业务层接口
创建分类接口TypeService,定义分类相关的方法
代码如下:
public interface TypeService {
//新增 保存
Type saveType(Type type);
//根据id查询
Type getType(Long id);
//根据名字查询
Type getTypeByName(String name);
//分页查询
Page<Type> listType(Pageable pageable);
//查询全部
List<Type> listType();
List<Type> listTypeTop(Integer size);
//更新
Type updateType(Long id,Type type);
//删除
void deleteType(Long id);
}
2.创建接口的实现类
创建 TypeServiceImpl 实现 用户接口 TypeService
代码如下:
package com.example.service.impl;
import com.example.NotFoundException;
import com.example.dao.TypeRepository;
import com.example.po.Type;
import com.example.service.TypeService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class TypeServiceImpl implements TypeService {
@Autowired
private TypeRepository typeRepository;
@Transactional
@Override
public Type saveType(Type type) {
return typeRepository.save(type);
}
@Override
public Type getType(Long id) {
return typeRepository.findById(id).orElse(null);
}
/**
* 根据名字查信息
* @param name
* @return
*/
@Override
public Type getTypeByName(String name) {
return typeRepository.findByName(name);
}
/**
* 分页查询
* @param pageable
* @return
*/
@Transactional
@Override
public Page<Type> listType(Pageable pageable) {
return typeRepository.findAll(pageable);
}
/**
* 查询全部
* @return
*/
@Override
public List<Type> listType() {
return typeRepository.findAll();
}
/**
* 博客页面 右上角 查询分类
* @param size
* @return
*/
@Override
public List<Type> listTypeTop(Integer size) {
// 设置排序对象
Sort sort = Sort.by(Sort.Direction.DESC,"blogs.size");
Pageable pageable = PageRequest.of(0, size, sort);
return typeRepository.findTop(pageable);
}
@Transactional
@Override
public Type updateType(Long id, Type type) {
Type t = typeRepository.findById(id).orElse(null); //根据id查询 存在就更新保存
if (t ==null ){
throw new NotFoundException("不存在该类型");
}
BeanUtils.copyProperties(type,t); //将type值赋给t
return typeRepository.save(t);
}
@Transactional
@Override
public void deleteType(Long id) {
typeRepository.deleteById(id);
}
}
分析:
- @Service注解:用于标注业务层组件
- @Autowired注解:@Autowired表示被修饰的类需要注入对象,spring会扫描所有被@Autowired标注的类,然后根据类型在ioc容器中找到匹配的类注入
- @Transactional注解:实现事务操作
三、分类控制器
创建 TypeController 分类控制器,在这里实现分页查询、添加分类、保存分类、修改分类、删除分类功能,代码如下:
package com.example.web.admin;
import com.example.po.Type;
import com.example.service.TypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.validation.Valid;
@Controller
@RequestMapping("/admin")
public class TypeController {
@Autowired
private TypeService typeService;
/**
* 分页查询 (显示分类页面)
* @param pageable
* @param model
* @return
*/
@GetMapping("/types")
public String types(@PageableDefault(size = 5,sort = {"id"},direction = Sort.Direction.DESC) //设置pageable 设置排序方式
Pageable pageable, Model model){ //pageable 是已经定义好的,有当前页,页容量,排序字段等属性
model.addAttribute("page",typeService.listType(pageable)); //往前台传数据 记录页面信息
return "admin/types";
}
/**
* 添加分类名称 (跳转页面)
* @return 返回新增页面
*/
@GetMapping("/types/input")
public String input(Model model){
model.addAttribute("type",new Type()); //
return "admin/types-input";
}
/**
* 跳转修改分类名称 (根据id跳转页面)
* @param id
* @param model
* @return
*/
@GetMapping("/types/{id}/input")
public String editInput(@PathVariable Long id, Model model){ // @PathVariable 接收id
model.addAttribute("type",typeService.getType(id)); //查询需要
return "admin/types-input";
}
/**
* 保存分类名称 (请求 实现保存)
* @param type
* @param result
* @param attributes
* @return
*/
@PostMapping("/types")
public String post(@Valid Type type, BindingResult result,RedirectAttributes attributes){ // @Valid验证注解 BindingResult校验参数必须跟在Valid后面
Type type1 = typeService.getTypeByName(type.getName()); //根据名字查询该分类是否存在
if (type1!=null){
result.rejectValue("name","nameError","不能添加重复的分类");
}
if (result.hasErrors()){
return "admin/types-input";
}
Type t = typeService.saveType(type);
if (t == null ){
attributes.addFlashAttribute("message","新增失败");
//如果 t 为空 则新增失败
}else{
attributes.addFlashAttribute("message","新增成功");
}
return "redirect:/admin/types";
}
/**
* 更新分类名称 (请求 实现更新)
* @param type
* @param result
* @param id
* @param attributes
* @return
*/
@PostMapping("/types/{id}")
public String editPost(@Valid Type type, BindingResult result,
@PathVariable Long id, RedirectAttributes attributes){
//查询type是否存在
Type type1 = typeService.getTypeByName(type.getName());
if (type1!=null){
result.rejectValue("name","nameError","不能添加重复的分类");
}
if (result.hasErrors()){
return "admin/types-input";
}
Type t = typeService.updateType(id,type);
if (t == null ){
attributes.addFlashAttribute("message","更新失败");
//如果 t 为空 则新增失败
}else{
attributes.addFlashAttribute("message","更新成功");
}
return "redirect:/admin/types";
}
/**
* 删除分类 (请求)
* @param id
* @param attributes
* @return
*/
@GetMapping("/types/{id}/delete")
public String delete(@PathVariable Long id,RedirectAttributes attributes){
typeService.deleteType(id);
attributes.addFlashAttribute("message","删除成功");
return "redirect:/admin/types";
}
}
分析:
- @Controller注解:用于标注控制层组件
- @RequestMapping("/admin"):建立请求URL和处理方法之间的对应关系
- @GetMapping注解:一个组合注解,是@RequestMapping(method = RequestMethod.GET)的缩写,用于将HTTP get请求映射到特定处理程序的方法注解
- @PostMapping注解:一个组合注解,是@RequestMapping(method = RequestMethod.POST)的缩写,用于将HTTP post请求映射到特定处理程序的方法注解
- @Valid注解:请求数据校验,用来判断是否有重复的分类
- @PathVariable注解:获取URL中的数据
- attributes.addFlashAttribute:相当于重定向后,在URL后面拼接了参数,这样在重定向之后的页面后者控制器再去获取URL后年的参数就可以了
四、前后端交互
1.点击分类
<a href="#" th:href="@{/admin/types}" class=" m-item item m-mobile-hide" th:classappend="${n==2} ? 'active'"><i class="idea icon"></i>分类</a>
2.新增
<a href="#" th:href="@{/admin/types/input}" class="ui mini right floated teal basic button">新增</a>
3.编辑、删除
<a href="#" th:href="@{/admin/types/{id}/input(id=${type.id})}" class="ui mini teal basic button">编辑</a> <!--点击时传递id给后台进行编辑跳转-->
<a href="#" th:href="@{/admin/types/{id}/delete(id=${type.id})}" class="ui mini red basic button">删除</a>
4.分页
<div class="ui mini pagination menu" th:if="${page.totalPages}>1"> <!--totalPages存在才显示-->
<a th:href="@{/admin/types(page=${page.number}-1)}" class=" item" th:unless="${page.first}">上一页</a>
<a th:href="@{/admin/types(page=${page.number}+1)}" class=" item" th:unless="${page.last}">下一页</a>
</div>
5.操作提示
<div class="ui success message" th:unless="${#strings.isEmpty(message)}">
<i class="close icon"></i>
<div class="header">提示:</div>
<p th:text="${message}">恭喜,操作成功!</p>
</div>
标签页面与分类页面相似不做另外的描述