Mybatis:

今天上午,讲了Mybatis里的多表连接查询,包括一对一和一对多。

涉及到两个表:

customer表:

Springboot 多表关联查询 性能很差 优化方案_mybatis


orders表:

Springboot 多表关联查询 性能很差 优化方案_mvc_02


从表中我们可以看到,orders表中有一个外键:customer_id,这个外键是customer表的主键。

首先来看一对一的内连接查询,根据订单表的id查出该订单的所有信息以及该订单所属的客户信息。一条订单的记录对应于一个客户,所以这是一对一的表关联查询。

sql语句:

select c.id cid,c.name,c.age,o.id oid,o.orderNumber,o.orderPrice 
from orders o inner join customer c on o.customer_id = c.id 
where o.id = 1

因为customer表和orders表的主键名都叫id,查询的时候如果写select c.*,o.* from orders的话,就搞不清楚哪个id对应于哪张表,所以我们起了别名来将他们区分开来。
对应的实体类中,Orders类中必须有一个类型为Customer的属性customer。
对应的映射文件ordersMapper.xml为:

<?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-->
<mapper namespace="com.sxau.mybatis.mapper.OrdersMapper">
    <resultMap id="ordersMap" type="orders">
        <id property="id" column="oid"></id>
        <result property="orderNumber" column="orderNumber"></result>
        <result property="orderPrice" column="orderPrice"></result>
        <!--javaType里存放的是类名而不是对象名-->
        <association property="customer" javaType="Customer">
            <id property="id" column="cid"></id>
            <result property="name" column="name"></result>
            <result property="age" column="age"></result>
        </association>
    </resultMap>
    <select id="getById" resultMap="ordersMap">
        select c.id cid,c.name,c.age,o.id oid,o.orderNumber,o.orderPrice 
        from orders o inner join customer c on o.customer_id = c.id 
        where o.id = #{id}
    </select>
</mapper>

接下来我们来看一对多的连接。现在的需求是:根据客户的id查询这个客户相关联的所有订单信息。因为一个客户可能对应有多个订单,所以这是一对多的表关联查询。
sql语句:

select c.id cid,c.name,c.age,o.id oid,o.orderNumber,o.orderPrice 
from customer c left join orders o on c.id = o.customer_id 
where c.id = 1

这里如果使用内连接inner join,那么当一个客户没有生成任何订单信息时我们什么也查不到,也就是说我们想根据客户的id来查询客户信息并且捎带地查询该客户名下的订单信息,结果有了客户的id后只因为该客户没有生成订单,就查询不到该客户,这显然是违背了我们的本意,我们希望当客户没有与之关联的订单时,根据客户的id可以查到客户的信息只不过他的订单列表为空。所以,我们放弃使用inner join而改用了left join。left join的意思是左外连接,用左外连接做表关联查询时,它会始终保留左表(即主表)的所有相关列的信息。这样一来,就不会因为某个客户没有生成订单就查不出来这个客户的信息。
对应的实体类中,Customer类中必须有一个类型为List<Orders>的属性ordersList,用来存放该客户的订单对象。
对应的映射文件ordersMapper.xml为:

<?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-->
<!--左外连接,left join 左表里的内容会完全显示-->
<!--记住一个口诀,resultMap的type值永远是这个mapper的namespace下的东西-->
<!--在一方关联多方时要注意的点,1,left左外连接  2,两个表的id名字冲突时必须起别名区分开-->
<mapper namespace="com.sxau.mybatis.mapper.CustomerMapper">
    <resultMap id="customerMap" type="customer">
        <id property="id" column="cid"></id>
        <result property="name" column="name"></result>
        <result property="age" column="age"></result>
        <!--对应多方的集合的绑定,使用collection标签-->
        <collection property="ordersList" ofType="Orders">
            <id property="id" column="oid"></id>
            <result property="orderNumber" column="orderNumber"></result>
            <result property="orderPrice" column="orderPrice"></result>
        </collection>
    </resultMap>
    <select id="getById" resultMap="customerMap">
        select c.id cid,c.name,c.age,o.id oid,o.orderNumber,o.orderPrice 
        from customer c left join orders o on c.id = o.customer_id 
        where c.id = #{id}
    </select>
</mapper>

Spring MVC:

