华清远见-重庆中心-框架阶段Mybatis技术总结

ORM

Object Relational Mapping 对象关系映射

创建Java类的对象,将其属性和数据库表中的字段名之间构建映射关系。

这样通过操作该类创建的一个对象,就能操作数据库中对应的数据表。

ORM框架就是对象关系映射框架,用于简化JDBC。

主流的ORM框架有Hibernate、JPA、MyBatis、MyBatisPlus等

Mybatis

Mybatis是一个半自动化的ORM框架。原本叫做ibatis,后来升级改名为MyBatis。

MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。

使用XML文件或注解的形式,将SQL语句映射为持久层(dao层)中的方法。

官网:https://mybatis.org/mybatis-3/zh/index.html

特点

  • 方便

  • 简化JDBC

  • 解除SQL语句与业务代码的耦合

  • sql和java代码分离,sql写在xml文件中,方便扩展维护。

  • 支持动态SQL

  • 不同的情况下构造不同的SQL

  • 主流

  • SSM中的M

SSM项目的搭建

整体流程

1.创建基于Maven的webapp项目。

2.修改web.xml版本为4.0 ,创建java、resoureces目录、项目包结构、web-inf下的页面目录。

3.导入依赖:

  • spring-webmvc

  • mybatis

  • mybatis-spring

  • mysqldruid

  • spring-jdbc

  • jstl

<!--SpringWebMVC-->
<dependency>                
     <groupId>org.springframework</groupId>
     <artifactId>spring-webmvc</artifactId>
     <version>5.3.22</version>
</dependency>
<!--Mybatis-->
<dependency>
     <groupId>org.mybatis</groupId>
     <artifactId>mybatis</artifactId>
     <version>3.5.11</version>
</dependency>
<!--Spring集成MyBatis-->
<dependency>
     <groupId>org.mybatis</groupId>
     <artifactId>mybatis-spring</artifactId>
     <version>2.0.7</version>
</dependency>
 <!--数据库连接池Druid-->
<dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>druid</artifactId>
     <version>1.2.8</version>
</dependency>
<!--SpringJDBC-->
<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-jdbc</artifactId>
     <version>5.3.23</version>
</dependency>
<!--MySQL-->
<dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>8.0.29</version>
</dependency>
<!--jstl-->
<dependency>
     <groupId>jstl</groupId>
     <artifactId>jstl</artifactId>
     <version>1.2</version>
</dependency>

4.配置Spring:

  • 在resoureces下创建application.xml

  • 扫描项目根包

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
    <!--扫描使用了Spring注解的根包-->
    <context:component-scan base-package="com.hqyj.ssm01"></context:component-scan>
</beans>
  • 配置web.xml

  • 设置全局参数: ,读取application.xml文件

  • 设置全局监听器:ContextLoaderListener,初始化Spring容器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"    
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
     http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
     <!--通过全局监听器初始化Spring容器-->
     <listener>
         <listenerclass>org.springframework.web.context.ContextLoaderListener</listener-class>
     </listener>
     <!--定义全局参数读取Spring配置文件-->
     <context-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>classpath:application.xml</param-value>
     </context-param>
</web-app>

5.配置SpringMVC:

  • 创建springmvc.xml

  • 扫描控制层所在包

  • 设置内部资源视图解析器:InternalResourceViewResolver,设置前后缀

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/context
     https://www.springframework.org/schema/context/spring-context.xsd">
    <!--扫描使用了SpringMVC注解的根包-->
    <context:component-scan base-package="com.hqyj.ssm01.controller">
    </context:component-scan>
    <!--设置内部资源视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--设置页面前缀和后缀-->
        <property name="prefix" value="/WEB-INF/pages/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>
  • 配置web.xml

  • 设置请求分发器:DispatcherServlet,在其中读取springmvc.xml配置文件

  • 过滤器解决请求中文乱码:CharacterEncodingFilter

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"    
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
     http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
     <!--通过全局监听器初始化Spring容器-->
     <listener>
         <listenerclass>org.springframework.web.context.ContextLoaderListener</listener-class>
     </listener>
     <!--定义全局参数读取Spring配置文件-->
     <context-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>classpath:application.xml</param-value>
     </context-param>
     <!--配置DispatcherServlet-->
     <servlet>
         <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--初始化参数读取SpringMVC配置文件-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <!--过滤器解决请求中文乱码-->
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

