美文网首页
半暖商城第一季--SpringMVC+Mybatis本地环境开发

半暖商城第一季--SpringMVC+Mybatis本地环境开发

作者: Alibct | 来源:发表于2017-03-10 15:52 被阅读294次

    1. 前言

    在前一篇教程中我们介绍了如何使用IntelliJ IDEA搭建本地开发环境,同时也集成了本地服务器和远程数据库。在本教程中我们将使用本地开发环境进行一些开发测试,以此确保本地开发环境稳定可靠。

    2. 本地开发环境测试

    2.1 添加自定义404 Not Found页面

    在实际开发中一些静态页面有时是需要自己定义的例如:404 not found界面,那么如何实现在SpringMVC+Mybatis的框架中,让用户在请求不存在的页面时出现自己自定义的页面呢?下面就来探讨一下。

    • 首先需要在web.xml中做相应的配置
        <!--配置错误页面-->
        <error-page>
            <error-code>404</error-code>
            <location>/static/view/404.html</location>
        </error-page>
    

    上面的配置其实我在上一篇教程中已经添加了,这里做一下说明,在web.xml中error-page标签表示配置错误界面,error-code标签表示错误码类型,这里我们需要配置404错误,所以错误码就选择404,location标签是指自定义的错误静态界面存放的位置,这里我们放在之前新增的view文件夹中。

    • 去网上找一个自己喜欢的404 not found界面,然后将代码拷贝到本地的404.html中即可

    这里有一个坑,我在配置404界面的时候死活不出中文,全是乱码,去网上找了好久,才发现需要在web.xml中配置htm和html的编码方式,如果使用的404界面是jsp格式的则不会出现此问题,但是既然问题出现了,这里我就提一下,解决的办法有两种。

    第一种:在自己项目的web.xml中添加以下配置(同样的,在前一篇教程中我已经添加了,这里做一个提醒):

        <!-- 防止html页面出现中文乱码 -->
        <mime-mapping>
            <extension>htm</extension>
            <mime-type>text/html;charset=utf-8</mime-type>
        </mime-mapping>
    
        <mime-mapping>
            <extension>html</extension>
            <mime-type>text/html;charset=utf-8</mime-type>
        </mime-mapping>
    

    第二种:配置全局属性,这种配置方法是在自己的服务器中。因为我们调试的是在本地服务器,所以本地服务器需要配置。但是项目完成后又会部署到远程服务器,所以远程服务器也需要配置。具体配置方法如下:

    打开~/tomcat/conf/web.xml,'~'表示你'tomcat'所在的目录地址,请用你具体的'tomcat'所在地址进行替换。并在web.xml中添加以下配置:

        <!-- 防止html页面出现中文乱码 -->
        <mime-mapping>
            <extension>htm</extension>
            <mime-type>text/html;charset=utf-8</mime-type>
        </mime-mapping>
    
        <mime-mapping>
            <extension>html</extension>
            <mime-type>text/html;charset=utf-8</mime-type>
        </mime-mapping>
    

    至此就可以解决htm和html页面在显示时出现中文乱码的问题

    下面按下绿色的运行按钮,等待浏览器跳出来,然后输入一个不存在的地址就能看到你自己定义的404啦,我的404如下(闲丑的憋说话):

    404.html404.html

    3. 登录教程实践

    在写登录教程之前,我们先来聊聊SpringMVC。

    先看一个SpringMVC结构的整个请求过程。

    SpringMVC请求SpringMVC请求

    用户发出请求,第一个接收到请求的是前端控制器,前端控制器调用处理器映射器,处理器映射根据相应的请求查找处理器适配器,然后处理器适配器就开始适配能够处理请求的处理器,处理器再到底层调用业务类处理相应的业务,业务类处理完成后会返回ModelAndView给前端控制器,前端控制器再请求视图解析器解析ModelAndView最后将解析的结果返回给浏览器响应用户。以上就是整个处理过程

    在整个请求链中需要我们程序员实现的就是一个处理器,也就是Controller和一个业务类,其它的SpringMVC底层都有实现,我们只需要关注我们的Controller和业务的逻辑实现就可以了。

    那么要实现这个登录功能我们需要写什么呢?

    • 登录业务的具体实现
    • 登录的Controller
    • 具体的登录界面,即jsp页面

    但是实际开发中因为集成了Mybatis同时又需要遵循高内聚低耦合的设计思想,所以我们在实现具体登录业务的过程中会将整个业务微粒化,保证每一个单元高内聚,单元与单元之间低耦合。好了废话不多说,show the code!

    3.1 数据库持久层开发

    3.1.1 在开始开发之前,我们先来看一下上一篇教程中最后实现的项目结构图。

    00520052

    上图中标记的两个文件夹就是持久层的文件所要放置的位置。在以前没有Mybatis的时候我们是自己写dao接口自己实现dao接口,最后自己调用dao接口。有了Mybatis之后,我们只需要写接口和sql语句,Mybatis会自动帮我们实现接口,这样能省好多事,关键是只要你sql语句没问题,接口就不会出什么大问题,何乐而不为呢?

    在进行数据库持久层开发之前我们需要准备一个数据库和一张表。
    我们就是用上一篇中在IntelliJ IDEA中配置好的数据库中心。

    • 创建数据库
    00460046

    第一步:刷新数据库以连接数据库
    第二步:打开命令行窗口以键入sql语句
    第三步:键入sql语句
    第四步:执行sql语句

    注意:一次只能执行一条语句,在键入第二条语句之前需要先删除第一条语句(不太智能)

    • 创建管理员表
    CREATE TABLE ADMINISTRATOR (
      admin_id VARCHAR(100) NOT NULL UNIQUE COMMENT '管理员ID',
      admin_name VARCHAR(100) NOT NULL UNIQUE COMMENT '管理员名称',
      password VARCHAR(41) NOT NULL COMMENT '密码',
      privilege_level TINYINT NOT NULL DEFAULT 1 COMMENT '权限等级',
      status BOOLEAN NOT NULL DEFAULT TRUE COMMENT '账号是否可用',
      create_time DATETIME NOT NULL COMMENT '管理员创建时间',
      PRIMARY KEY(admin_id)
    ) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='管理员表';
    

    准备完成开始进行数据库持久层开发。

    3.1.2 编写实体类Administrator.java

    看代码,这里不想多说

    Administrator.java 该文件在~/java/entity/

    package cn.semiwarm.admin.entity;
    import java.io.Serializable;
    import java.sql.Timestamp;
    
    /**
     * 管理员类
     * Created by alibct on 2017/3/9.
     */
    public class Administrator implements Serializable {
    
        private String adminId; // 管理员ID
        private String adminName; // 管理员名称
        private String password; // 密码
        private Integer privilegeLevel; // 权限等级默认一级,最高十级
        private Boolean status; // 是否可用
        private Timestamp createTime; // 创建时间
    
        public String getAdminId() {
            return adminId;
        }
    
        public void setAdminId(String adminId) {
            this.adminId = adminId;
        }
    
        public String getAdminName() {
            return adminName;
        }
    
        public void setAdminName(String adminName) {
            this.adminName = adminName;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public Integer getPrivilegeLevel() {
            return privilegeLevel;
        }
    
        public void setPrivilegeLevel(Integer privilegeLevel) {
            this.privilegeLevel = privilegeLevel;
        }
    
        public Boolean getStatus() {
            return status;
        }
    
        public void setStatus(Boolean status) {
            this.status = status;
        }
    
        public Timestamp getCreateTime() {
            return createTime;
        }
    
        public void setCreateTime(Timestamp createTime) {
            this.createTime = createTime;
        }
    
        @Override
        public String toString() {
            return "Administrator{" +
                    "adminId='" + adminId + '\'' +
                    ", adminName='" + adminName + '\'' +
                    ", password='" + password + '\'' +
                    ", privilegeLevel=" + privilegeLevel +
                    ", status=" + status +
                    ", createTime=" + createTime +
                    '}';
        }
    }
    

    3.1.3 编写基本操作接口

    为了让每一个xxxMapper.java接口都能实现增删改查这几个基本操作,我们先创建一个基本接口

    BaseMapper.java 该文件在~/java/mapper/

    接口中使用范型

    package cn.semiwarm.admin.mapper;
    import java.io.Serializable;
    import java.util.List;
    
    /**
     * 描述:定义基本查询方法的接口
     * Created by alibct on 2017/2/23.
     */
    public interface BaseMapper<T> {
        /**
         * 增加方法
         *
         * @param t 要增加的对象
         * @return 受影响的行数
         */
        int add(T t);
    
        /**
         * 删除方法
         *
         * @param t 要删除的对象
         * @return 受影响的行数
         */
        int delete(T t);
    
        /**
         * 更新方法
         *
         * @param t 要更新的对象
         * @return 受影响的行数
         */
        int update(T t);
    
        /**
         * 根据id查询某个对象
         *
         * @param id 要查询对象的id
         * @return 要查询的对象
         */
        T findById(Serializable id);
    
        /**
         * 查询所有对象
         *
         * @return 所有对象
         */
        List<T> findAll();
    }
    

    3.1.4 编写AdministratorMapper接口

    AdministratorMapper.java 该文件在~/java/mapper/

    AdministratorMapper.java接口继承BaseMapper.java接口以规范接口中的方法

    package cn.semiwarm.admin.mapper;
    import cn.semiwarm.admin.entity.Administrator;
    import java.io.Serializable;
    import java.util.List;
    
    /**
     * 管理员基本操作
     * Created by alibct on 2017/3/9.
     */
    public interface AdministratorMapper extends BaseMapper<Administrator>{
    
        int add(Administrator administrator);
    
        int delete(Administrator administrator);
    
        int update(Administrator administrator);
    
        Administrator findById(Serializable id);
    
        List<Administrator> findAll();
    }
    

    3.1.5 编写AdministratorMapper.xml文件

    该文件中存放的是对数据库中ADMINISTRATOR表的基本操作sql语句

    AdministratorMapper.xml 该文件在~/resources/mapper/

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!-- namespace是指明Mybatis扫描的目录,即cn.semiwarm.admin.mapper.xxxMapper -->
    <mapper namespace="cn.semiwarm.admin.mapper.AdministratorMapper">
        <insert id="add" parameterType="cn.semiwarm.admin.entity.Administrator">
            INSERT INTO ADMINISTRATOR (admin_id, admin_name, password, privilege_level, status, create_time)
            VALUE (#{adminId}, #{adminName}, password(#{password}), #{privilegeLevel}, #{status}, #{createTime})
        </insert>
    
        <delete id="delete" parameterType="cn.semiwarm.admin.entity.Administrator">
            DELETE FROM ADMINISTRATOR
            WHERE admin_id = #{adminId}
        </delete>
    
        <update id="update" parameterType="cn.semiwarm.admin.entity.Administrator">
            UPDATE ADMINISTRATOR
            SET
            admin_name      = #{adminName},
            password        = password(#{password}),
            privilege_level = #{privilegeLevel},
            status          = #{status}
            WHERE admin_id = #{adminId}
        </update>
    
        <select id="findById" parameterType="String" resultType="cn.semiwarm.admin.entity.Administrator">
            SELECT *
            FROM ADMINISTRATOR
            WHERE admin_id = #{adminId}
        </select>
    
        <select id="findAll" resultType="cn.semiwarm.admin.entity.Administrator">
            SELECT *
            FROM ADMINISTRATOR
        </select>
    </mapper>
    

    这个文件中有以下几点需要说明

    1. <insert></insert>标签对应数据库的INSERT语法
    2. <delete></delete>标签对应数据库的DELETE语法
    3. <update></update>标签对应数据的UPDATE语法
    4. <select></select>标签对应数据库的SELECT语法
    5. 标签中的id中的值对应接口中的方法名
    6. 标签中的parameterType对应接口中的形参的类型
    7. 标签中的resultType对应接口中方法的返回值类型,即使在接口中返回值类型是List<E>类型,这里的返回值类型也是List<E>类型中的范型,例如在接口中findAll方法的返回值是List<Administrator>,但是在.xml文件中id为findAll的语句中的resultType还是为Administrator类型。
    8. 标签里面的sql语句需要的参数用#{value}替换,value的值就是parameterType中填写的类型的属性名。例如在findById的方法中,参数应该是id,所以SELECT语句中的参数就是AdministratoradminId属性。Mybatis可以自动识别实体类中的属性,并将用该属性值替换占位符。
    9. password(#{password})表示将Administrator类中的password对应的属性值进行密码加密后存储到数据库,这种加密方式是不可逆的,也就是说如果需要在代码中进行比较password是否相同需要在写查询语句的时候对password使用密码加密后进行比较,如果加密后的秘文相等则说明密码相等。md5的加密方式也是不可逆的。

    3.1.6 单元测试

    以上三个文件写完之后就可以进行单元测试,养成写好代码就测试好习惯能够有效减少bug的存在。

    /src/test/java/文件夹中创建AdministratorMapperTest.java

    AdministratorMapperTest.java

    import cn.semiwarm.admin.entity.Administrator;
    import cn.semiwarm.admin.mapper.AdministratorMapper;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import java.sql.Timestamp;
    import java.util.UUID;
    
    /**
     * 管理员测试类
     * Created by alibct on 2017/3/10.
     */
    @RunWith(SpringJUnit4ClassRunner.class) // 注入Spring测试类
    @ContextConfiguration("classpath:spring/spring-*.xml") // 注入Spring的所有配置文件,如果没有配置对会出现'failed load context application'的错误,如果出现请把之前的两个教程中的配置文件好好查看一下
    public class AdministratorMapperTest {
        // 注入AdministratorMapper接口
        @Autowired
        private AdministratorMapper administratorMapper;
        // 标记为测试类
        @Test
        public void testAdd() { // 光标放在这里右键'Debug',然后看控制台的打印日志,出现添加成功后就OK了
            Administrator admin = new Administrator();
            admin.setAdminId(UUID.randomUUID().toString());
            admin.setAdminName("admin");
            admin.setPassword("admin");
            admin.setPrivilegeLevel(10);
            admin.setStatus(true);
            admin.setCreateTime(new Timestamp(System.currentTimeMillis()));
            int result;
            result = administratorMapper.add(admin);
    
            if (result > 0) {
                System.out.println("添加成功!");
            } else {
                System.out.println("添加失败!");
            }
        }
    }
    

    具体说明我都注释了。

    此时的项目结构如下:

    00530053

    另外这里提一下,之前我看过黑马的SpringMVC教程,里面的讲解的和Mybatis的集成时是把xxxMapper.xml文件和xxxMapper.java文件放在~/java/mapper/包内,但是我用了这种方法在做单元测试的时候愣是不能成功,并且在spring-dao.xml文件中我也按照教程进行了相应的配置但是还是没能成功,如果有知道怎么把这两个文件放在同一个文件夹中并且测试成功的童鞋可以告诉我哈,感激不尽。

    3.1.7 编写业务代码

    到这里我们才真正的开始关心登录业务的具体逻辑,由此可见前期准备工作还真是不少呢

    3.1.7.1 编写BaseService.java接口,同样的为了防止以后业务类中会出现一些基本的业务操作,我们在此留下接口,方便扩展

    BaseService.java 该文件在~/java/service/

    package cn.semiwarm.admin.service;
    /**
     *  基本功能接口,防止以后会出现一些基本功能
     *  可扩展性高,耦合性低
     * Created by alibct on 2017/2/24.
     */
    public interface BaseService<T> {
    }
    
    3.1.7.2 编写AdministratorService.java接口,完成一些复杂的业务操作,例如登录,暂时就放了登录方法以后需要的请自行实现,我只是示范,示范,示范!

    AdministratorService.java 该文件在~/java/service/

    package cn.semiwarm.admin.service;
    import cn.semiwarm.admin.entity.Administrator;
    import org.springframework.web.servlet.ModelAndView;
    
    /**
     * 管理员相关服务
     * Created by alibct on 2017/3/9.
     */
    public interface AdministratorService extends BaseService<Administrator>{
        ModelAndView signIn(Administrator administrator) throws Exception;
    }
    
    3.1.7.3 编写AdministratorServiceImpl.java接口实现类,完成登录业务的具体实现过程

    在实现接口之前,我们先来思考一下当前的问题。

    1. 登录界面呢?好像还没写...
    2. 如何获取用户输入的用户名和密码?
    3. 如何检查合法性?
    4. 出错了怎么提示用户?
    • 先来解决第一个问题,登录界面,其实网上有一大堆模版,我是照着知乎的登录界面一点点写的,最后效果如下:
    半暖-登录界面半暖-登录界面

    至于你们自己怎么解决,自己想去(果断甩锅顺便秀一下自己的登录界面,哈哈)

    • 如何获取用户的输入?这里用的是表单,在input标签中的name属性中填写和Administrator类中属性一致的名称,例如:
    <div class="login-card">
        <div class="login-form">
            <h1 class="logo hide-text">半暖</h1>
            <form action="<%=request.getContextPath()%>/signIn" method="post" accept-charset="utf-8">
                <div class="group-inputs">
                    <div class="input-wrapper account">
                        <input type="text" name="adminName" aria-label="请输入用户名" placeholder="请输入用户名" required>
                    </div>
                    <div class="input-wrapper password" style="margin-top: -1px">
                        <input type="password" name="password" aria-label="请输入密码" placeholder="请输入密码" required>
                    </div>
                </div>
                <div class="button-wrapper">
                    <button class="sign-button" type="submit">登录</button>
                </div>
            </form>
        </div>
    </div>
    

    用户名的那个输入框的name属性填写的就是Administrator类中的adminName属性名。密码输入框的name属性就填写Administrator类中的password属性名。Spring会自动将这些属性名对应的属性值封装成实体类,我们只需要调用就好了。

    • 检查合法性?这个具体的实现看下面的代码。
    • 错误提示方式?有三种,第一种是弹窗;第二种是打开一个新的页面;第三种是在当前页面的某个地方提示用户错哪里了。第一种可以用jQuery实现,第二种直接再写一个错误页面进行提示,第三种需要使用ajax技术。这里为了方便,我们选用第二种实现方式,第一种我就不喜欢,第三种使用ajax的实现方式以后会填坑。

    好,下面就开始一个个解决问题。

    登录界面你们自己写

    先在/src/main/webapp/WEB-INF/下新增一个jsp文件夹用于存放.jsp文件,然后在jsp文件夹中添加一个main.jsp文件用于成功转跳和不成功时候的提示信息

    这里需要先下载jquery-3.1.1.min.js文件(点我下载)并引入到项目的静态资源文件夹中(~/webapp/static/js/)

    <%--
      Created by IntelliJ IDEA.
      User: alibct
      Date: 2017/3/9
      Time: 下午2:14
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <script type="text/javascript" src="../../static/js/jquery-3.1.1.min.js"></script>
    <head>
        <title>半暖-后台管理界面</title>
    </head>
    <body>
    后台管理主界面<br />
    管理员信息:${message} 
    </body>
    </html>
    

    编写AdministratorServiceImpl.java 该文件在~/java/service/impl/

    package cn.semiwarm.admin.service.impl;
    import cn.semiwarm.admin.entity.Administrator;
    import cn.semiwarm.admin.mapper.AdministratorMapper;
    import cn.semiwarm.admin.service.AdministratorService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.web.servlet.ModelAndView;
    
    /**
     * 管理员业务实现类
     * Created by alibct on 2017/3/10.
     */
    @Service("administratorService") // Service注解
    public class AdministratorServiceImpl implements AdministratorService {
    
        private final AdministratorMapper administratorMapper;
    
        // 注入Mapper接口
        @Autowired
        public AdministratorServiceImpl(AdministratorMapper administratorMapper) {
            this.administratorMapper = administratorMapper;
        }
    
        public ModelAndView signIn(Administrator administrator) throws Exception {
    
            ModelAndView view = new ModelAndView("main"); // 实例化jsp界面就是前面写的那个main.jsp
    
            Administrator administratorInfo = administratorMapper.verifyAdministratorByName(administrator);
    
            if (null != administratorInfo) {
                view.addObject("message",administratorInfo.toString());
            } else {
                view.addObject("message","用户名或密码有误!");
            }
    
            return view;
        }
    }
    

    signIn方法中的形参administrator就是Spring获取form表单中对应属性名的属性值而封装成的实体类,我们直接拿着这个实体类去验证登录就可以了。

    我在AdministratorMapper.java接口中添加了一个验证方法并在AdministratorMapper.xml文件中添加了对应的sql语句。所以直接在这里调用验证方法验证合法性就好了,其实就是拿着用户名和密码去查数据库中是否有这条数据的,简单的判断没有那么复杂。

    view.addObject("message","用户名或密码有误!");这句代码就是使用key/value的方式将提示信息封装在ModelAndView中之后在main.jsp中就可以使用${message}来获取value对应的值了。

    编写AdministratorController.java类 该文件在~/java/controller/

    package cn.semiwarm.admin.controller;
    import cn.semiwarm.admin.entity.Administrator;
    import cn.semiwarm.admin.service.impl.AdministratorServiceImpl;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.servlet.ModelAndView;
    
    /**
     * 管理员控制器
     * Created by alibct on 2017/3/10.
     */
    @Controller // 标记该类是控制器
    public class AdministratorController {
    
        private final AdministratorServiceImpl administratorService;
    
        // service注入
        @Autowired
        public AdministratorController(AdministratorServiceImpl administratorService) {
            this.administratorService = administratorService;
        }
    
        // 请求登录映射路径
        @RequestMapping(value = "/signIn", method = RequestMethod.POST, produces = "application/json;charset=utf-8")
        public ModelAndView signIn(Administrator administrator) throws Exception{
            return administratorService.signIn(administrator);
        }
    }
    

    处理器调用底层服务类,实现相应的业务

    启动tomcat分别输入正确登录验证和错误登录验证查看效果

    正确显示页面

    正确正确

    错误信息页面

    错误错误

    至此我们就迈出了后台管理系统开发的第一步,虽然教程中还有很多漏洞和缺陷,但我会在之后一点点当作提示贴出来,这个系列会详细记录我的开发过程,请感兴趣的小伙伴多多关注!

    参照:Clone丶记忆

    GitHub项目地址

    相关文章

      网友评论

          本文标题:半暖商城第一季--SpringMVC+Mybatis本地环境开发

          本文链接:https://www.haomeiwen.com/subject/shfogttx.html