CRUD示例

351 阅读7分钟

页面设置

下载完bootstrap模板后,将html文件放在templates文件夹下,包含css,img,js文件的asserts文件夹放在static文件夹下。

我下载的模板首页页面:

国际化设置

国际化是指根据根据语言的不同,页面的语言也会跟着改变,比如在上例中显示语言是英文,此处我们可以修改为中文:

在设置国际化前需要在IDEA设置字符集为utf-8

在resources文件夹下新建i18n文件夹,并在其中新建login.properties文件,然后再新建login_zh_CN.properties文件,此时IDEA会自动将这两个文件放在一个Resource Bundle 'login'文件夹里。

新建一个en_US文件

点击login.properties文件,点击+符号新建login.tip:

在右边三个输入框分别是默认语言,英文,中文对应的页面显示:

我们希望将上述5个部分国际化:

然后在主配置文件application.properties加上:

spring.messages.basename=i18n.login

此处我们希望国际化index.html,则在其加上

<html lang="en" xmlns:th="http://www.thymeleaf.org">

通过#{}来加上对应配置

最后首页就变成之前我展示的样子。 然后我们希望点击页面的中文,English就会跳转到不同地区的页面,因此我们需要自定义一个解析器。

在MVC自动配置类WebMvcAutoConfiguration中,有一个解析地区的方法:

@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(
    prefix = "spring.mvc",
    name = {"locale"}
)
public LocaleResolver localeResolver() {
    // 若我们配置了本地区域化的信息,就返回用户配置的信息
    if (this.mvcProperties.getLocaleResolver()==org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.LocaleReslvr.FIXED) {
        return new FixedLocaleResolver(this.mvcProperties.getLocale());
    } else {    //否则使用默认的地区解析器
        AcceptHeaderLocaleResolver localeResolver = newAcceptHeaderLocaleResolver();
        localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
        return localeResolver;
    }
}

这是默认的地区解析器:

public class AcceptHeaderLocaleResolver implements LocaleResolver {
public Locale resolveLocale(HttpServletRequest request) {
        //获取默认语言环境
        Locale defaultLocale = this.getDefaultLocale();
        //若默认环境非空且发来请求的头部字段Accept-Language为空,
        //则使用默认的语言环境
        if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
            return defaultLocale;
        } else {
            Locale requestLocale = request.getLocale();
            List<Locale> supportedLocales = this.getSupportedLocales();
            if (!supportedLocales.isEmpty() && !supportedLocales.contains(requestLocale)) {
                Locale supportedLocale = this.findSupportedLocale(request, supportedLocales);
                if (supportedLocale != null) {
                    return supportedLocale;
                } else {
                    return defaultLocale != null ? defaultLocale : requestLocale;
                }
            } else {
                return requestLocale;
            }
        }
    }
}

我们照搬着写一个自定义解析器:

public class MyLocalResolver implements LocaleResolver {
    