6.配置MyBatis

  • 在resoureces中创建mybatis-config.xml(官网模板)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration            
     PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
     "https://mybatis.org/dtd/mybatis-3-config.dtd">
     <configuration>
        <!--设置-->
        <settings>
        <!--开启驼峰命名映射-->
            <setting name="mapUnderscoreToCamelCase" value="true"/>
            <!--开启SQL日志-->
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>
    </configuration>
  • 在application.xml中注入

  • 数据库连接池Druid:DruidDataSource

  • SQL会话工厂:SqlSessionFactoryBean

  • 映射扫描配置器:MapperScannerConfigurer

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd">
    <!--扫描使用了Spring注解的根包-->
    <context:component-scan base-package="com.hqyj.ssm01">
    </context:component-scan>
    <!--注入Druid数据源DruidDataSource-->
    <bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/herodb?serverTimezone=Asia/Shanghai"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
    <!--注入MyBatis核心对象SqlSessionFactoryBean-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
        <!--设置数据源-->
        <property name="dataSource" ref="dataSource"></property>
        <!--设置MyBatis配置文件路径-->
        <property name="configLocation" value="classpath:mybatisconfig.xml"></property>
        <!--设置MyBatis的sql映射文件路径-->
        <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
    </bean>
    <!--扫描项目中的dao层目录-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--设置MyBatis核心对象SqlSessionFactoryBean的名字-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
        <!--dao层根包-->
        <property name="basePackage" value="com.hqyj.ssm01.dao"></property>
    </bean>
</beans>

7.执行sql语句

  • 创建dao层接口,定义方法

//实体类
package com.hqyj.ssm01.entity;
public class Hero {        
    private int id;
    private String name;
    private String sex;
    private String position;
    private int jiage;
    private String shelfTime;
    //省略get/set
}


//dao层接口
package com.hqyj.ssm01.dao;
import com.hqyj.ssm01.entity.Hero;
import org.springframework.stereotype.Repository;
import java.util.List;
/*
* 使用MyBatis时,数据访问层/持久层/dao层,只需定义为接口
* */
@Repository
public interface HeroDao {
     /*查询所有*/
    List<Hero> queryAll();
}


//service层
package com.hqyj.ssm01.service;
import com.hqyj.ssm01.dao.HeroDao;
import com.hqyj.ssm01.entity.Hero;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class HeroService {
    @Autowired
    private HeroDao heroDao;
    public List<Hero> queryAll(){
        return heroDao.queryAll();
    }
}


//controller层
package com.hqyj.ssm01.controller;
import com.hqyj.ssm01.entity.Hero;
import com.hqyj.ssm01.service.HeroService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@Controller
@RequestMapping("/hero")
public class HeroController {
    @Autowired
    private HeroService heroService;
    @RequestMapping("/queryAll")
    public String queryAll(Model model){
        List<Hero> list = heroService.queryAll();
        model.addAttribute("list",list);
        return "heroList";
    }
}
  • 在resoureces下创建mapper目录创建sql映射文件xx.xml(官网模板),在其中定义sql语句

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper        
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--namespace是该映射文件对应的dao层接口全限定名-->
    <mapper namespace="com.hqyj.ssm01.dao.HeroDao">
    <!--查询select标签-->
    <!--id对应dao层中的某个方法名-->
    <!--如果实体类中的属性名和表中的字段名一致,加入resultType属性,设置实体的全限定名-->
    <!--
        <select id="queryAll" resultType="com.hqyj.ssm01.entity.Hero">
            select * from hero
        </select>
    -->
    <!--resultMap标签用于给指定的实体类设置返回结果集映射-->
    <!--id属性自定义该映射名称-->
    <!--type属性指定实体类-->
    <resultMap id="heroMap" type="com.hqyj.ssm01.entity.Hero">
        <!--主键字段用id标签,其他字段用result标签-->
        <!--id和result标签都有两个属性 property表示实体类中的属性名 column表示表中的字段名-->
        <id property="id" column="hid"></id>
        <result property="jiage" column="price"></result>
        <!--如果遇到下划线转驼峰命名,无需在这里自定义映射,可以在mybatis的配置文件中设置开启驼峰命名映射-->
        <!--<result property="shelfTime" column="shelf_time"></result>-->
    </resultMap>
    <!--如果实体中的属性和表中的字段名不一致,使用resultMap属性设置自定义的结果集映射-->
    <select id="queryAll" resultMap="heroMap">
        select * from hero
    </select>
</mapper>

heroList.jsp页面

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <style>
        table{
            border-collapse: collapse;
        }
        td{
            width: 100px;
            height: 20px;
            border: 1px solid skyblue;
        }
    </style>
</head>
<body>
    <table>
        <c:forEach var="hero" items="${list}">
            <tr>
                <td>${hero.id}</td>
                <td>${hero.name}</td>
                <td>${hero.sex}</td>
                <td>${hero.position}</td>
                <td>${hero.jiage}</td>
                <td>${hero.shelfTime}</td>
            </tr>
        </c:forEach>
    </table>