下午老师讲了SpringMVC框架的基本流程以及搭建的步骤。
SpringMVC运行的基本流程:

  1. 向服务器发送HTTP请求,请求被前端控制器 DispatcherServlet 捕获。
  2. DispatcherServlet 根据 -servlet.xml 中的配置对请求的URL进行解析,得到请求资源标识符(URI)。 然后根据该URI,调用 HandlerMapping 获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以 HandlerExecutionChain 对象的形式返回。
  3. DispatcherServlet 根据获得的Handler,选择一个合适的 HandlerAdapter。
  4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
    HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息。
    数据转换:对请求消息进行数据转换。如String转换成Integer、Double等。
    数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等。
    数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中。
  5. Handler(Controller)执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象
  6. 根据返回的ModelAndView,选择一个适合的 ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet。
  7. ViewResolver 结合Model和View,来渲染视图。
  8. 视图负责将渲染结果返回给客户端

入门级SpringMVC框架搭建:

导入jar包:

Springboot 多表关联查询 性能很差 优化方案_web_03


编写配置文件web.xml:

<?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">
    <!--字符编码过滤器,防止中文乱码,这个过滤器必须放在所有内容的开始,否则不起作用-->
    <filter>
        <filter-name>Set Character Encoding</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>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>Set Character Encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!--springmvc框架使用的是servlet进行注册-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>
</web-app>

编写配置文件springmvc.xml:

<!--注册srpingmvc框架-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:task="http://www.springframework.org/schema/task"

       xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
      http://www.springframework.org/schema/mvc
      http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-4.0.xsd
      http://www.springframework.org/schema/aop
      http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
      http://www.springframework.org/schema/tx
      http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
      http://www.springframework.org/schema/task
          http://www.springframework.org/schema/task/spring-task-4.0.xsd">
    <!--指定注解扫描的根包-->
    <context:component-scan base-package="com.oracle.demo.controller"></context:component-scan>
    <!--视图解析器-->
    <bean id = "ResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name = "prefix" value = "/"></property>
        <property name = "suffix" value=".jsp"></property>
    </bean>
</beans>

接下来介绍SpringMVC提交数据的4种方式(常见的):

  1. 第一种:散提(分散提交)
    这种方式一般情况下要提交的数据不是很多,而且数据绑定时名字必须一致。
<%--${pageContext.request.contextPath}表示绝对路径的方式--%>
  <form action="${pageContext.request.contextPath}/admin/one.action" method="post">
    <p>姓名:<input name = "stuname"></p>
    <p>密码:<input type="password" name = "stupwd"></p>
    <p>年龄:<input type = "text" name="stuage"></p>
    <p><input type="submit" value="提交"><input type="reset" value = "重置"></p>
  </form>
@RequestMapping("/one")
    public String one(String stuname,String stupwd,Integer stuage){
        System.out.println(stuname+"-----"+stupwd+"---------"+(stuage+100));
        return "main";
    }
  1. 第二种:封装为一个对象
    这种方式的优点是减少了代码的冗余且不容易出错,但前提是得事先创建好一个对应的java实体类。
<form action="${pageContext.request.contextPath}/admin/two.action" method="post">
    <p>姓名:<input name = "stuname"></p>
    <p>密码:<input type="password" name = "stupwd"></p>
    <p>年龄:<input type = "text" name="stuage"></p>
    <p><input type="submit" value="提交"><input type="reset" value = "重置"></p>
  </form>
@RequestMapping("/two")
    public String two(Student stu){
        System.out.println(stu.getStuname()+"-----"+stu.getStupwd()+"---------"+(stu.getStuage()+100));
        return "main";
    }
  1. 第三种:@RequestParam注解方式
    这种方式的好处在于进行数据绑定时可以定义别名。
<a href="${pageContext.request.contextPath}
  /admin/three.action?name=王丽&age=12">第三种数据提交方式</a>
@RequestMapping("/three")
    public String three(@RequestParam("name") String stuname,@RequestParam("age") int stuage){
        System.out.println(stuname+"-----------"+(stuage+100));
        return "main";
    }
  1. 第四种:使用HttpServletRequest
    这也是最原始的方式,它的缺点是数据的提取,类型转换都需要我们手动完成,优点是HttpServletRequest不局限于可以取数据,还可以调用其他方法完成各种功能。
<form action="${pageContext.request.contextPath}/admin/four.action" method="post">
    <p>姓名:<input name = "stuname"></p>
    <p>密码:<input type="password" name = "stupwd"></p>
    <p>年龄:<input type = "text" name="stuage"></p>
    <p><input type="submit" value="提交"><input type="reset" value = "重置"></p>
  </form>
@RequestMapping("/four")
    public String four(HttpServletRequest request){
        String name = request.getParameter("stuname");
        String pwd = request.getParameter("stupwd");
        int age =Integer.parseInt(request.getParameter("stuage"));
        System.out.println(name+"-----"+pwd+"---------"+(age+100));
        return "main";
    }