    @Override
    public Locale resolveLocale(HttpServletRequest httpServletRequest) {
        String chooseLanguage = httpServletRequest.getParameter("language");
        Locale locale = Locale.getDefault();
        //StringUtils.isBlank(String str) 判断某字符串是否为空或长度为0或由空白符构成
        //而isEmpty是判断str==null 或 str.length()==0
        if(!StringUtils.isEmpty(chooseLanguage)) {
            //zh_CN
            String[] languages = chooseLanguage.split("_");
            //参数为语言和环境
            locale = new Locale(languages[0], languages[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {}
}

然后将自定义的国际化组件添加到容器里

@Configuration
public class MyMVCConfig implements WebMvcConfigurer {

    @Bean       //向容器中注入组件MyLocalResolver
    public LocaleResolver localeResolver() {
        return new MyLocalResolver();
    }
}

这是请求的链接:

运行点击中文,English即可切换到对应地区的页面

登陆设置

首先在登陆页面设置输入的账户名和密码对应的name值并设置提示信息:

然后是处理请求的Controller:

@Controller
public class UserController {

    @RequestMapping("/user/login")
    public String login(@RequestParam("uname") String uname, @RequestParam("password") String password,
                        Model model, HttpSession session) {
        if(!StringUtils.isEmpty(uname) && "123".equals(password)) {
            session.setAttribute("loginUser", uname);
            /*此处我们自定义一个视图解析器处理对main.html的请求,
                将其请求转至对dashboard.html页面的请求
                实际上我们并没有main.html页面
                这样是为了掩盖掉登陆后地址栏出现类似
                http://localhost:8080/user/login?uname=123&password=123
                的情况。(forward:/main.html掩盖不了输入的信息)
            */
            return "redirect:/main.html";   
        }else {
            model.addAttribute("msg", "用户名或密码错误");
            return "index";
        }
    }
}

为了防止用户没有登陆却直接通过输入地址http://localhost:8080/main.html来访问页面,我们需要一个拦截器来检测访问者是否登陆:

public class LoginHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object loginUser = request.getSession().getAttribute("loginUser");
        if(loginUser == null) {
            request.setAttribute("msg", "请先登陆");
            request.getRequestDispatcher("/index.html").forward(request, response);
            return false;
        }else {
            return true;
        }
    }
}

最后在配置类里自定义跳转页面和启动拦截器,

@Configuration
public class MyMVCConfig implements WebMvcConfigurer {

    //视图转换器
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
        System.out.println("addViewControllers");
        registry.addViewController("/main.html").setViewName("dashboard");
    }
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //所有除了访问首页,登录,静态资源的请求都需经过拦截器
        registry.addInterceptor(new LoginHandlerInterceptor())
                .addPathPatterns("/**").excludePathPatterns("/index.html", "/", "/user/login",
                "/asserts/*");
    }
    
    @Bean       //向容器中注入自定义国际化组件MyLocalResolver
    public LocaleResolver localeResolver() {
        return new MyLocalResolver();
    }
}

运行后无论是直接输入http://localhost:8080/dashboard.html还是http://localhost:8080/main.html,都会转至登陆页面并提示需要登陆:

登陆后即可跳转页面:

这是redirect发出的两次请求信息:

后台设置

此处我们以员工管理系统作为增删改查的示例。

我们可以通过lombok工具来省略bean类的get/set,构造函数。

首先导入依赖:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

然后在IDEA中添加相关插件并开启。

然后是两个bean类:

@Data   //提供类所有属性的 get 和 set 方法,以及equals、canEqual、hashCode、toString 方法。
@NoArgsConstructor      //无参构造
@AllArgsConstructor     //有参构造
public class Department {
    private Integer id;
    private String name;
}
@Data
@NoArgsConstructor
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Integer sex;

    private Department department;
    private Date birth;

    public Employee(Integer id, String lastName, String email, Integer sex, Department department) {
        this.id = id;
        this.lastName = lastName;
        this.email = email;
        this.sex = sex;
        this.department = department;
        this.birth = new Date();        //默认日期
    }
}

dao层,此处我们通过HashMap模拟数据库里的两个表,不用数据库存储记录

@Repository
public class EmployeeDao {

//  模拟员工表
    private static Map<Integer, Employee> employees = null;

    @Autowired
    private DepartmentDao departmentDao;

    static {
        employees = new HashMap<Integer, Employee>();
        employees.put(1001, new Employee(1001, "AA", "323232@qq.com", 1, new Department(101, "教学部")));
        employees.put(1002, new Employee(1002, "BB", "323232@qq.com", 0, new Department(101, "市场部")));
        employees.put(1003, new Employee(1003, "CC", "323232@qq.com", 0, new Department(101, "运营部")));
        employees.put(1004, new Employee(1004, "DD", "323232@qq.com", 1, new Department(101, "教研部")));
        employees.put(1005, new Employee(1005, "EE", "323232@qq.com", 1, new Department(101, "后勤部")));
    }
//    模拟主键自增
    private static Integer selfIncreaseId = 1006;
    //由于我们是将HashMap看作员工表,
    // 因此添加/更新员工信息都是同一方法
    public void save(Employee employee) {
        if(employee.getId() == null) {
            employee.setId(selfIncreaseId++);
        }
        //加入新员工记录前先设置其部门,部门信息是从新员工信息里获取的。
        employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
        // 模拟将新记录添加到员工表中
        employees.put(employee.getId(), employee);
    }
    //查询全部员工信息
    public Collection<Employee> getAll() { return employees.values();}