</body>
</html>

SSM项目搭建补充

通过db.properties文件保存连接数据库的信息

.properties文件称为属性文件,其中的数据以键值对(key=value)的形式保存

  • 在resources目录下新建db.properties文件

DB_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver
DB_URL=jdbc:mysql://localhost:3306/bookdb?serverTimezone=Asia/Shanghai
DB_USERNAME=root
DB_PASSWORD=root
  • 在application.xml中读取properties文件

<!--读取properties文件-->
<context:property-placeholder location="classpath:db.properties">
</context:property-placeholder>
  • 读取时使用EL表达式访问其中的键

<!--Druid数据源DruidDataSource-->
     <bean class="com.alibaba.druid.pool.DruidDataSource" id="druidDataSource">
        <property name="driverClassName" value="${DB_DRIVER_CLASS_NAME}"></property>
        <property name="url" value="${DB_URL}"></property>
        <property name="username" value="${DB_USERNAME}"></property>
        <property name="password" value="${DB_PASSWORD}"></property>
     </bean>
MyBatis配置文件常用设置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration    
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <!--设置-->
        <settings>
        <!--开启驼峰命名映射-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--开启SQL日志-->
        <setting name="logImpl" value="STDOUT_LOGGING"/></settings>
    </configuration>
MyBatis基本增删改查
dao层
package com.hqyj.ssm02.dao;
import com.hqyj.ssm02.entity.BookInfo;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface BookInfoDao {
    //查询所有
    List<BookInfo> queryAll();
    //根据id删除
    int delete(int id);
    //添加
    int insert(BookInfo bookInfo);
    //根据id查询
    BookInfo findById(int no);
    //修改时,参数通常为一个完整的修改对象
    int update(BookInfo bookInfo);
    //批量删除
    //分页查询
    //关键字分页
    //如果dao层中某个方法不止1个参数,需要给每个参数添加@Param("参数名")注解,给该参数命名
    //命名后,才能在mybatis的sql映射文件中使用该参数,即#{参数名}
    //如这里的newPrice,在sql中用#{newPrice}获取
    int update(@Param("newPrice") int bookPrice,@Param("newNum") intbookNum,@Param("updateId") int bookId);
}
sql映射文件
<?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">  
<mapper namespace="com.hqyj.ssm02.dao.BookInfoDao">
    <!--查询所有-->
    <select id="queryAll" resultType="com.hqyj.ssm02.entity.BookInfo">
        select *
        from book_info
    </select>
    <!--根据id查询-->
    <select id="findById" resultType="com.hqyj.ssm02.entity.BookInfo">
        select *
        from book_info
        where book_id = #{no}
    </select>
    <!--根据id删除-->
    <delete id="delete">
        delete
        from book_info
        where book_id = #{id}
    </delete>
    <!--添加-->
    <insert id="insert">
        insert into book_info
        values (null, #{typeId}, #{bookName}, #{bookAuthor}, #{bookPrice}, #{bookNum}, #{publisherDate}, #{bookImg})
    </insert>
    <!--修改-->
    <update id="update">
        update book_info
        set book_price=#{newPrice},
            book_num=#{newNum},
            type_id=#{typeId}
        where book_id = #{updateId}
    </update>
</mapper>
解决无法引入静态资源的问题
  • 在webapp目录下,新建一个目录,通常命名为static。将项目中的静态资源文件都保存于此。

  • 在springmvc.xml中

<!--映射静态资源目录-->
<!--location表示要映射的静态资源目录-->
<!--mapping表示最终通过哪种方式进行访问。这里表示只要以/static开头的请求,都可以访问静态
资源目录-->
<mvc:resources mapping="/static/**" location="/static/"></mvc:resources>
<!--开启注解驱动,有这个标签,SpringMVC就能区分哪个是资源文件,哪个是Controller-->
<mvc:annotation-driven></mvc:annotation-driven>
导入BootStrap的样式和JS文件
<%--导入bootstrap的css文件--%>
<link href="${pageContext.request.contextPath}/static/bootstrap-3.4.1-dist/css/bootstrap.min.css" rel="stylesheet" type="text/css">
<%--导入jquery--%>
<script src="${pageContext.request.contextPath}/static/bootstrap-3.4.1-dist/js/jquery-3.6.2.min.js"></script>
<%--导入boostrap的js文件--%>
<script src="${pageContext.request.contextPath}/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>

在SpringMVC中,让某个控制层中的方法返回JSON格式的数据

  • 添加依赖

<!--jackson:将数据转换为JSON格式-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.14.2</version>
</dependency>
  • @ResponseBody注解

该注解可以加在类或方法上

  • 如果加在方法上,表示该方法的返回值类型为JSON格式

  • 如果加在类上,表示该类中的所有方法的返回值类型为JSON格式

package com.hqyj.ssm02.controller;
import com.hqyj.ssm02.entity.BookType;
import com.hqyj.ssm02.service.BookTypeService;
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.ResponseBody;
import java.util.List;
@Controller
@RequestMapping("/bookType")
public class BookTypeController {
    @Autowired
    private BookTypeService bookTypeService;
    //如果controller的某个方法返回一个JSON字符串,需要使用@ResponseBody
    @RequestMapping("/queryAllToJson")
    @ResponseBody//表示该方法无论返回值是什么,都返回JSON格式字符串
    public List<BookType> queryAllToJson(){
        List<BookType> list = bookTypeService.queryAll();
        return list;
    }
}

SSM项目中使用Ajax

ajax依赖于jquery,所以先保证页面中存在jquery.js。

$.ajax({
    url:"访问地址",
    data:{
    "提交的参数名":"实际值",
    "提交的参数名":"实际值"
    },
    type:"get/post",
    success:function(res){
    //成功后的回调函数,res为访问后的结果,必须是json格式
    }
});

在前端页面中使用ajax访问controller时,controller的返回值必须是一个JSON格式的字符串。

所以controller中的方法上要加入@ResponseBody注解

在SpringMVC中使用Session

方式一:@SessionAttributes注解

由于SSM项目中,没有使用servlet,所以不能通request.getSession()方法来获取session对象。

在控制器Controller中,在类上加入@SessionAttributes注解@SessionAttributes({"参数1","参数2"})表示在session中保存两个参数

再在某个方法中,通过Model对象调用addAttribute("参数1",对象)方法将指定对象保存到session中

使用和销毁
@Controller
@RequestMapping("/sysAdmin")
//如果要将数据保存到session中,先使用该注解定义session中的参数名
@SessionAttributes({"sysAdmin"})
public class SysAdminController {
  @Autowired
  SysAdminService sysAdminService;
  @RequestMapping("/login")
  @ResponseBody
  public SysAdmin login(SysAdmin sysAdmin,Model model) {
    SysAdmin login = sysAdminService.login(sysAdmin);
    //将登录成功的对象保存到session中
    model.addAttribute("sysAdmin",login);
    return login;
 }
  /*登出时销毁session*/
  @RequestMapping("/logout")
  public String logout(SessionStatus sessionStatus) {
    //在方法中使用SessionStatus参数表示session状态对象
    //调用setComplete()方法,将session设置为完成状态
    sessionStatus.setComplete();
    return "redirect:/login";
 }
}
方式二:HttpServletRequest参数

给项目添加javax.servlet.api依赖,给controller中某个方法添加 HttpServletRequest参数后,获取session使用

<!--如果要使用servlet相关内容-->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>4.0.1</version>
</dependency>
使用和销毁
@Controller
@RequestMapping("/sysAdmin")
//如果要将数据保存到session中,先使用该注解定义session中的参数名
@SessionAttributes({"sysAdmin"})
public class SysAdminController {
  @Autowired
  SysAdminService sysAdminService;
  @RequestMapping("/login")
  @ResponseBody
  public SysAdmin login(SysAdmin sysAdmin,HttpServletRequest req) {
    SysAdmin login = sysAdminService.login(sysAdmin);
    //将登录成功的对象保存到session中
    req.getSession().setAttribute("sysAdmin",login);
    return login;
 }
  /*登出时销毁session*/
  @RequestMapping("/logout")
  public String logout(HttpServletRequest req) {
   req.getSession().invalidate();
    return "redirect:/login";
 }
}

拦截器

每次请求controller时,都要经过的一个类。

当一个项目中有过滤器、拦截器和controller时的执行流程

实现过程
1.导入servlet依赖
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>4.0.1</version>
</dependency>
2.自定义一个类,实现拦截器HandlerInterceptor接口

其中有三个default方法可以重写

  • preHandle

  • 在发送请求后,DispatcherServlet解析控制器中某个RequestMapping之前执行的方法

  • 该方法返回true时,请求才能继续

  • postHandle

  • 在preHandle方法返回值为true后执行

  • 在DispatcherServlet解析控制器中某个RequestMapping之后执行

  • afterCompletion

  • 在preHandle方法返回true后执行

  • 在解析视图后执行的方法

这里只需重写preHandle方法即可

package com.hqyj.ssm02.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/*
* 自定义拦截器,用户拦截未登录时的请求
* */
public class MyInterceptor implements HandlerInterceptor {
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse
response, Object handler) throws Exception {
    String requestURI = request.getRequestURI();
    //登录成功后,会在session中保存一个名为sysAdmin的对象
    Object sysAdmin = request.getSession().getAttribute("sysAdmin");
    //如果有对象,说明登录成功,可以放行return true
    if(sysAdmin!=null){
      return true;
   }else{
      response.sendRedirect(request.getContextPath()+"/login");
   }
    System.out.println(requestURI+"试图访问,拦截成功");
    return false;
 }
}
3.在springmvc.xml中配置拦截器
<!--配置拦截器们-->
<mvc:interceptors>
  <!--配置某个拦截器-->
  <mvc:interceptor>
    <!--设置要拦截的请求,这里的/**表示拦截一切请求-->
    <mvc:mapping path="/**"/>
    <!--设置不拦截的请求-->
    <!--放行登录和注册页-->
    <mvc:exclude-mapping path="/login"/>
    <mvc:exclude-mapping path="/register"/>
    <!--放行静态资源-->
    <mvc:exclude-mapping path="/static/**"/>
    <!--放行用户模块-->
    <mvc:exclude-mapping path="/sysAdmin/**"/>
    <!--注入指定的拦截器-->
    <bean class="com.hqyj.ssm02.interceptor.MyInterceptor"></bean>
  </mvc:interceptor>
</mvc:interceptors>

单元测试JUnit

1.导入依赖

如果只在普通的Maven项目中使用,只需导入该依赖

<!--单元测试-->
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.13.2</version>
  <scope>test</scope>
</dependency>

如果要在Spring环境下使用,还需

<!--spring集成JUnit-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>5.3.23</version>
  <scope>test</scope>
</dependency>
2.创建单元测试类所在目录

在项目的src目录下新建test目录,会自动在其中创建java目录

3.使用
  • 在test/java目录下,新建一个类

  • 在类上加入

  • @ContextConfiguration("classpath:application.xml")

  • @RunWith(SpringJUnit4ClassRunner.class)

  • 在类中创建公开的无返回值的无参数方法,加入@Test注解

  • 在该方法上右键运行,运行的是当前方法,在类中空白处右键运行,运行的是当前类中的所有方法。

import com.hqyj.ssm02.dao.BookInfoDao;
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;
//解析Spring配置文件
@ContextConfiguration("classpath:application.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class Test {
  /*
  * 单元测试的方法必须是无参数无返回值的公共方法
  * */
  @org.junit.Test
    public void fun(){
    System.out.println("单元测试");
 }
  @Autowired
  private BookInfoDao bookInfoDao;
  @org.junit.Test
    public void test1(){
    System.out.println(bookInfoDao.queryAll());
 }
}

查询

单表查询

dao中的方法

List<BookType> queryAll();

sql映射文件

  • 当数据库表中的字段名和实体属性一致或开启了驼峰命名映射,使用resultType

<select id="queryAll" resultType="com.xxx.entity.BookType">
     select * from book_type
</select
  • 当数据库表中的字段名和实体属性不一致,使用resultMap

<select id="queryAll" resultMap="map">
 select * from book_type
</select>
<!--自定义结果集映射-->
<resultMap id="map" type="com.xxx.entity.BookType">
  <!--主键使用id标签,其他字段使用result标签-->
  <!--column对应字段名 property对应属性名-->
  <id column="type_id" property="typeId"></id>
  <result column="type_name" property="typeName"></result>
</resultMap>
多表查询
多对一查询

多对一表示多个从表实体对应一个主表实体。如多本图书对应一个图书类型。

在从表实体中,额外添加一个属性:对应的主表实体对象

public class BookInfo {
  private int bookId;
  private int typeId;
  private String bookName;
  private String bookAuthor;
  private int bookPrice;
  private int bookNum;
  private String publisherDate;
  private String bookImg;
  //多对一查询,额外添加外键对应的实体对象
  private BookType bt;
}

dao中的方法

List<BookInfo> queryAll();

sql映射文件

  • 关联查询:构建特殊的sql语句

  • 适合外键对应的表中字段比较少的情况下使用