    public Employee getById(Integer id) { return employees.get(id); }

    public void deleteById(Integer id) { employees.remove(id); }
}
@Repository
public class DepartmentDao {

//   模拟部门表
    private static Map<Integer, Department> departments = null;
    
    //部门表初始记录
    static {
        departments = new HashMap<Integer, Department>();
        departments.put(101, new Department(101, "教学部"));
        departments.put(102, new Department(101, "市场部"));
        departments.put(103, new Department(101, "运营部"));
        departments.put(104, new Department(101, "教研部"));
        departments.put(105, new Department(101, "后勤部"));
    }

    public Collection<Department> getDepartments() { return departments.values(); }

    public Department getDepartmentById(Integer id) { return departments.get(id); }
}

最后给出项目页面结构:

添加操作

首先在主配置文件application.properties中设置日期格式:

spring.mvc.date-format=yyyy-MM-dd HH:mm:ss

在index页面登陆后点击员工管理选项,发出请求

请求转至EmployeeController类的的toAddPage方法:

@Controller
public class EmployeeController {

//    Controller层本应调用service层,此处我们直接调用dao层

    @Autowired
    EmployeeDao employeeDao;

    @Autowired
    DepartmentDao departmentDao;

    @RequestMapping("/getEmps")
    public String list(Model model) {
        Collection<Employee> employees = employeeDao.getAll();
        model.addAttribute("emps", employees);
        return "emp/list";
    }
    @GetMapping("/addEmp")
    public String toAddPage(Model model) {
        Collection<Department> departments = departmentDao.getDepartments();
        model.addAttribute("departments", departments);
        return "emp/add";
    }

    @PostMapping("/addEmp")
    public String addEmp(Employee employee) {
        System.out.println(employee);
        employeeDao.save(employee);
        //(1)如果是forward,则第一次添加完员工信息后跳转至员工信息列表
        // 每刷新一次页面就会添加一条记录。
        //(2)无法 return "getEmps"
        return "redirect:/getEmps";
    }
}

然后转至添加页面:

填写完信息后提交,转至addEmp方法,然后跳转至emp文件夹下的list页面:

效果图

更新操作

在index页面登陆后,点击员工管理转至list页面,点击编辑按钮,

然后EmployeeController下的toUpdatePage方法处理请求

/*
    跳转至更新页面
*/
    @GetMapping("/emp/{id}")
    public String toUpdatePage(@PathVariable("id")Integer id, Model model) {
        Employee employee = employeeDao.getById(id);
        System.out.println(employee);
        model.addAttribute("emp", employee);
        Collection<Department> departments = departmentDao.getDepartments();
        model.addAttribute("departments", departments);
        return "emp/update";
    }

    @PostMapping("/updateEmp")
    public String updateEmp(Employee employee) {
        System.out.println(employee);
        employeeDao.save(employee);
        return "redirect:/getEmps";
    }

跳转至emp文件夹下的update页面

然后updateEmp处理并跳转至list页面。

效果图

删除操作

在list页面点击删除选项,EmployeeController处理该请求

@GetMapping("/delEmp/{id}")
public String deleteEmp(@PathVariable("id")Integer id) {
        employeeDao.deleteById(id);
        return "redirect:/getEmps";
}

最后转至list页面

效果图

其他操作

404页面

将类似404,505等需要显示错误的页面放在新建的error页面即可

注册

在UserController下通过invalidate 方法使 HttpSession 失效,然后转至登陆页面

@RequestMapping("/user")
@Controller
public class UserController {

    @RequestMapping("/login")
    public String login(@RequestParam("uname") String uname, @RequestParam("password") String password,
                        Model model, HttpSession session) {
        if(!StringUtils.isEmpty(uname) && "123".equals(password)) {
            session.setAttribute("loginUser", uname);
            System.out.println("转发请求main.html");
            return "redirect:/main.html";
        }else {
            model.addAttribute("msg", "用户名或密码错误");
            return "index";
        }
    }

    @RequestMapping("/logout")
    public String logout(HttpSession session) {
        session.invalidate();
        return "redirect:/index.html";

    }
}