<!--sql语句除了查询自身表之外,还要查询关联的表中的字段,将其命名为"实体对象.属性"-->
<select id="queryAll" resultType="com.xxx.entity.BookInfo">    
    select bi.*,type_name as 'bt.typeName' from book_info bi,book_type bt
    where bi.type_id=bt.type_id
</select
  • 连接查询(不建议)

<select id="queryAll" resultMap="booksMap">
     select *
     from book_info bi,
         book_type bt
     where bi.type_id = bt.type_id
</select>
<resultMap id="booksMap" type="com.hqyj.ssm02.entity.BookInfo">
      <!--主键用id标签,其他字段用result标签赋值-->
      <id property="bookId" column="book_id"></id>
      <result property="bookName" column="book_name"></result>
      <result property="typeId" column="type_id"></result>
      <result property="bookAuthor" column="book_author"></result>
      <result property="bookPrice" column="book_price"></result>
      <result property="bookNum" column="book_num"></result>
      <result property="bookImg" column="book_img"></result>
      <result property="publisherDate" column="publisher_date"></result>
      <!--外键对应的实体对象使用association标签,通过property属性对应实体对象名-->
      <association property="bt" >
<select id="queryAll" resultMap="booksMap">
     select *
     from book_info bi,
         book_type bt
     where bi.type_id = bt.type_id
</select>
<resultMap id="booksMap" type="com.hqyj.ssm02.entity.BookInfo">
  <!--主键用id标签,其他字段用result标签赋值-->
  <id property="bookId" column="book_id"></id>
  <result property="bookName" column="book_name"></result>
  <result property="typeId" column="type_id"></result>
  <result property="bookAuthor" column="book_author"></result>
  <result property="bookPrice" column="book_price"></result>
  <result property="bookNum" column="book_num"></result>
  <result property="bookImg" column="book_img"></result>
  <result property="publisherDate" column="publisher_date"></result>
  <!--外键对应的实体对象使用association标签,通过property属性对应实体对象名-->
  <association property="bt" >
</resultMap>
  • 子查询

  • 适合外键对应的表中字段比较多的情况

<!--1.查询从表-->
<select id="queryAll" resultMap="booksMap">
 select * from book_info
</select>
<!--2.自定义结果集映射,使用type_id进行子查询,下方的sql-->
<resultMap id="booksMap" type="com.hqyj.ssm02.entity.BookInfo">
  <!--由于使用type_id进行了子查询,所以如果要给type_id赋值,需要再次进行映射-->
  <result column="type_id" property="typeId"></result>
  <!--外键对应的实体对象,使用association赋值-->
  <!--如果这里的子查询来自当前映射文件-->
  <association property="bt" column="type_id" select="findTypeByTypeId">
</association>
  <!--如果这里的子查询来自于其他dao中的方法-->
  <!--<association property="bookType" column="type_id"
select="com.hqyj.ssm02.dao.BookTypeDao.findById"></association>-->
</resultMap>
<!--3.根据type_id查询完整对象-->
<select id="findTypeByTypeId" resultType="com.hqyj.ssm02.entity.BookType">
 select * from book_type where type_id=#{typeId}
</select>
一对多查询

一对多表示一个主表实体对应多个从表实体。如一个图书类型对应多本图书。

在主表对应的实体类中,额外添加一个属性:多个从表对象的集合

public class BookType {
  private int typeId;
  private String typeName;
  private List<BookInfo> books;
}

dao中的方法

BookType queryBooksByType(int typeId);

sql映射文件

  • 连接查询

<!--一对多查询,方式一:连接查询-->
<select id="queryBooksByType" resultMap="testMap">
 select *
 from book_info bi
 inner join book_type bt on bi.type_id = bt.type_id
 where bi.type_id = #{typeId}
</select>
<!--自定义结果集映射-->
<resultMap id="testMap" type="com.hqyj.ssm02.entity.BookType">
  <id property="typeId" column="type_id"></id>
  <result property="typeName" column="type_name"></result>
  <!--集合类型的属性,使用collection标签,使用ofType设置集合中的对象类型-->
  <collection property="books" ofType="com.hqyj.ssm02.entity.BookInfo">
    <id column="book_id" property="bookId"></id>
    <result column="book_name" property="bookName"></result>
    <result column="type_id" property="typeId"></result>
    <result column="book_author" property="bookAuthor"></result>
    <result column="book_num" property="bookNum"></result>
    <result column="book_price" property="bookPrice"></result>
    <result column="book_img" property="bookImg"></result>
    <result column="publisher_date" property="publisherDate"></result>
  </collection>
</resultMap>
  • 子查询

<!--一对多查询,方式二:子查询-->
<!--1.根据类型编号查询自身表-->
<select id="queryBooksByType" resultMap="testMap">
 select *
 from book_type
 where type_id = #{typeId}
</select>
<!--2.设置结果集映射-->
<resultMap id="testMap" type="com.hqyj.ssm02.entity.BookType">
  <id column="type_id" property="typeId"></id>
  <result column="type_name" property="typeName"></result>
  <!--集合对象,使用collection标签。使用type_id字段执行子查询getBooksByTypeId,将
结果映射到books属性-->
  <collection property="books" column="type_id" select="getBooksByTypeId">
</collection>
</resultMap>
<!--3.子查询,根据类型编号查询所有对应的图书集合-->
<select id="getBooksByTypeId" resultType="com.hqyj.ssm02.entity.BookInfo">
 select *
 from book_info
 where type_id = #{typeId}
</select>
总结
  • 多对一

如果查询要以从表数据为主,关联主表相应的数据时,属于多对一查询。如查询所有图书的同时,

显示其类型。

建议使用子查询或自定义特殊的sql语句。都需要在从表实体中添加一个主表对象属性。

  • 主表字段比较少,建议使用自定义特殊的sql语句,保证字段重命名为"主表对象.属性"

  • 主表字段比较多,建议使用子查询,使用 标签映射主表对象属性

  • 一对多

如果查询要以主表数据为主,关联该主键对应的从表实体对象的集合时,属于一对多查询。如查询

某个类型,同时显示该类型下的所有图书。

建议使用子查询。在主表实体中添加一个从表集合属性。

使用 标签映射从表集合属性

多条件查询

参考#{}和${}的用法

#{}和${}

在mybatis的sql映射文件中,可以使用#{}和${}表示sql语句中的参数。

#{}相当于预处理,会将参数用?占位后传值

{}相当于拼接,会将参数原样拼接

通常使用#{},防止sql注入

-- #{}会自动用''将参数引起来,如参数为admin
select * from user where username=#{username}
select * from user where username='admin'
-- ${}会将参数原样拼接,如参数为admin
select * from user where username=${username}
select * from user where username=admin

${}使用情况

如有批量删除的sql,需要传递" 1,2,3,4,5"参数

delete from user  where id in (1,2,3,4,5)

int delelteByIds(String ids);
//如实际传递的是"1,2,3,4,
5" 
<delete id="deleteByIds">
    delete from user where id in (${ids})
</delete>
#{}的使用
  • 如果dao层接口中的方法参数为一个原始类型或字符串时

  • sql语句#{}中的参数是接口中方法的形参名

public interface UserDao{
  User findById(int userId);
}
<select id="findById" resultType="com.xxx.entity.User">
      select * from user where id=#{userId}
</select>
  • 如果dao层接口中的方法参数为一个实体对象时

  • sql语句#{}中的参数必须是接口中方法参数的某个属性

public interface UserDao{
  User login(User user);
}
<select id="login" resultType="com.xxx.entity.User">
      select * from user where username=#{username} and password=#{password}
</select>
  • 如果dao层接口中的方法参数不止一个时

  • 给每个参数添加@Param("自定义参数名")

  • sql语句#{}中的参数必须是@Param注解中自定义的名称

public interface UserDao{
  User update(@Param("bianhao")int id,@Param("mima")String password);
}
<update id="update">
     update user set password=#{mima} where id=#{bianhao}
</update>
动态SQL
  • 搭配 用于修改

<!--动态SQL:set-if用于修改-->
<update id="testUpdate">
 update book_info
  <set>
   book_id=#{bookId},
    <if test="bookName!=null">
     book_name = #{bookName},
    </if>
    <if test="bookPrice!=null">
     book_price = #{bookPrice}
    </if>
  </set>
 where book_id=#{bookId}
</update>
  • 搭配 用于查询、修改、删除时的条件

<select id="queryByCondition" resultType="com.xxx.entity.BookInfo">
    select * from book_info
      <where>
        <if test="bookName!=null">
           book_name = #{bookName}
        </if>
        <if test="bookAuthor!=null">
           and book_author = #{bookAuthor}
        </if>
        <if test="typeId!=null">
           and type_id = #{typeId}
        </if>
      </where>
</select>
  • 搭配 可以替换set-if和where-if

该标签有四个属性

prefix 表示如果trim标签中有if条件满足,就在整个trim部分添加指定前缀

suffix 表示如果trim标签中有if条件满足,就在整个trim部分添加指定后缀

prefixOverrides 表示去除整个trim部分多余的前缀

suffixOverrides 表示去除整个trim部分多余的后缀

使用trim实现修改

可以修改所有字段,也可以修改部分字段

<update id="testUpdate">
 update book_info
  <!--prefix="set"表示在所有内容前加入set关键字-->
  <!--suffixOverrides=","表示所有内容之后如果有多余的逗号,去掉逗号-->
  <trim prefix="set" suffixOverrides=",">
   book_id=#{bookId},
    <if test="bookName!=null">
     book_name = #{bookName},
    </if>
    <if test="bookAuthor!=null">
     book_author = #{bookAuthor},
    </if>
    <if test="bookNum!=null">
     book_num = #{bookNum},
    </if>
    <if test="bookPrice!=null">
     book_price = #{bookPrice},
    </if>
    <if test="publisherDate!=null">
     publisher_date = #{publisherDate}
    </if>
  </trim>
 where book_id=#{bookId}
</update>
使用trim标签实现多条件查询
<select id="queryByCondition" resultType="com.hqyj.ssm02.entity.BookInfo">
 SELECT bi.*,type_name as 'bookType.typeName' FROM book_info bi,book_type bt
  <!--prefix="where"表示在所有内容前加入where关键字-->
  <!--prefixOverrides="and"表示所有内容之前如果有多余的and,去掉and-->
  <!--suffix="order by book_id desc"表示在所有内容之后加入order by book_id desc-->
  <trim prefix="where" prefixOverrides="and" suffix="order by book_id desc">
   bi.type_id=bt.type_id
    <if test="bookName!=null">
     and book_name like concat ('%',#{bookName},'%')
    </if>
    <if test="bookAuthor!=null">
     and book_author like concat ('%',#{bookAuthor},'%')
    </if>
    <if test="typeId!=0">
     and bt.type_id =#{typeId}
    </if>
  </trim>
</select>
多选删除具体实现

页面核心js

$(function () {
  //一键全选按钮
  $("#checkAll").click(function () {
    var state = this.checked;
    $(".checkDel").each(function () {
      this.checked = state;
   });
 });
  //删除所选按钮
  $("#deleteAll").click(function () {
    //定义保存id的数组
    var ids = [];
    //获取当前被选中的复选框所在的tr
    let $tr = $(".checkDel:checked").parent().parent();
    //遍历所选的tr
    $tr.each(function () {
      //获取id对应的td的值
      var id = $(this).children("td:eq(1)").text();
      //保存到数组中
      ids.push(id);
   });
    //至少选中一项
    if (ids.length == 0) {
      alert("请至少选中一项");
      return; 
   }
    if (!confirm("确认要删除这" + ids.length + "条数据吗")) {
      return;
   }
    //使用ajax访问controller删除所选
    $.ajax({
      url: "${pageContext.request.contextPath}/bookInfo/deleteByChecked",
      data: {
        "ids": ids
     },
      type: "post",
      //ajax提交数组,需要添加一个参数
      traditional: true,
      success: function () {
        location.reload();
     }
   });
 });
});

dao

//批量删除
int deleteByIds(@Param("idList") List<Integer> idList);

mapper.xml

<!--批量删除-->
<delete id="deleteByIds">
 delete from book_info where book_id in
  <foreach collection="idList" item="id" open="(" close=")" separator=",">
   #{id}
  </foreach>
</delete>

service

public void deleteByChecked(Integer[] ids) {
  //将数组转换为集合
  List<Integer> list = Arrays.asList(ids);
  bookInfoDao.deleteByIds(list);
}

controller

@RequestMapping("/deleteByChecked")
public String deleteByChecked(Integer[] ids){
  bookInfoService.deleteByChecked(ids);
  return "redirect:queryAll";
}
分页

使用分页组件PageHelper

1.导入依赖
<!--分页组件-->
<dependency>
  <groupId>com.github.pagehelper</groupId>
  <artifactId>pagehelper</artifactId>
  <version>5.3.2</version>
</dependency>
2.在mybatis的配置文件中
<!--设置分页插件-->
<plugins>
  <plugin interceptor="com.github.pagehelper.PageInterceptor">
    <!--保证翻页不会超出范围-->
    <property name="reasonable" value="true"/>
  </plugin>
</plugins>
3.使用
  • 通过PageHelper类调用静态方法startPage(当前页数,每页显示的记录数)开启分页

  • 查询所有,返回集合

  • 创建PageInfo分页模型对象,构造方法的参数为查询出的集合,设置泛型,

//定义当前页和每页显示的数量
int pageNum=1;
int size=10;
//开启分页
PageHelper.startPage(pageNum,size);
//正常调用查询,返回查询到的数据集合
BookInfo bookInfo = new BookInfo();
bookInfo.setTypeId(1);
List<BookInfo> list = bookInfoDao.queryByCondition(bookInfo);
//创建分页模型对象,构造方法的参数为查询出的结果,设置泛型,
PageInfo<BookInfo> pageInfo = new PageInfo<>(list);
//此时分页相关数据都保存在pageInfo对象中
System.out.println("总记录数"+pageInfo.getTotal());
System.out.println("最大页数"+pageInfo.getPages());
System.out.println("当前页"+pageInfo.getPageNum());
System.out.println("当前容量"+pageInfo.getSize());
System.out.println("当前分页数据"+pageInfo.getList());
System.out.println("是否有上一页"+pageInfo.isHasPreviousPage());
System.out.println("是否有下一页"+pageInfo.isHasNextPagexml
System.out.println("是否是首页"+pageInfo.isIsFirstPage());
System.out.println("是否是尾页"+pageInfo.isIsLastPage());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值