框架

一套规范。

实际是他人实现的一系列接口和类的集合。通入导入对应框架的jar文件(maven项目导入对应的依赖),进行适当的配置,就能使用其中的所有内容。

开发者可以省去很多模板代码,如dao中的CRUD,MVC模式下层与层之间的关联。只需要集中精力实现项目中的业务逻辑部分。

Java主流框架

Spring、SpringMVC、MyBatis、MyBatisPlus、Hibernate、JPA等。

SSH:最初是Spring+Stucts2+Hibernate组成,之后Stucts2被SpringMVC取代。

SSM:Spring+SpringMVC+MyBatis

新项目使用SpringBoot,早起的SSH项目由于维护成本高,基本不会推翻重做,但会维护一些SSM项目。

无论是SSH还是SSM,Spring、SpringMVC必不可少。从2004年推出至今,依旧是主流框架中不可获取的一部分。

Spring

概念

一个轻量级开源的Java框架。是一个管理项目中对象的容器,同时也是其他框架的粘合器,目的就是对项目进行解耦。

轻量级:对原有代码的侵入很小。

IOC

Inversion Of Control 控制反转

DI

Dependency Injection 依赖注入

控制反转(IOC)是一种思想,就是让创建对象的控制权由自身交给第三方,控制反转这种思想,通过依赖注入(DI)的方式实现。

IOC和DI其实都是在描述控制反转,IOC是思想,DI是具体实现方式。

这里的第三方,就是Spring。Spring是一个容器,可以管理所有对象的创建和他们之间的依赖关系。

可以理解为:“Spring就是用来管理对象的,在需要用到某个对象的时候,帮我们自动创建”。

AOP

Aspect Orintend Programming 面向切面编程

Spring核心注解

在Spring配置文件中加入

<!--设置要扫描的包,扫描这个包下所有使用了注解的类-->
<context:component-scan base-package="com.hqyj.spring02.bookSystem"></context:component-scan>

类上加的注解

  • @Component

    • 当一个类不好归纳时,定义为普通组件
  • @Controller
    • 定义一个类为控制层组件
  • @Service
    • 定义一个类为业务层组件
  • @Repository
    • 定义一个类为持久层(数组访问层)组件
  • @Lazy/@Lazy(value=true)
    • 设置该类为懒加载。
  • @Scope(value=“singleton/prototype”)
    • 设置为单例/原型模式。

说明

以上注解公共特点

  • 都是将对应类的对象注入到Spring容器中,用于替换配置文件中的bean标签
  • 都默认是单例模式非懒加载
  • 默认注入的对象id为当前类的类名首字母小写形式
    • 如在BookDao类上添加,id默认为bookDao
  • 可以通过注解的value属性自定义注入的对象的id名,如@Component(value=“key”)表示注入的对象id为key

属性上加的注解

  • @Autowired

    • 优先使用byType方式从Spring容器中获取对应类型的对象自动装配。先检索Spring容器中对应类型对象的数量,如果数量为0直接报错;数量为1直接装配

      数量大于1,会再尝试使用byName方式获取对应id的对象,但要配合@Qualifier(value=“某个对象的id”)一起使用,指定id进行装配

  • @Qualifier(value=“某个对象的id”)

    • 配合@Autowired注解,使用byName方式获取某个对象id的bean进行装配
  • @Resource(name=“某个对象的id”)

    • 该注解相当于@Autowired+@Qualifier(value=“某个对象的id”)

    • 优先使用byName方式,从Spring容器中检索name为指定名的对象进行装配,如果没有则尝试使用byType方式,要求对象有且只有一个,否则也会报错。

说明

  • 如果要在某个类中使用Spring容器中的某个对象时,只需定义成员变量,无需创建对象,通过@Autowired或@Resource注解进行自动装配

  • 实际开发中,绝大部分情况下,需要自动装配对象有且只有一个,并且命名规范,所以@Autowired或@Resource区别不是很大。@Autowired优先使用byType方式,@Resource优先使用byName方式

  • 如果@Resource不能使用,是因为缺少javax.annotation包,需要引入对应依赖

    <dependency><groupId>javax.annotation</groupId><artifactId>javax.annotation-api</artifactId><version>1.3.2</version>
    </dependency>
    

MVC

MVC设计思想并不是某个语言特有的设计思想,而是一种通用的模式。

是将一个应用分为三个组成部分:Model模型,View视图,Controller控制器

这样会降低系统的耦合度,提高它的可扩展性和维护性。

SpringMVC

在Web阶段中,控制层是由Servlet实现,传统的Servlet,需要创建、重写方法、配置映射。使用时极不方便,SpringMVC可以替换Servlet

SpringMVC是Spring框架中位于Web开发中的一个模块,是Spring基于MVC设计模式设计的轻量级Web框架。

SpringMVC提供了一个DispatcherServlet的类,是一个Servlet。它在指定映射(通常设置为/或*.do)接收某个请求后,调用相应的模型处理得到结果,再通过视图解析器,跳转到指定页面,将结果进行渲染。

原理大致为:配置SpringMVC中的DispatcherServlet将其映射设置为/或.do。*

如果是/表示一切请求先经过它,如果是*.do表示以.do结尾的请求先经过它,

它对该请求进行解析,指定某个Controller中的某个方法,这些方法通常返回一个字符串,

这个字符串是一个页面的名称,再通过视图解析器,将该字符串解析为某个视图的名称,跳转到该视图页面。

使用SpringMVC

1.创建webapp项目

  • 修改web.xml版本为4.0
  • 创建java和resources目录
  • 创建包结构

2.添加依赖

<!-- spring-webmvc -->
<!-- 这个依赖会包含spring-web和spring-context -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.23</version>
</dependency>

3.配置初始化Spring

  • 在resources目录下创建Spring配置文件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"><!--设置扫描使用了注解的根包--><context:component-scan base-package="com.hqyj.springmvc"></context:component-scan>
    </beans>
    
  • 在web.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><listener-class>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>
    

4.配置SpringMVC

  • 在resources目录下创建配置SpringMVC的配置文件springmvc.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"><!--扫描控制层所在的包--><context:component-scan base-package="com.hqyj.springmvc.controller"></context:component-scan><!--配置内部资源视图解析器--><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><!--最终控制层跳转的页面所在的路径及页面自身后缀名--><!--jsp页面不建议直接通过浏览器访问。在WEB-INF目录下在资源,无法通过浏览器直接方法,所以将jsp保存在WEB-INF目录下,最好创建一个pages--><property name="prefix" value="/WEB-INF/pages/"></property><!--现阶段使用jsp输出数据,所以后缀为.jsp--><property name="suffix" value=".jsp"></property></bean>
    </beans>
    
  • 在web.xml中配置DispatcherServlet

    • 将该Servlet的请求映射设置为/,表示所有请求都会访问该Servlet,由该Servlet再进行分发
    <!--配置DispatcherServlet-->
    <servlet><servlet-name>dispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--设置该Servlet的初始化参数,用于读取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><!--设置该servlet的映射为/或*.do--><url-pattern>/</url-pattern>
    </servlet-mapping>
    

5.在WEB-INF目录下创建一个pages目录,在其中创建一个welcome.jsp页面

  • 通常jsp页面不允许被浏览器直接访问,需要保存在WEB-INF目录下

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jeggBi2T-1676453638385)(D:\框架\SpringMVC.assets\image-20230114144258023.png)]

6.编写控制层代码

  • 在controller包下创建一个类,加上@Controller注解

  • 该类中定义的方法方法加入@RequestMapping()注解表示访问该方法的映射

  • 该类中定义的方法返回值通常为字符串,表示某个页面的名称,也可以是另一个controller中的方法映射名

    package com.hqyj.springmvc.controller;import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;//加入@Controller注解表示该类是一个控制层类,替换之前的servlet
    @Controller
    //该注解如果只设置请求映射,直接填字符串
    @RequestMapping("/first")
    public class HelloController {//该注解如果还有其他参数要设置,路径用path赋值@RequestMapping(path="/hello")public String hello(){//返回的字符串是某个页面的名称或另一个控制层中方法的请求映射return "welcome";}
    }
    

将项目部署到tomcat后,访问http://localhost:8080/SpringMVC_war_exploded/first/hello,即可跳转到指定页面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XVbN0mGS-1676453638387)(D:\框架\SpringMVC.assets\image-20230114144225441.png)]

SpringMVC相关注解

  • @Controller

    • 只能写在类上,表示该类属于一个控制器
  • @RequestMapping(“/请求映射名”)/@RequestMapping(value=“/请求映射名”)/@RequestMapping(path=“/请求映射名”)

    • 该注解可以写在类或方法上。写在类上用于区分功能模块,写在类上用于区分具体功能
    • 默认写一个属性或value或path后的值,都表示访问该类或该方法时的请求映射
  • @RequestMapping(value=“/请求映射名”,method=RequestMethod.GET/POST/PUT/DELETE)

    • method属性表示使用哪种请求方式访问该类或该方法
    • 如果注解中不止一个属性,每个属性都需要指定属性名
  • **@GetMapping(“/请求映射名”)**相当于@RequestMapping(value=“/请求映射名”,method=RequestMethod.GET)

    • post、put、delete同理
    • @GetMapping只能写在方法上
  • @PathVariable

    • 该注解写在某个方法的某个形参上

    • 通常配合@RequestMapping(“/{path}”)获取请求时传递的参数

      @RequestMapping("/{path}")
      public String fun(@PathVariable("path") String pageName){return pageName;
      }
      //当前方法如果通过"localhost:8080/项目名/hello"访问,就会跳转到hello.jsp
      //当前方法如果通过"localhost:8080/项目名/error"访问,就会跳转到error.jsp
      //映射中的/{path}就是获取路径中的hello或error,将其赋值给形参
      //通常用于跳转指定页面
      
  • @RequestParam(value=“传递的参数名”,defaultValue =“没有传递参数时的默认值”)

    • 该注解写在某个方法的某个参数上
    • 用于获取提交的数据,可以设置默认值在没有提交数据时使用

解决提交数据时的中文乱码

使用过滤器解决中文乱码。

在web.xml中配置spring-web提供的CharaterEncodingFilter过滤器

<!--定义解决中文乱码的过滤器CharacterEncodingFilter-->
<filter><filter-name>encodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><!--设置编码格式--><init-param><!--该过滤器需要设置一个encoding属性,用于设置编码格式--><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>

SpringMVC中的跳转

  • 控制层跳转到某个jsp页面

    • 在控制层中定义方法,这种方式跳转,属于请求转发

    • 如果要使用重定向跳转,在页面名之前添加"redirect:"

      @RequestMapping("/hello")
      public String hello(){//返回页面名称return "hello";//请求转发
      }@RequestMapping("/hello")
      public ModelAndView hello(ModelAndView mav){//设置页面名称mav.setViewName("hello");return mav;
      }
      
    • 在springmvc配置文件中

      <mvc:view-controller path="请求映射" view-name="页面名称"></mvc:view-controller>
      <!-- 访问项目根目录,跳转到welcome.jsp页面 -->
      <mvc:view-controller path="/" view-name="welcome"></mvc:view-controller>
      <!-- 这个标签使用时,会让@RequesMapping失效,如果要共存,添加以下标签 -->
      <!--来自于xmlns:mvc="http://www.springframework.org/schema/mvc" -->
      <mvc:annotation-driven></mvc:annotation-driven>
      
  • 控制层跳转到另一个控制层中的方法

    • 方法的返回值为"redirect/forward:另一个控制层中方法的映射名"

      @RequestMapping("/hello")
      public String hello(){return "redirect:hero";//使用重定向的方式,跳转到映射名为hero的控制层return "forward:hero"//使用请求转发的方式,跳转到映射名为hero的控制层
      }
      
  • jsp页面跳转另一个jsp页面

    • 当前项目中jsp页面都在WEB-INF下,无法直接访问,a标签同样如此,只能通过控制层跳转页面

    • 定义用于跳转页面控制层类

      package com.hqyj.springmvc.controller;import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.PathVariable;
      import org.springframework.web.bind.annotation.RequestMapping;/** 定义一个用于跳转指定页面或controller的控制层* */
      @Controller
      public class ToPageController {/*项目启动时或直接访问根目录,跳转到指定的controller*/@RequestMapping("/")public String toIndex() {return "redirect:/hero/queryAll";}/** 这个方法的作用:会将请求中第一个/后的单词截取出来命名为path赋值给参数page* 如 localhost:8080/web/hero,就会识别出hero,return "hero";* 就会跳转到 /WEB-INF/pages/hero.jsp页面* */@RequestMapping("/{path}")public String toPage(@PathVariable("path") String page) {return page;}
      }
      
    • 这时在页面中这样跳转

      <%--这个路径实际是/项目名/addHero,会截取addHero,跳转到/项目名/WEB-INF/pages/addHero.jsp--%>
      <a href="${pageContext.request.contextPath}/addHero">添加</a>
      

配置Spring+SpringMVC时用到的关键类

  • 在web.xml中配置Spring全局监听器

    • ContextLoaderListener

      <listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:application.xml</param-value>
      </context-param>
      
  • 在Springmvc配置文件中配置SpringMVC内部资源视图解析器

    • InternalResourceViewResolver

      <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/pages/"></property><property name="suffix" value=".jsp"></property>
      </bean>
      
  • 在web.xml中配置SpringMVC请求分发Servlet

    • DispatcherServlet

      <servlet><servlet-name>dispatcherServlet</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>dispatcherServlet</servlet-name><url-pattern>/</url-pattern>
      </servlet-mapping>
      
  • 在web.xml中配置字符编码过滤器

    • CharacterEncodingFilter

      <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>
      

ORM

Object Relational Mapping 对象关系映射

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

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

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

主流的ORM框架有Hibernate、JPAMyBatisMyBatisPlus

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

  • mysql****druid

  • spring-jdbc

  • jstl

4.配置Spring

  • 创建application.xml

  • 扫描项目根包

  • 配置web.xml

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

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

5.配置SpringMVC

  • 创建springmvc.xml

  • 扫描控制层所在包

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

  • 配置web.xml

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

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

6.配置MyBatis

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

  • 在application.xml中注入

  • 数据库连接池Druid:DruidDataSource

  • SQL会话工厂:SqlSessionFactoryBean

  • 映射扫描配置器:MapperScannerConfigurer

7.执行sql语句

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

  • 在resoureces下创建mapper目录创建sql映射文件xx.xml(官网模板),在其中定义sql语句

SSM项目中使用Ajax

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

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

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

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

拦截器

每次请求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 {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String requestURI = request.getRequestURI();//登录成功后,会在session中保存一个名为sysAdmin的对象Object sysAdmin = request.getSession().getAttribute("sysAdmin");//如果有对象,说明登录成功,可以放行return trueif(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>

动态SQL

  • <set>搭配<if>用于修改

    <!--动态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>
    
  • <where>搭配<if>用于查询、修改、删除时的条件

    <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>
    
  • <trim>搭配<if>可以替换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>

分页

使用分页组件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.isHasNextPage();
System.out.println("是否是首页"+pageInfo.isIsFirstPage());
System.out.println("是否是尾页"+pageInfo.isIsLastPage());
PageInfo对象常用属性和方法 作用
total/getTotal() 得到总记录数
pages/getPages() 得到最大页数
pageNum/getPageNum() 得到当前页
size/getSize() 得到每页显示的记录数
list/getList() 得到按当前page和size查询到的数据集合
isFirstPage/isIsFirstPage() 是否是首页
isLastPage/isIsLastPage() 是否是尾页

多条件分页具体实现

controller

@RequestMapping("/queryByCondition")
public String queryByCondition(@RequestParam(defaultValue = "1") int pageNum,@RequestParam(defaultValue = "8") int size,BookInfo bookInfo,Model model) {//1.PageHelper.startPage()PageHelper.startPage(pageNum, size);//2.查询集合List<BookInfo> list = bookInfoService.queryByCondition(bookInfo);//3.PageInfo(集合)PageInfo<BookInfo> pageInfo = new PageInfo<>(list);//将查询到的分页模型对象保存到model中model.addAttribute("pageInfo",pageInfo);//构造页数的集合ArrayList<Integer> pageList = new ArrayList<>();for (int i = 1; i <= pageInfo.getPages(); i++) {pageList.add(i);}model.addAttribute("pageList",pageList);return "bookInfoList";
}

页面分页组件

<nav aria-label="Page navigation"><ul class="pagination"><c:if test="${!pageInfo.isFirstPage}"><li><a href="${pageContext.request.contextPath}/bookInfo/queryByCondition?bookName=${param.bookName}&bookAuthor=${param.bookAuthor}&typeId=${param.typeId}&pageNum=${pageInfo.pageNum-1}"aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li></c:if><c:forEach items="${pageList}" var="pno"><li class="pno"><a href="${pageContext.request.contextPath}/bookInfo/queryByCondition?bookName=${param.bookName}&bookAuthor=${param.bookAuthor}&typeId=${param.typeId}&pageNum=${pno}">${pno}</a></li></c:forEach><c:if test="${!pageInfo.isLastPage}"><li><a href="${pageContext.request.contextPath}/bookInfo/queryByCondition?bookName=${param.bookName}&bookAuthor=${param.bookAuthor}&typeId=${param.typeId}&pageNum=${pageInfo.pageNum+1}"aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li></c:if></ul>
</nav>

SpringBoot

Spring推出的一个Spring框架的脚手架。

不是一个新的框架,而是搭建Spring相关内容框架的平台。

它省去了Spring、SpringMVC项目繁琐的配置过程,让开发变得更加简单。

本质还是Spring+SpringMVC,可以搭配其他的ORM框架,如MyBatis、MyBatisPlus、JPA、Hibernate等。

特点

  • 内置了Tomcat服务器,不需要部署项目到Tomcat中
  • 内置了数据源Hikari
  • 减少了jar文件依赖的配置
  • SpringBoot中的配置文件可以使用yml格式文件,代替properties或xml

热部署

项目在开发过程中,可以不需要每次都重启,等待一段时间后会自动更新编译运行

使用

添加依赖,可以在创建的项目的时候选择,也可以中途添加

<!--热部署-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><version>2.7.8</version>
</dependency>

开启热部署

Lombok

用于简化实体类中模板代码的工具

使用

添加依赖,可以在创建的项目的时候选择,也可以中途添加

<!--Lombok-->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version><scope>provided</scope>
</dependency>

安装插件(IDEA2020.2之后的版本会内置Lombok插件,无需安装)

在某个实体类上添加注解

lombok常用注解 作用
@AllArgsConstructor 自动生成全参构造方法
@Data 以下注解之和
@Setter 自动生成set方法
@Getter 自动生成get方法
@NoArgsConstructor 自动生成无参构造方法
@ToString 自动生成toString方法
@EqualsAndHashcode 自动生成equals和hashcode方法

SpringBoot+LayUI实现酒店客房管理

核心知识点

  • SpringBoot项目搭建

    • 核心依赖

      <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      
    • 集成MyBatis

      <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.0</version>
      </dependency>
      
  • 新注解

    • @RestController

      如果某个控制器类中的所有方法都要加@ResponseBody,可以在类上加@ResponseBody,也可以用@RestController代替@ResponseBody和@Controller

  • LayUI

    • 数据表格

      • 数据接口格式
      • 真假分页
      • 头工具栏事件
      • 行内事件
      • 单元格编辑事件
    • 弹出层
      • layer.msg()
      • layer.confirm()
      • layer.open()
  • ajax

    $.ajax({url:"",data:xxx,type:"get/post",success:function(){}
    })
    
  • 前后端分离

    • 该案例可以将页面独立出来,成为一个前后端分离项目,也可以将页面作为静态资源保存在static目录下
  • 如果设计为前后端分离,要在控制器类上加入@CrossOrigin,表示该类中的所有方法允许跨域请求

  • 打包SpringBoot项目

    保证项目中无错误,包含单元测试中

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b3C5jrUn-1676453638388)(C:\Users\hnn\Desktop\image-20230215165946364.png)]

​ 打包后是一个.jar文件,位于target目录中

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AiUQkZdA-1676453638389)(C:\Users\hnn\Desktop\image-20230215170020310.png)]

​ 在安装有java环境的机器中,控制台运行jar文件

java -jar 文件名.jar

核心Java代码

实体类

订单表

/*
* 订单
* */
@Data
public class Orders {private Integer id;private Integer roomNo;private Integer cusId;//@JsonFormat是springboot集成的jackson包中的注解,用于格式化日期//pattern指定格式日期 timezone指定时区,这里和数据库的时区保持一致@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "Asia/Shanghai")private Date leaveTime;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "Asia/Shanghai")private Date stayTime;private Integer cost;//显示订单的同时,要显示房间的相关信息private Room room;//显示订单的同时,要显示客户的相关信息private Customer customer;
}

数据访问层

  • 实现随意修改某个字段的值

    int update(@Param("field") String field,@Param("value") String value,@Param("id") int id);
    

    sql映射文件

    <update id="update">update room<set><if test="field=='roomType'">room_type= #{value}</if><if test="field=='roomPrice'">room_price= #{value}</if><if test="field=='roomUse'">room_use=#{value}</if></set>where room_no=#{id}
    </update>
    
  • 添加时获取自增的值

    int insert(Customer customer);
    

    sql映射文件

    <!--添加顾客的同时,获取自增的id-->
    <insert id="insert" useGeneratedKeys="true" keyColumn="cus_id" keyProperty="cusId">insert into customer values(null,#{cusName},#{cusPhone},#{cusIdcard})
    </insert>
    
  • 多表连接查询/条件查询

    List<Orders> queryAll();List<Orders> search(String keyword);//根据客户编号查询是否已入住
    Orders isStay(Integer cusId);//根据房间号查询正在入住的订单信息
    Orders findCheckInByRoomNo(Integer roomNo);//计算费用
    int checkOut(@Param("roomNo") Integer roomNo,@Param("cost") Integer cost);
    

    sql映射文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--设置该文件对应的dao层接口全限定名-->
    <mapper namespace="com.hqyj.hotel_sys.dao.OrdersDao"><insert id="insert">insert into ordersvalues (null, #{roomNo}, #{cusId}, now(), null, 0)</insert><!--关联查询方式一:特殊的SQL语句--><!--<select id="queryAll" resultType="com.hqyj.hotel_sys.entity.Orders">select o.*,cus_name as 'customer.cusName',cus_phone as 'customer.cusPhone',cus_idcard as 'customer.cusIdcard',room_type as 'room.roomType',room_price as 'room.roomPrice',room_use as 'room.roomUse'from customer c,room r,orders owhere c.cus_id = o.cus_idand r.room_no = o.room_no</select>--><!--多表连接条件查询--><select id="search" resultMap="ordersMap">SELECT * FROM orders o,customer c<trim prefix="where" prefixOverrides="and">o.cus_id = c.cus_id<if test="keyword!=null">and cus_name like concat('%',#{keyword},'%')</if></trim></select><!--关联查询方式二:子查询--><!--1.查询自身表--><select id="queryAll" resultMap="ordersMap">select *from orders</select><!--自定义结果集映射,用cus_id和room_no做子查询,重新映射一次到orders对象--><resultMap id="ordersMap" type="com.hqyj.hotel_sys.entity.Orders"><result property="roomNo" column="room_no"></result><result property="cusId" column="cus_id"></result><association property="room" column="room_no" select="findRoomByNo"></association><association property="customer" column="cus_id" select="findCustomerById"></association></resultMap><!--子查询一:根据房号查房间--><select id="findRoomByNo" resultType="com.hqyj.hotel_sys.entity.Room">select *from roomwhere room_no = #{roomNo}</select><!--子查询二:根据编号查客户--><select id="findCustomerById" resultType="com.hqyj.hotel_sys.entity.Customer">select *from customerwhere cus_id = #{cusId}</select><select id="isStay" resultType="com.hqyj.hotel_sys.entity.Orders">select *from orderswhere cus_id = #{cusId}and leave_time is null</select><!--根据房号查询正在入住的订单信息,包含房间价格--><select id="findCheckInByRoomNo" resultType="com.hqyj.hotel_sys.entity.Orders">select o.*, room_price as 'room.roomPrice'from orders o,room rwhere o.room_no = r.room_noand r.room_no = #{roomNo}and leave_time is null</select><update id="checkOut">update ordersset leave_time=now(),cost=#{cost}where room_no = #{roomNo}and leave_time is null</update>
    </mapper>
    

业务流程层

package com.hqyj.hotel_sys.service;import com.hqyj.hotel_sys.dao.CustomerDao;
import com.hqyj.hotel_sys.dao.OrdersDao;
import com.hqyj.hotel_sys.dao.RoomDao;
import com.hqyj.hotel_sys.entity.Customer;
import com.hqyj.hotel_sys.entity.Orders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.Date;
import java.util.List;@Service
public class CustomerService {@Autowiredprivate CustomerDao customerDao;@Autowiredprivate RoomDao roomDao;@Autowiredprivate OrdersDao ordersDao;/** 入住* 1.获取顾客信息:姓名、电话、身份证号,*   根据身份证号判断是否存在,如果存在,用存在的对象*   如果不存在,向customer表中添加一条记录 insert into customer values(null,#{cusName},#{cusPhone},#{cusIdcard})*   添加成功时,要获取自增的id,用于订单表中使用*   判断该顾客是否已入住:查询订单表中对应顾客编号的记录,如果退房时间为空,说明已入住*   select * from orders where cus_id=#{cusId} and leave_time is null* 2.将对应房间的状态改为1,对room表中修改 update room set room_use=1 where room_no=#{roomNo}* 3.向订单表中添加一条记录 insert into orders values (null,#{roomNo},#{cusId},now(),null,0)** 以上3个步骤属于一个事务,要在该方法上加事务注解* */@Transactional//让该方法成为一个事务,如果执行中途出错,会自动回滚public boolean checkIn(Customer customer, int roomNo) {//1.添加客户,根据身份证号判断是否存在Customer byIdcard = customerDao.findByIdcard(customer.getCusIdcard());boolean b1;if (byIdcard == null) {//不存在,调用添加b1 = customerDao.insert(customer) > 0;} else {customer = byIdcard;b1 = true;}//查看是否已入住if (ordersDao.isStay(customer.getCusId()) != null) {return false;}//2.修改房间状态boolean b2 = roomDao.update("roomUse", "1", roomNo) > 0;//3.添加订单信息//创建订单对象Orders orders = new Orders();//客户编号在添加客户成功后,会自动获取orders.setCusId(customer.getCusId());orders.setRoomNo(roomNo);boolean b3 = ordersDao.insert(orders) > 0;return b1 & b2 & b3;}/** 退房* 1.根据房间号查询对应正在入住的订单记录(同时查询出房价)* select o.* from orders o,room r where o.room_no=r.room_no and r.room_no =#{roomNo} and leave_time is null* 2.结算* 添加退房时间、添加花费* update orders set leave_time =now() ,cost=#{cost} where room_no =#{roomNo} and leave_time is null* 3.修改房间状态为空闲* update room set room_use = 0 where room_no=#{roomNo}* */@Transactionalpublic boolean checkOut(Integer roomNo) {//1.根据房间号查询对应正在入住的订单记录Orders orders = ordersDao.findCheckInByRoomNo(roomNo);//2.计算费用Date stayTime = orders.getStayTime();Date now = new Date();double l = now.getTime() - stayTime.getTime();//转换为天数double day = Math.ceil(l / 1000 / 3600 / 24);double cost = orders.getRoom().getRoomPrice() * day;//修改订单中的花费和退房时间//update orders set leave_time=now(),cost=#{cost} where room_no=#{roomNo} and leave_time is nullboolean b1 = ordersDao.checkOut(roomNo, (int) cost) > 0;//3.修改房间状态为空闲0//update room set room_use where room_no = #{roomNo}boolean b2 = roomDao.update("roomUse", "0", roomNo) > 0;return b1 & b2;}
}

控制层

如果某个控制器中的所有方法都需要返回JSON格式字符串,在类上加@ResponseBody

@Controller
@RequestMapping("/room")
public class RoomController {@Autowiredprivate RoomService roomService;@ResponseBody@RequestMapping("/queryAll")public ResultData<Room> queryAll(Integer page, Integer limit) {//使用PageHelper分页PageHelper.startPage(page, limit);//正常查询所有List<Room> rooms = roomService.queryAll();//创建分页模型对象PageInfo<Room> pageInfo = new PageInfo<>(rooms);//返回的数据为分页后的集合,数量为总记录数return new ResultData<>((int)pageInfo.getTotal(),pageInfo.getList());}
}

LayUI数据表格

数据表格所需的数据接口格式为

{code:0,msg:"",count:1000,data:[{},{}]
}

构造满足LayUI数据表格的数据模型类ResultData

/*
* 定义一个用于LayUI数据表格对应格式的模板类
* code  状态码         0成功
* msg   提示文字
* count 数据总量
* data  数据集合
* */
@Data
public class ResultData<T> {private Integer code;private String msg;private Integer count;private List<T> data;/** 定义带count和data的构造方法,用于初始化code和msg* */public ResultData(Integer count, List<T> data) {code=0;msg="";this.count = count;this.data = data;}
}

最后在控制层中,将查询的方法的返回值更改为ResultData类型

@RequestMapping("/queryAll")
public ResultData<Room> queryAll() {List<Room> rooms = roomService.queryAll();return new ResultData<>(rooms.size(), rooms);
}

最终页面

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>LayUI</title><meta name="renderer" content="webkit"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"><link rel="stylesheet" href="../LayUI/css/LayUI.css" media="all"><!-- 注意:如果你直接复制所有代码到本地,上述css路径需要改成你本地的 -->
</head>
<body>
<table class="LayUI-hide" id="test" lay-filter="test"></table>
<script type="text/html" id="toolbarDemo"><div class="LayUI-btn-container"><button class="LayUI-btn LayUI-btn-sm" lay-event="getCheckData">获取选中行数据</button><button class="LayUI-btn LayUI-btn-sm" lay-event="getCheckLength">获取选中数目</button><button class="LayUI-btn LayUI-btn-sm" lay-event="isAll">验证是否全选</button><button class="LayUI-btn LayUI-btn-sm" lay-event="addRoom">添加客房</button></div>
</script>
<script type="text/html" id="barDemo"><!--插值表达式-->{{# if(d.roomUse==0){ }}<a class="LayUI-btn LayUI-btn-normal LayUI-btn-sm" lay-event="check-in">入住</a><a class="LayUI-btn LayUI-btn-danger LayUI-btn-sm" lay-event="del">删除</a>{{# }else{ }}<a class="LayUI-btn LayUI-btn-warm LayUI-btn-sm" lay-event="check-out">退房</a>{{# } }}
</script><script src="../LayUI/LayUI.js" charset="utf-8"></script>
<!-- 注意:如果你直接复制所有代码到本地,上述 JS 路径需要改成你本地的 -->
<script>LayUI.use('table', function () {var table = LayUI.table;//引入Jqueryvar $ = LayUI.$;/** 渲染表格数据* url:数据访问接口* cols:表格列* field:数据字段名* title:表头* cellminwidth:100  自适应宽度* fixed:left    固定在某侧* unresize:true 不可改变尺寸* sort:true 排序* edit:true 行内编辑* */table.render({elem: '#test'//设置数据接口, url: 'http://localhost:9090/hotel/room/queryAll', toolbar: '#toolbarDemo' //开启头部工具栏,并为其绑定左侧模板, defaultToolbar: ['filter', 'exports', 'print', { //自定义头部工具栏右侧图标。如无需自定义,去除该参数即可title: '提示', layEvent: 'LAYTABLE_TIPS', icon: 'LayUI-icon-tips'}], title: '用户数据表', cols: [[{type: 'checkbox', fixed: 'left'}, {field: 'roomNo', title: '房间号', cellminwidth: 100, fixed: 'left', unresize: true, sort: true}, {field: 'roomType', title: '房间类型', cellminwidth: 100, edit: 'text'}, {field: 'roomPrice', title: '房间单价', cellminwidth: 100, edit: 'text', sort: true}, {field: 'roomUse', title: '使用状态', cellminwidth: 100,templet: function (res) {return res.roomUse == 0 ? "<span style='color:green'>空闲中</span>" : "<span style='color:red'>使用中</span>";}}, {fixed: 'right', title: '操作', toolbar: '#barDemo', cellminwidth: 100,}]]//开启分页组件, page: true//设置每页显示记录数的下拉选项, limits: [5, 10, 20]//默认每页显示的条数,没有设置默认为10, limit: 10//解析数据表格url请求后的数据, parseData: function (res) {//res就是url对应的数据/*//假分页var result;if (this.page.curr) {result = res.data.slice(this.limit * (this.page.curr - 1), this.limit * this.page.curr);} else {result = res.data.slice(0, this.limit);}*/return {"code": res.code,"msg": res.msg,"count": res.count,"data": res.data}}});//头工具栏事件table.on('toolbar(test)', function (obj) {var checkStatus = table.checkStatus(obj.config.id);switch (obj.event) {case 'addRoom':layer.open({title: '添加客房',type: 2,content: 'addRoom.html',area: ['350px', '250px'],resize: false,anim: 2,/* success: function(layero, index){console.log(layero, index);}*/})break;case 'getCheckData'://data是所选数据的集合var data = checkStatus.data;for (var i = 0; i < data.length; i++) {console.log(data[i].roomNo)}// layer.alert(JSON.stringify(data));break;case 'getCheckLength':var data = checkStatus.data;layer.msg('选中了:' + data.length + ' 个');break;case 'isAll':layer.msg(checkStatus.isAll ? '全选' : '未全选');break;//自定义头工具栏右侧图标 - 提示case 'LAYTABLE_TIPS':layer.alert('这是工具栏右侧自定义的一个图标按钮');break;};});//监听单元格编辑事件table.on('edit(test)', function (obj) {// console.log(obj);//layer.confirm("提示文件",function(){确认触发},function(){取消触发})layer.confirm('确认要修改吗', function (index) {//使用ajax提交要修改的字段名、修改后的值、要修改的编号$.ajax({url: "http://localhost:9090/hotel/room/update",data: {"field": obj.field,//要修改的字段"value": obj.value,//修改后的值"id": obj.data.roomNo//要修改的id},success: function () {//修改成功,关闭确认框layer.close(index);}});}, function () {//修改失败,重新加载location.reload()})})//监听行工具事件table.on('tool(test)', function (obj) {//data就是当前行中的数据var data = obj.data;// console.log(obj)if (obj.event === 'del') {//弹出确认框layer.confirm('真的删除行么', function (index) {//使用ajax提交要删除的id$.ajax({url: "http://localhost:9090/hotel/room/delete",data: {"delId": data.roomNo},success: function (res) {if (res) {//前端假删除obj.del();//关闭确认框layer.close(index);}}});});} else if (obj.event === 'check-in') {//弹出输入顾客信息表单layer.open({title: '输入顾客信息',type: 2,content: 'addCustomer.html',area: ['350px', '400px'],resize: false,anim: 2,//弹出窗口后的回调函数success: function (layero, index) {//弹出成功后,在弹出页面中加入当前点击行的roomNo//获取弹出层的body部分var body = layer.getChildFrame('body', index);//获取弹出层body中的隐藏域,给其赋值body.find("input[name=roomNo]").val(data.roomNo);}})} else if (obj.event === 'check-out') {layer.confirm("确定要退房吗", function () {$.ajax({url: "http://localhost:9090/hotel/customer/checkOut",data: {"roomNo": data.roomNo},success: function (res) {if (res) {location.reload();}}});})}});});
</script></body>
</html>

LayUI添加页面

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>Title</title><link rel="stylesheet" href="../LayUI/css/LayUI.css" media="all"></head><body><form class="LayUI-form" action=""><div class="LayUI-form-item"><label class="LayUI-form-label">房间类型</label><div class="LayUI-input-inline"><input type="text" name="roomType" required  lay-verify="required" placeholder="请输入房间类型" autocomplete="off" class="LayUI-input"></div></div><div class="LayUI-form-item"><label class="LayUI-form-label">房间单价</label><div class="LayUI-input-inline"><input type="text" name="roomPrice" required lay-verify="required" placeholder="请输入房间单价" autocomplete="off" class="LayUI-input"></div></div><div class="LayUI-form-item"><div class="LayUI-input-block"><button class="LayUI-btn" lay-submit lay-filter="formDemo">立即提交</button><button type="reset" class="LayUI-btn LayUI-btn-primary">重置</button></div></div></form><script src="../LayUI/LayUI.js" charset="utf-8"></script><script>//DemoLayUI.use('form', function(){var form = LayUI.form;//使用LayUI内置jqueryvar $=LayUI.$;//监听提交form.on('submit(formDemo)', function(data){// data.field是当前表单中的所有数据// layer.msg(JSON.stringify(data.field));$.ajax({url:'http://localhost:9090/hotel/room/addRoom',//将表单中的所有数据一起提交,实际提交的是name=value,如roomType=值&roomPrice=值data:data.field,//相当于/* data:{roomType:"",roomPrice:""}*/success:function(res){if(res){//刷新父页面parent.location.reload()}}})//return false时不提交表单return false;});});</script></body>
</html>

LayUI分页

  • 假分页

    查询所有,在页面中分页,适合记录比较少的情况

    table.render({//省略url等//开启分页组件, page: true//设置每页显示记录数的下拉选项, limits: [5, 10, 20]//默认每页显示的条数,没有设置默认为10, limit: 10//解析数据表格url请求后的数据, parseData: function (res) {//res就是url对应的数据//假分页var result;if (this.page.curr) {result = res.data.slice(this.limit * (this.page.curr - 1), this.limit * this.page.curr);} else {result = res.data.slice(0, this.limit);}return {"code": res.code,"msg": res.msg,"count": res.count,"data": result}}
    })
    
  • 真分页

    可以使用PageHelper组件

    依赖

    <!--分页组件SpringBoot集成PageHelper-->
    <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.6</version>
    </dependency>
    

    在application.properties中配置

    # 防止不合理分页
    pagehelper.reasonable=true
    

    在控制层中使用

    @ResponseBody
    @RequestMapping("/queryAll")
    //这里的page和limit参数是layui数据表格自动传递
    public ResultData<Room> queryAll(Integer page, Integer limit) {//使用PageHelper分页PageHelper.startPage(page, limit);//正常查询所有List<Room> rooms = roomService.queryAll();//创建分页模型对象PageInfo<Room> pageInfo = new PageInfo<>(rooms);//返回的数据为分页后的集合,数量为总记录数return new ResultData<>((int)pageInfo.getTotal(),pageInfo.getList());
    }
    

LayUI条件查询

页面头部工具栏中加入搜索框

<script type="text/html" id="toolbarDemo"><div class="layui-form-item"><div class="layui-input-inline"><input type="text" name="keyword" required lay-verify="required" placeholder="请输入姓名关键字" autocomplete="off"class="layui-input"></div><button class="layui-btn layui-btn-primary" lay-event="search">搜索</button></div>
</script>

js部分

//头工具栏事件
table.on('toolbar(test)', function (obj) {switch (obj.event) {case 'search'://获取输入的内容let keyword = $("input[name=keyword]").val();if(keyword==""){layer.msg("输入不能为空");return;}//如果要修改数据表格中的数据,只能更改url的地址后,重新加载数据表格table.reload('test',{url:"http://localhost:9090/hotel/orders/search?keyword="+keyword});break;}
});

dao层参考上方数据访问层条件查询部分

MyBatisPlus

官网简介 | MyBatis-Plus (baomidou.com)

MyBatis-Plus (简称 MP)是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

只需简单的配置,就能实现对单表的CURD。

其核心有两个接口:BaseMapper和IService

BaseMapper中封装了大量数据访问层的方法

IServcie中封装了大量业务流程层的方法

SpringBoot+MyBatisPlus

1.创建SpringBoot项目

创建时勾选依赖

  • devtools
  • lombok
  • spring-web
  • mysql-driver

2.导入SpringBoot集成MyBatisPlus依赖

<!-- SpringBoot集成MyBatisPlus -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version>
</dependency>

3.配置application.properties文件

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/gamedb?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root# 开启sql日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 无需加入开启驼峰命名映射,MyBatisPlus默认使用驼峰命名进行属性-字段映射
#mybatis-plus.configuration.map-underscore-to-camel-case=true

4.根据数据表创建实体类

实体类的属性名命名方式:

  • MyBatisPlus默认使用驼峰命名对字段和属性进行映射。如将字段stu_name对应的属性写为stuName
  • 如果字段名和属性名不一致,在属性名上加入**@TableField(value = “字段名”)**
  • 主键字段对应的属性,需要加入@TableId注解,其type属性表示主键生成策略
    • @TableId(type = IdType.AUTO)表示主键自增,在数据库也要将主键设置为自增
    • @TableId(type = IdType.ASSIGN_ID)//IdType.ASSIGN_ID表示使用"雪花算法"(根据时间和机器特征码)生成一个id
    • @TableId(type = IdType.ASSIGN_UUID)//IdType.ASSIGN_UUID表示使用UUID生成一个随机字符串id
@Data
public class Hero {//type表示主键生成策略,@TableId(type = IdType.AUTO)// IdType.AUTO表示主键自增,在数据库也要将主键设置为自增//@TableId(type = IdType.ASSIGN_ID)//IdType.ASSIGN_ID表示使用"雪花算法"(根据时间和机器特征码)生成一个id//@TableId(type = IdType.ASSIGN_UUID)//IdType.ASSIGN_UUID表示使用UUID生成一个随机字符串idprivate Integer id;//如果属性名和字段名不一致@TableField(value = "name")private String heroName;private String position;private String sex;private Integer price;private String shelfDate;
}

5.编写数据访问层接口

可以不用写@Repository,继承BaseMapper接口,设置泛型

/*
* 数据访问层可以称为dao或mapper层
* 可以不用加@Repository注解
* */
public interface HeroMapper extends BaseMapper<Hero> {}

6.在SpringBoot的启动类中,扫描数据访问层所在包

@SpringBootApplication
@MapperScan("com.hqyj.sbmp01.mapper")
public class Sbmp01Application {public static void main(String[] args) {SpringApplication.run(Sbmp01Application.class, args);}
}

测试

在SpringBoot自带的单元测试类中,注入HeroMapper对象,调用BaseMapper中定义的方法即可实现CURD。

BaseMapper接口

BaseMapper接口中定义了常用的增删改查方法,

在数据访问层接口中继承该接口

使用

public interface HeroMapper extends BaseMapper<Hero> {}

BaseMapper接口中的常用方法

方法名 参数 作用
selectList(Wrapper wrapper) 条件构造器 根据条件查询集合,如果实参为null表示查询所有,返回List集合
selectById(Serializable id) 主键 根据主键查询单个对象,返回单个对象
selectOne(Wrapper wrapper) 条件构造器 条件查询单个对象,返回单个对象
insert(T entity) 实体对象 添加单个实体
updateById(T entity) 实体对象 根据实体对象单个修改,对象必须至少有一个属性和主键
update(T entity,Wrapper wrapper) 实体对象和条件构造器 根据条件修改全部,对象必须至少有一个属性
deleteById(Serializable id/T entity) 主键/实体对象 根据主键删除单个对象
deleteBatchIds(Collection ids) 主键集合 根据集合删除
delete(Wrapper wrapper) 条件构造器 根据条件删除,如果实参为null表示无条件删除所有

测试

package com.hqyj.sbmp01;import com.hqyj.sbmp01.entity.Hero;
import com.hqyj.sbmp01.mapper.HeroMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;@SpringBootTest
class Sbmp01ApplicationTests {@Autowired//@Resourceprivate HeroMapper mapper;@Testvoid contextLoads() {//查询所有List<Hero> list = mapper.selectList(null);for (Hero hero : list) {System.out.println(hero);}}@Testvoid test1() {//根据主键查询单个对象System.out.println(mapper.selectById(20));}@Testvoid test2() {//根据id删除,参数为id//System.out.println(mapper.deleteById(20));//根据id删除,参数为带有id的对象/*Hero hero = new Hero();hero.setId(100);System.out.println(mapper.deleteById(hero));*///删除所有//mapper.delete(null);//根据id集合删除ArrayList<Integer> list = new ArrayList<>();list.add(99);list.add(100);list.add(88);list.add(69);System.out.println(mapper.deleteBatchIds(list));}@Testvoid test3() {//根据id修改//参数为一个对象,这个对象至少要有一个非主键属性有值Hero hero = new Hero();hero.setHeroName("a");hero.setPosition("b");hero.setSex("c");hero.setShelfDate("d");hero.setPrice(123);hero.setId(999);//System.out.println(mapper.updateById(hero));// 修改全部数据// mapper.update(修改后的对象,条件构造器)mapper.update(hero,null);}@Testvoid test4() {Hero hero = new Hero();hero.setHeroName("asdfsdfsdf");hero.setPosition("b");hero.setSex("c");hero.setShelfDate("1999-9-9");hero.setPrice(123);mapper.insert(hero);}
}

IService接口

IService接口减少业务逻辑层的代码,并对BaseMapper进行了拓展

在业务流程类中继承该接口

使用

  • 1.创建一个业务层接口,继承IService接口,设置泛型

    /*
    * 创建业务逻辑层接口,继承IService<T>接口,设置泛型
    * */
    public interface HeroService extends IService<Hero> {}
  • 2.创建一个业务层接口的实现类,添加@Service注解,继承 ServiceImpl<M, T>,实现上一步创建的接口

    • M是数据访问层接口
    • T是实体类
    /*
    * 1.添加@Service
    * 2.继承ServiceImpl<M, T> M是Mapper类 T是实体类
    * 3.实现自定义Service接口
    * */
    @Service
    public class ImpHeroService extends ServiceImpl<HeroMapper, Hero> implements HeroService {}
    

IService接口中的常用方法

方法 作用
list() 无条件查询所有
list(Wrapper wrapper) 条件查询素有
page(Page page) 无条件分页查询,Page是分页模型对象
page(Page page,Wrapper wrapper) 条件分页查询,Page是分页模型对象
getById(Serializable id) 根据主键查询单个对象
getOne(Wrapper wrapper) 条件查询单个对象
save(T entity) 添加单个对象
save(Collection col) 批量添加对象的集合
updateById(T entity) 修改,参数至少有一个属性值和主键
saveOrUpdate(T entity) 添加或修改。如果实参对象的主键值不存在则添加,存在则修改
update(T entity,Wrapper wrapper) 条件修改,条件为null则修改全部数据
removeById(Serializable id/T entity) 根据主键或包含主键的对象删除
removeBatchByIds(Collection ids) 根据集合删除
remove(Wrapper wrapper) 根据条件删除,条件为null则删除全部

测试

package com.hqyj.sbmp01;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hqyj.sbmp01.entity.Hero;
import com.hqyj.sbmp01.mapper.HeroMapper;
import com.hqyj.sbmp01.service.HeroService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;@SpringBootTest
class IServiceTest {@Autowiredprivate HeroService heroService;@Testvoid test1() {//无条件查询所有//heroService.list().forEach(System.out::println);//条件查询,如果条件为null表示查询所有//heroService.list(null).forEach(System.out::println);//根据主键查询//Hero hero = heroService.getById(22);//System.out.println(hero);//根据条件查询,如果条件为null表示查询所有,会导致异常//System.out.println(heroService.getOne(null));}@Testvoid test2() {//添加save(T entity);参数对象至少要有一个属性值//添加后,主键自增的值会自动赋值给主键属性/*Hero hero = new Hero();hero.setHeroName("cvb");hero.setSex("男");hero.setPrice(2000);hero.setPosition("战士");heroService.save(hero);System.out.println(hero);*///批量添加,参数为要添加的对象集合/* Hero h1 = new Hero(0, "1", "1", "1", 100, "1999-9-9");Hero h2 = new Hero(0, "2", "1", "1", 100, "1999-9-9");Hero h3 = new Hero(0, "3", "1", "1", 100, "1999-9-9");List<Hero> heroes = Arrays.asList(h1, h2, h3);heroService.saveBatch(heroes);*/Hero h1 = new Hero(0, "666", "1", "1", 100, "1999-9-9");//如果添加的对象主键值已存在执行修改,不存在则添加heroService.saveOrUpdate(h1);}@Testvoid test3(){//根据对象修改Hero h1 = new Hero(999, "修改后", "修改后", "女", 100, "1999-9-9");//如果对象主键值已存在执行修改,不存在则添加//heroService.saveOrUpdate(h1);//根据主键修改heroService.updateById(h1);//根据条件修改//heroService.update(null);//根据实体和条件修改,如果条件为空,会将所有数据修改为指定实体的数据//heroService.update(h1,null);}@Testvoid test4(){//根据主键或带有主键值的对象删除//heroService.removeById(1103302663);/*Hero hero = new Hero();hero.setId(1103302662);heroService.removeById(hero);*///批量删除//heroService.removeBatchByIds(Arrays.asList(77,88,99));//根据条件删除,条件为null则删除全部heroService.remove(null);}}

SpringBoot+MyBatisPlus

1.创建SpringBoot项目

创建时勾选依赖

  • devtools
  • lombok
  • spring-web
  • mysql-driver

2.导入SpringBoot集成MyBatisPlus依赖

<!-- SpringBoot集成MyBatisPlus -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version>
</dependency>

3.配置application.properties文件

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/gamedb?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root# 开启sql日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 无需加入开启驼峰命名映射,MyBatisPlus默认使用驼峰命名进行属性-字段映射
#mybatis-plus.configuration.map-underscore-to-camel-case=true

4.根据数据表创建实体类

实体类的属性名命名方式:

  • MyBatisPlus默认使用驼峰命名对字段和属性进行映射。如将字段stu_name对应的属性写为stuName
  • 如果字段名和属性名不一致,在属性名上加入**@TableField(value = “字段名”)**
  • 主键字段对应的属性,需要加入@TableId注解,其type属性表示主键生成策略
    • @TableId(type = IdType.AUTO)表示主键自增,在数据库也要将主键设置为自增
    • @TableId(type = IdType.ASSIGN_ID)//IdType.ASSIGN_ID表示使用"雪花算法"(根据时间和机器特征码)生成一个id
    • @TableId(type = IdType.ASSIGN_UUID)//IdType.ASSIGN_UUID表示使用UUID生成一个随机字符串id
@Data
public class Hero {//type表示主键生成策略,@TableId(type = IdType.AUTO)// IdType.AUTO表示主键自增,在数据库也要将主键设置为自增//@TableId(type = IdType.ASSIGN_ID)//IdType.ASSIGN_ID表示使用"雪花算法"(根据时间和机器特征码)生成一个id//@TableId(type = IdType.ASSIGN_UUID)//IdType.ASSIGN_UUID表示使用UUID生成一个随机字符串idprivate Integer id;//如果属性名和字段名不一致@TableField(value = "name")private String heroName;private String position;private String sex;private Integer price;private String shelfDate;
}

5.编写数据访问层接口

可以不用写@Repository,继承BaseMapper接口,设置泛型

/*
* 数据访问层可以称为dao或mapper层
* 可以不用加@Repository注解
* */
public interface HeroMapper extends BaseMapper<Hero> {}

6.在SpringBoot的启动类中,扫描数据访问层所在包

@SpringBootApplication
@MapperScan("com.hqyj.sbmp01.mapper")
public class Sbmp01Application {public static void main(String[] args) {SpringApplication.run(Sbmp01Application.class, args);}
}

测试

在SpringBoot自带的单元测试类中,注入HeroMapper对象,调用BaseMapper中定义的方法即可实现CURD。

BaseMapper接口

BaseMapper接口中定义了常用的增删改查方法,

在数据访问层接口中继承该接口

方法列表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k9NOzwWl-1676453638390)(C:\Users\hnn\AppData\Local\Temp\Temp1_框架.zip\SpringBoot+MyBatisPlus.assets\image-20230208172250774.png)]

使用

public interface HeroMapper extends BaseMapper<Hero> {}

BaseMapper接口中的常用方法

方法名 参数 作用
selectList(Wrapper wrapper) 条件构造器 根据条件查询集合,如果实参为null表示查询所有,返回List集合
selectById(Serializable id) 主键 根据主键查询单个对象,返回单个对象
selectOne(Wrapper wrapper) 条件构造器 条件查询单个对象,返回单个对象
insert(T entity) 实体对象 添加单个实体
updateById(T entity) 实体对象 根据实体对象单个修改,对象必须至少有一个属性和主键
update(T entity,Wrapper wrapper) 实体对象和条件构造器 根据条件修改全部,对象必须至少有一个属性
deleteById(Serializable id/T entity) 主键/实体对象 根据主键删除单个对象
deleteBatchIds(Collection ids) 主键集合 根据集合删除
delete(Wrapper wrapper) 条件构造器 根据条件删除,如果实参为null表示无条件删除所有

测试

package com.hqyj.sbmp01;import com.hqyj.sbmp01.entity.Hero;
import com.hqyj.sbmp01.mapper.HeroMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;@SpringBootTest
class Sbmp01ApplicationTests {@Autowired//@Resourceprivate HeroMapper mapper;@Testvoid contextLoads() {//查询所有List<Hero> list = mapper.selectList(null);for (Hero hero : list) {System.out.println(hero);}}@Testvoid test1() {//根据主键查询单个对象System.out.println(mapper.selectById(20));}@Testvoid test2() {//根据id删除,参数为id//System.out.println(mapper.deleteById(20));//根据id删除,参数为带有id的对象/*Hero hero = new Hero();hero.setId(100);System.out.println(mapper.deleteById(hero));*///删除所有//mapper.delete(null);//根据id集合删除ArrayList<Integer> list = new ArrayList<>();list.add(99);list.add(100);list.add(88);list.add(69);System.out.println(mapper.deleteBatchIds(list));}@Testvoid test3() {//根据id修改//参数为一个对象,这个对象至少要有一个非主键属性有值Hero hero = new Hero();hero.setHeroName("a");hero.setPosition("b");hero.setSex("c");hero.setShelfDate("d");hero.setPrice(123);hero.setId(999);//System.out.println(mapper.updateById(hero));// 修改全部数据// mapper.update(修改后的对象,条件构造器)mapper.update(hero,null);}@Testvoid test4() {Hero hero = new Hero();hero.setHeroName("asdfsdfsdf");hero.setPosition("b");hero.setSex("c");hero.setShelfDate("1999-9-9");hero.setPrice(123);mapper.insert(hero);}
}

IService接口

IService接口减少业务逻辑层的代码,并对BaseMapper进行了拓展

在业务流程类中继承该接口

部分方法列表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-715jNTGy-1676453638391)(C:\Users\hnn\AppData\Local\Temp\Temp1_框架.zip\SpringBoot+MyBatisPlus.assets\image-20230208172729669.png)]

使用

  • 1.创建一个业务层接口,继承IService接口,设置泛型

    /*
    * 创建业务逻辑层接口,继承IService<T>接口,设置泛型
    * */
    public interface HeroService extends IService<Hero> {}
  • 2.创建一个业务层接口的实现类,添加@Service注解,继承 ServiceImpl<M, T>,实现上一步创建的接口

    • M是数据访问层接口
    • T是实体类
    /*
    * 1.添加@Service
    * 2.继承ServiceImpl<M, T> M是Mapper类 T是实体类
    * 3.实现自定义Service接口
    * */
    @Service
    public class ImpHeroService extends ServiceImpl<HeroMapper, Hero> implements HeroService {}
    

IService接口中的常用方法

方法 作用
list() 无条件查询所有
list(Wrapper wrapper) 条件查询素有
page(Page page) 无条件分页查询,Page是分页模型对象
page(Page page,Wrapper wrapper) 条件分页查询,Page是分页模型对象
getById(Serializable id) 根据主键查询单个对象
getOne(Wrapper wrapper) 条件查询单个对象
save(T entity) 添加单个对象
save(Collection col) 批量添加对象的集合
updateById(T entity) 修改,参数至少有一个属性值和主键
saveOrUpdate(T entity) 添加或修改。如果实参对象的主键值不存在则添加,存在则修改
update(T entity,Wrapper wrapper) 条件修改,条件为null则修改全部数据
removeById(Serializable id/T entity) 根据主键或包含主键的对象删除
removeBatchByIds(Collection ids) 根据集合删除
remove(Wrapper wrapper) 根据条件删除,条件为null则删除全部

测试

package com.hqyj.sbmp01;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hqyj.sbmp01.entity.Hero;
import com.hqyj.sbmp01.mapper.HeroMapper;
import com.hqyj.sbmp01.service.HeroService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;@SpringBootTest
class IServiceTest {@Autowiredprivate HeroService heroService;@Testvoid test1() {//无条件查询所有//heroService.list().forEach(System.out::println);//条件查询,如果条件为null表示查询所有//heroService.list(null).forEach(System.out::println);//根据主键查询//Hero hero = heroService.getById(22);//System.out.println(hero);//根据条件查询,如果条件为null表示查询所有,会导致异常//System.out.println(heroService.getOne(null));}@Testvoid test2() {//添加save(T entity);参数对象至少要有一个属性值//添加后,主键自增的值会自动赋值给主键属性/*Hero hero = new Hero();hero.setHeroName("cvb");hero.setSex("男");hero.setPrice(2000);hero.setPosition("战士");heroService.save(hero);System.out.println(hero);*///批量添加,参数为要添加的对象集合/* Hero h1 = new Hero(0, "1", "1", "1", 100, "1999-9-9");Hero h2 = new Hero(0, "2", "1", "1", 100, "1999-9-9");Hero h3 = new Hero(0, "3", "1", "1", 100, "1999-9-9");List<Hero> heroes = Arrays.asList(h1, h2, h3);heroService.saveBatch(heroes);*/Hero h1 = new Hero(0, "666", "1", "1", 100, "1999-9-9");//如果添加的对象主键值已存在执行修改,不存在则添加heroService.saveOrUpdate(h1);}@Testvoid test3(){//根据对象修改Hero h1 = new Hero(999, "修改后", "修改后", "女", 100, "1999-9-9");//如果对象主键值已存在执行修改,不存在则添加//heroService.saveOrUpdate(h1);//根据主键修改heroService.updateById(h1);//根据条件修改//heroService.update(null);//根据实体和条件修改,如果条件为空,会将所有数据修改为指定实体的数据//heroService.update(h1,null);}@Testvoid test4(){//根据主键或带有主键值的对象删除//heroService.removeById(1103302663);/*Hero hero = new Hero();hero.setId(1103302662);heroService.removeById(hero);*///批量删除//heroService.removeBatchByIds(Arrays.asList(77,88,99));//根据条件删除,条件为null则删除全部heroService.remove(null);}}

条件构造器Wrapper

BaseMapper和IService接口中有很多方法都有这个参数,表示一个条件构造器对象。

如果该参数实际传递的值为null,表示没有任何条件,

这个Wrapper是一个抽象类,如果想要带条件,就要创建一个该类的子类对象。

常用子类为QueryWrapper和UpdateWrapper,

查询是创建QueryWrapper对象,更新时创建UpdateWrapper,实际使用无区别。

Wrapper对象带参数

Wrapper<T> wrapper =  new QueryWrapper(T entity);
QueryWrapper<T> wrapper =  new QueryWrapper(T entity);

Wrapper构造方法的参数如果是一个实体对象,只要该对象的属性不为空,就会将所有属性用and拼接起来作为条件。

这种适合已知某个字段为某个值时使用。

@Test
void test1() {//创建实体对象,只带两个属性值Hero hero = new Hero();hero.setHeroName("瑞兹");hero.setSex("女");//创建一个带实体参数的条件构造器对象Wrapper<Hero> wrapper = new QueryWrapper<>(hero);System.out.println(heroService.getOne(wrapper));
}

Wrapper对象不带参数

Wrapper<T> wrapper =  new QueryWrapper();
QueryWrapper<T> wrapper =  new QueryWrapper();

当条件构造器不带参数时,就需要自定义条件。

这种适用于自定义查询条件。

默认多个条件时,用and连接,如果条件之间要使用or,调用or()方法

分页查询

MyBatisPlus中集成了分页功能,只需在项目的启动类中注入一个分页拦截器对象

/*注入一个MyBatisPlus提供的分页拦截器对象定义一个方法,在方法上加入@Bean注解,将该方法的返回值注入到Spring容器中*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){//创建一个MybatisPlus拦截器对象MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//通过拦截器对象添加分页拦截器对象,设置数据库类型interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;
}
@Test
void test1(){//无条件分页查询page(Page pageInfo)//1.创建一个分页模型对象,设置泛型,参数为当前页和每页显示的记录数Page<Hero> pageInfo = new Page<>(1, 5);//2.调用IService中的page方法,参数为分页模型对象,返回值也是该对象pageInfo = heroService.page(pageInfo);//分页的相关信息都保存在分页模型对象pageInfo中System.out.println("总页数"+pageInfo.getPages());System.out.println("当前页"+pageInfo.getCurrent());System.out.println("每页显示的记录数"+pageInfo.getSize());System.out.println("总记录数"+pageInfo.getTotal());System.out.println("分页后的集合"+pageInfo.getRecords());System.out.println("是否有下一页"+pageInfo.hasNext());System.out.println("是否有上一页"+pageInfo.hasPrevious());//条件分页page(Page pageInfo,Wrapper wrapper)//分页查询所有法师pageInfo= heroService.page(pageInfo,new QueryWrapper<Hero>().eq("position","法师"));pageInfo.getRecords().forEach(System.out::println);
}

代码生成器

MyBatisPlus中用于自动生成entity、mapper、service、controller这些类和接口的工具

1.添加依赖

<!-- 代码生成器 -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.3</version>
</dependency>
<!--代码生成器所需引擎-->
<dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.3</version>
</dependency>

2.代码生成器类

修改以下几处

  • 数据库信息
  • 输出目录为本地硬盘目录
  • 父包名
  • 要生成的表名
package com.hqyj.question_sys.util;import com.baomidou.mybatisplus.generator.FastAutoGenerator;public class CodeGenerator {public static void main(String[] args) {//***数据库的url, 用户名和密码FastAutoGenerator.create("jdbc:mysql://localhost:3306/question_sys?serverTimezone=Asia/Shanghai","root", "root").globalConfig(builder -> {builder.author("HQYJ") // 设置作者,生成文件的注释里的作者.outputDir("F:\\框架\\question_sys\\src\\main\\java"); //***指定输出目录}).packageConfig(builder -> {builder.parent("com.hqyj.question_sys"); //***设置父包名// .controller("controller") //控制层包名// .service("service") //service接口包名// .serviceImpl("service.impl")  //service接口实现包名// .mapper("mapper")  //mapper接口包名// .xml("mapper.xml") //映射文件的包名(如果要生成的话,加上这句,去掉下面的.xml方法)// .entity("entity"); //实体类的包名}).strategyConfig(builder -> {builder.addInclude("question_catalog","question")  //***设置需要生成的表名.controllerBuilder();//只生成Controller//.enableRestStyle(); //生成的Controller类添加@RestController;}).templateConfig(builder -> {builder.xml(null); //禁止生成xml映射文件}).execute();}
}

运行该类即可自动生成

Thymeleaf

SpringBoot官方建议使用的页面模板引擎,代替之前的JSP。

它是以HTML为基础,可以展示静态数据,方便前端人员开发静态页面,

也可以通过Thymeleaf的语法,配合EL输出由控制器跳转而来的动态数据。

Thymeleaf从入门到精通 - 知乎 (zhihu.com)

用法

1.添加依赖

<!--thymeleaf页面模板引擎-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId><version>2.7.3</version>
</dependency>

2.在temlates目录下创建一个HTML页面

在页面的HTML标签中,加入xmlns:th="http://www.thymeleaf.org"属性。

可以通过修改IDEA中的HTML页面模板,之后每次创建HTML页面都会有该属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-24RHrwYp-1676453638392)(C:\Users\hnn\AppData\Local\Temp\Temp1_框架.zip\day14-SpringBoot+MyBatisPlus2.assets\image-20230209143658152.png)]

3.thymeleaf具体使用

  • 双标签中的输出变量: th:text

    <h1 th:text="${作用域中某个对象名}"></h1><h1 th:text="${username}"></h1>
    
  • 单标签中设置value的值:th:value

    <input type="text" th:value="${作用域中某个对象名}"><input type="text" th:value="${变量.属性名}">
    
  • 遍历:th:each

    <table><tr th:each="变量:${作用域中的集合名}"><td th:text="${变量.属性名}"></td></tr>
    </table><table><tr th:each="hero:${heroList}"><td th:text="${hero.id}"></td><td th:text="${hero.name}"></td><td th:text="${hero.sex}"></td></tr>
    </table>
    
  • 超链接获取全局路径:th:href=“@{/}”

    • 不带参数

      <link th:href="@{/bootstrap-3.4.1-dist/css/bootstrap.css}" rel="stylesheet">
      
    • 带参数

      <a th:href="@{'/hero/delete?id='+${hero.id}">删除</a><a th:href="@{/question/findById(id=${question.quesCode})}">编辑</a>
      
  • 表单提交路径:th:action=“@{/}”

    <form methos="post" th:action="@{/hero/insert}"></form>
    
  • 判断:th:if

    <h1 th:if="判断逻辑">用户信息</h1><h1 th:if="${userinfo.username==null}">请登录</h1>
    
  • 选中单选按钮或复选框:th:checked

    <input type="radio" value="男" name="sex" th:checked="逻辑判断"><input type="radio" value="男" name="sex" th:checked="${user.sex=='男'}">男
    <input type="radio" value="女" name="sex" th:checked="${user.sex eq '女'}">女
    
  • 选中下拉菜单:th:selected

    <select><option th:selected="逻辑判断"></option>
    </select><select><option th:selected="${book.typeId==bookType.typeId}" th:each="bookType:${list}" th:value="${bookType.typeId}" th:text="${bookType.typeName}"></option>
    </select>
    
  • src属性:th:src

    <script th:src="@{/bootstrap-3.4.1-dist/js/jquery-3.6.2.min.js}"></script>
    
  • 内联表达式

    Thymeleaf页面中使用[[]]可以在script标签中使用EL表达式,这两对中括号拼接的内容称为内联表达式。

    在给script标签加入**th:inline=“javascript”**后使用

    <script th:inline="javascript">$(function () {//遍历页码$(".pno").each(function (){//如果页码和当前页一致,高亮显示if($(this).text()==[[${pageInfo.current}]]){$(this).addClass("active");}});});
    </script><script th:inline="javascript">$(function () {//使用ajax读取所有的题目类型对象,遍历成option$.ajax({url:[[@{/qc/queryAll}]],success:function (res){for(var i=0;i<res.length;i++){let qcId = res[i].qcId;let qcName = res[i].qcName;$("<option></option>").text(qcName).val(qcId).appendTo($("select[name=qcId]"));}}});});
    </script>
    

Spring Data JPA

2001年推出了Hibernate,是一个全自动ORM框架。可以不用编写SQL语句,就能实现对数据库的持久化操作。

SUN公司在Hibernate的基础上,制定了JPA,全称 Java Persisitence API,中文名Java持久化API,

是一套Java访问数据库的规范,由一系列接口和抽象类构成。

后来Spring团队在SUN公司制定的JPA这套规范下,推出了Spring Data JPA,是JPA的具体实现。

如今常说的JPA,通常指Spring Data JPA。

SpringBoot集成Spring Data JPA

1.创建SpringBoot项目,选择依赖

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vaL6I0xk-1676453638392)(C:\Users\hnn\Desktop\image-20230215171457393.png)]

2.编辑配置文件,设置要连接的数据库信息

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/bookdb?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root# 设置数据库类型
spring.jpa.database=mysql
# 打印SQL语句
spring.jpa.show-sql=true

3.创建实体类

  • 类上加**@Entity**注解

  • 主键属性上加

    • @Id注解标明主键

    • **@GeneratedValue(strategy = GenerationType.IDENTITY)**设置MySQL数据库主键生成策略,数据库设置为自增

  • 其他属性名与字段名一致或驼峰命名法

    • 如果字段名多个单词之间用_,使用驼峰命名法
    • 如果不相同,使用**@Column(name=“字段名”)**注解指定该属性对应的字段名
@Data
@Entity
/*
* 实体类的属性名建议使用驼峰命名法
* */
public class BookInfo {@Id//主键字段//主键生成策略,GenerationType.IDENTITY表示MySQL自增@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer bookId;private Integer typeId;private String bookName;private String bookAuthor;//如果字段名和属性名不一致,使用@Column指定字段名@Column(name = "book_price")private Integer price;private Integer bookNum;private String publisher_date;
}

4.数据访问层接口

  • 类上加@Repository注解
  • 继承JpaRepository<实体类型,主键类型>接口
@Repository
public interface BookInfoDao extends JpaRepository<BookInfo,Integer> {}

5.测试常用方法

方法名 返回值 作用
findAll() List 查询所有数据。
save(T entity) T entity 添加或修改。如果不存在主键属性或主键值不存在,执行添加;如果存在主键属性且有该主键值,执行修改。
delete(T entity) void 根据对象删除。如果该对象中有主键属性且有该主键值,根据该主键值删除。
findById(主键) Optional 根据主键值查询。返回的对象调用isPresent()结果为true,表示查询到了数据,继续调用get()得到查询到的对象。
@SpringBootTest
class Day16SpringBootJpaApplicationTests {@Autowiredprivate BookInfoDao bookInfoDao;@Testvoid contextLoads() {List<BookInfo> all = bookInfoDao.findAll();all.forEach(System.out::println);}@Testvoid insert() {BookInfo bookInfo = new BookInfo();bookInfo.setBookName("测试");bookInfo.setBookAuthor("测试");bookInfo.setBookNum(156);bookInfo.setPrice(20);bookInfo.setTypeId(1);//save()方法调用时,如果对象中没有主键或主键值不存在,作为添加使用BookInfo save = bookInfoDao.save(bookInfo);//添加成功后,会自动获取主键自增的值System.out.println(save);}@Testvoid update() {BookInfo bookInfo = new BookInfo();bookInfo.setBookName("xxxxxxxxxxx");bookInfo.setBookAuthor("测试");bookInfo.setBookNum(356);bookInfo.setPrice(23);bookInfo.setTypeId(1);//save()方法调用时,如果对象中有主键且存在,作为修改使用BookInfo save = bookInfoDao.save(bookInfo);//修改成功后,返回修改后的对象System.out.println(save);}@Testvoid delete() {//根据主键值删除,如果值不存在,会报错//bookInfoDao.deleteById(36);//根据对象删除,如果对象中包含主键值则删除,如果没有值或不存在,不会报错BookInfo bookInfo = new BookInfo();//bookInfo.setBookId(330);bookInfoDao.delete(bookInfo);}@Testvoid findOne() {//根据主键查询,返回值Optional类型Optional<BookInfo> byId = bookInfoDao.findById(60);//isPresent()如果为true表示查询到了数据if (byId.isPresent()) {//get()将查询到的数据转换为对应的实体类BookInfo bookInfo=byId.get();System.out.println(bookInfo);}else{System.out.println("未查询到数据");}}
}

JPA进阶

分页查询

调用数据访问层中的**findAll(Pageable pageable)**方法,即可实现分页。

参数Pageable是org.springframework.data.domain包中的一个接口,通过其实现类

PageRequest,调用静态方法of(int page,int size),当做Pageable对象使用。

这里的page从0开始为第一页。

@Test
void queryByPage(){//PageRequest是Pageable的实现类,调用静态方法of(int page,int size)//这里的page的值0表示第一页//调用findAll(Pageable pageable)方法,返回分页模型对象Page<BookInfo> pageInfo = bookInfoDao.findAll(PageRequest.of(0,5));//分页相关数据System.out.println("总记录数"+pageInfo.getTotalElements());System.out.println("最大页数"+pageInfo.getTotalPages());System.out.println("分页后的数据集合"+pageInfo.getContent());System.out.println("当前页数"+pageInfo.getNumber());System.out.println("每页显示的记录数"+pageInfo.getSize());System.out.println("是否还有下一页"+pageInfo.hasNext());System.out.println("是否还有上一页"+pageInfo.hasPrevious());
}

条件查询

在JPA中,使用自定义方法名自动生成对应的SQL语句,实现条件查询。

如在dao中定义了queryById(int id)方法,就表示根据id查询,自动生成sql语句。

方法命名格式

[xxx] [By] [字段对应的属性名] [规则] [Or/And] [字段对应的属性名] [规则] …

  • **xxx可以是find、get、query、search
  • 方法如果有参数,参数的顺序和方法名中的参数顺序一致

如findByBookNameAndBookAuthor(String bookName,String bookAuthor),

对应的sql语句为 select * from book where book_name =? and book_author=?

常用规则

规则 方法名 SQL中的条件
指定值 findByBookName(String name) book_name = name
Or/And findByBookNameOrBookAuthor(String name,String author) book_name = name or book_author = author
After/Befor findByBookPriceAfter(double price) book_price > price
GreaterThanEqual/LessThanEqual findByBookNumLessThanEqual(int num) book_num <= num
Between findByBookNumBetween(int min,int max) book_num between min and max
Is[Not]Null findByPublisherDateIsNull() publish_date is null
[Not]Like findByBookNameLike(String condition) book_name like ‘condition’
[Not]Contains findByBookNameContains(String keyword) book_name like ‘%keyword%’
StartsWith/EndsWith findByBookNameStartsWith(String firstName) book_name like ‘firstName%’
无条件排序:findAllByOrderBy字段[Desc/Asc] findAllByOrderByBookId() order by book_id asc
有条件排序:findAllBy条件OrderBy字段[Desc/Asc] findAllByTypeIdOrderByBookIdDesc() type_id = ? order by book_id desc
@Repository
public interface BookInfoDao extends JpaRepository<BookInfo,Integer> {//指定值查询//根据书名查询List<BookInfo> getAllByBookName(String x);//查询价格大于指定值   字段对应的属性名  After/GreaterThanList<BookInfo> findAllByPriceAfter(int price);//查询价格小于于指定值    字段对应的属性名  Before/LessThanList<BookInfo> findAllByPriceLessThan(int price);//查询库存大于等于指定值 GreaterThanEqualList<BookInfo> queryAllByBookNumGreaterThanEqual(int num);//查询库存在指定闭区间内 Between(int min,int max)List<BookInfo> findAllByBookNumBetween(int min,int max);//空值查询 null//查询出版日期为空  IsNull/IsNotNullList<BookInfo> findAllByPublisherDateIsNull();//书名中带有关键字 Like/NotLike 实参一定要使用%或_List<BookInfo> getAllByBookNameLike(String keyword);//作者名中带有关键字  Contains/NotContains 实参只需要关键字List<BookInfo> getAllByBookAuthorContains(String keyword);//指定作者的姓   指定开头/结尾  StartsWith/EndsWithList<BookInfo> getAllByBookAuthorStartsWith(String keyword);//查询所有数据,按价格降序    无条件排序 OrderBy字段[Desc/Asc]List<BookInfo> getAllByOrderByPriceDesc();//查询指定类型,按id降序List<BookInfo> getAllByTypeIdOrderByBookIdDesc(Integer typeId);}

条件分页查询

只需在定义方法时,将方法的返回值设置为Page类型,在参数中就如Pageable对象

//根据作者和书名的关键字分页查询
Page<BookInfo> getAllByBookNameContainsOrBookAuthorContains(String nameKeyword,String authorKeyword,Pageable pageable);
Page<BookInfo> pageInfo = bookInfoDao.getAllByBookNameContainsOrBookAuthorContains("龙","山",PageRequest.of(0,5));
//分页相关数据保存在pageInfo对象中

前后端分离项目

前后端分离,就是将web应用中的前端页面和后端代码分开完成、部署。

  • 前后端的开发者只需要完成各自的事情,最终以文档的形式约定数据接口(URL、参数、返回值、请求方式)
  • 前后端分别用独立的服务器
  • 后端只需处理数据并提供访问接口(路径),以RESTFul风格的JSON格式传输数据
  • 前端只需负责渲染页面和展示数据

传统项目和前后端分离项目对比

传统项目

前端和后端的代码运行在一个服务器上,页面经由控制器跳转

前后端分离项目

前后端的代码分别运行在各自的服务器上

后端提供JSON格式字符串的数据接口

前端负责跳转、解析JSON数据。

前后端分离项目后端控制层设计

请求方式设计:RESTFul风格

风格,不是标准,可以不用强制遵循。

RESTFul风格:用不同的请求方式去访问同一个URL地址时,执行不同的操作。

特点

  • 通过URL就能知道当前在哪个模块
  • 通过不同的请求方式决定执行什么操作
  • 通过返回的状态码得到操作结果

使用RESTFul风格和普通方式对比

普通方式

localhost:8080/user/queryAll                         查询所有
localhost:8080/user/queryById?id=1001                  条件查询
localhost:8080/user/insert?name=ez&sex=男&age=20          添加
localhost:8080/user/update?name=ez&id=1001                修改
localhost:8080/user/delete?id=1001                     删除

RESTFul风格

localhost:8080/user                          查询所有get请求
localhost:8080/user/1001                    条件查询get请求
localhost:8080/user                         添加post请求
localhost:8080/user                         修改put请求
localhost:8080/user/1001                    删除delete请求

RESTFul风格具体使用

  • 在请求映射的命名上,统一用小写字母的名词形式表示当前位于哪个模块。如/user、/book_info

  • 访问时如果要传参,使用"/模块名/参数"方式,配合controller中的@PathVariable获取

    @GetMapping("/book/{id}")
    public BookInfo queryById(@PathVariable("id")Integer id){return service.findById(id);
    }
    
  • 在controller的方法上,使用@XXXMapping()设置访问该方法的请求方式

    • @GetMapping(“路径”) 查询
    • @PostMapping(“路径”) 添加
    • **@PutMapping(“路径”) ** 修改
    • **@DeleteMapping(“路径”) ** 删除
    • @RequestMapping(value=“路径”,method=RequestMethod.GET/POST/PUT/DELETE))
  • 如果请求方式不匹配,会报405异常

  • 在同一个controller中,不能出现两个请求方式和路径都一致的方法

返回值设计

前后端分离项目的控制层方法的返回值也需要进行统一。

返回值通常包含以下信息

  • 传递状态,用状态码表示Integer code
  • 传递消息,用字符串表示String msg
  • 传递集合,用集合表示List list
  • 传递对象,用对象表示Object obj

将这些信息封装到一个对象中,这个对象称为返回结果类RestResult对象

RestResult类具体设计

package com.hqyj.day16springbootjpa.util;import lombok.Data;import java.util.List;/*
* 定义控制层返回数据的模板类
* 包含
* 状态码
* 消息文字
* 集合
* 对象
*
* */
@Data
public class RestResult<T> {//状态码  0表示成功,其他表示各种情况private Integer code;//消息文字private String msg;//集合private List<T> list;//对象private Object obj;//创建构造方法,方便创建指定参数的对象/** 适用于返回集合* 如查询集合* */public RestResult(Integer code, String msg, List<T> list) {this.code = code;this.msg = msg;this.list = list;}/** 适用于返回对象* 如查询单个、添加、修改* */public RestResult(Integer code, String msg,  Object obj) {this.code = code;this.msg = msg;this.obj = obj;}/** 适用于无需返回对象时* 如删除* */public RestResult(Integer code, String msg) {this.code = code;this.msg = msg;}//用于返回集合操作成功时调用public  static<T> RestResult ok(String msg,List<T> list){return  new RestResult(0,msg,list);}//用于返回对象操作成功时调用public  static<T> RestResult ok(String msg,Object obj){return  new RestResult(0,msg,obj);}//用于操作成功时调用public  static<T> RestResult ok(String msg){return  new RestResult(0,msg);}//用于异常情况public  static<T> RestResult error(Integer code, String msg){return  new RestResult(code,msg);}}

Java技术栈

服务器端

​ JavaSE(API、OOP)、JavaEE(Servlet、Spring、SpringMVC)

​ tomcat

前端

​ HTML+CSS+JS+JQUERY、Bootstrap、LayUI、EasyUI、VUE

数据库

​ MySQL、Oracle、Redis

​ JDBC、SpringJDBC、MyBatis、MyBatisPlus、JPA

工具

​ IDEA、HBuilder、VSCode、Notepad++、Sublime、Navicat、Postman

后端只需处理数据并提供访问接口(路径),以RESTFul风格的JSON格式传输数据

  • 前端只需负责渲染页面和展示数据

传统项目和前后端分离项目对比

传统项目

前端和后端的代码运行在一个服务器上,页面经由控制器跳转

前后端分离项目

前后端的代码分别运行在各自的服务器上

后端提供JSON格式字符串的数据接口

前端负责跳转、解析JSON数据。

前后端分离项目后端控制层设计

请求方式设计:RESTFul风格

风格,不是标准,可以不用强制遵循。

RESTFul风格:用不同的请求方式去访问同一个URL地址时,执行不同的操作。

特点

  • 通过URL就能知道当前在哪个模块
  • 通过不同的请求方式决定执行什么操作
  • 通过返回的状态码得到操作结果

使用RESTFul风格和普通方式对比

普通方式

localhost:8080/user/queryAll                         查询所有
localhost:8080/user/queryById?id=1001                  条件查询
localhost:8080/user/insert?name=ez&sex=男&age=20          添加
localhost:8080/user/update?name=ez&id=1001                修改
localhost:8080/user/delete?id=1001                     删除

RESTFul风格

localhost:8080/user                          查询所有get请求
localhost:8080/user/1001                    条件查询get请求
localhost:8080/user                         添加post请求
localhost:8080/user                         修改put请求
localhost:8080/user/1001                    删除delete请求

RESTFul风格具体使用

  • 在请求映射的命名上,统一用小写字母的名词形式表示当前位于哪个模块。如/user、/book_info

  • 访问时如果要传参,使用"/模块名/参数"方式,配合controller中的@PathVariable获取

    @GetMapping("/book/{id}")
    public BookInfo queryById(@PathVariable("id")Integer id){return service.findById(id);
    }
    
  • 在controller的方法上,使用@XXXMapping()设置访问该方法的请求方式

    • @GetMapping(“路径”) 查询
    • @PostMapping(“路径”) 添加
    • **@PutMapping(“路径”) ** 修改
    • **@DeleteMapping(“路径”) ** 删除
    • @RequestMapping(value=“路径”,method=RequestMethod.GET/POST/PUT/DELETE))
  • 如果请求方式不匹配,会报405异常

  • 在同一个controller中,不能出现两个请求方式和路径都一致的方法

返回值设计

前后端分离项目的控制层方法的返回值也需要进行统一。

返回值通常包含以下信息

  • 传递状态,用状态码表示Integer code
  • 传递消息,用字符串表示String msg
  • 传递集合,用集合表示List list
  • 传递对象,用对象表示Object obj

将这些信息封装到一个对象中,这个对象称为返回结果类RestResult对象

RestResult类具体设计

package com.hqyj.day16springbootjpa.util;import lombok.Data;import java.util.List;/*
* 定义控制层返回数据的模板类
* 包含
* 状态码
* 消息文字
* 集合
* 对象
*
* */
@Data
public class RestResult<T> {//状态码  0表示成功,其他表示各种情况private Integer code;//消息文字private String msg;//集合private List<T> list;//对象private Object obj;//创建构造方法,方便创建指定参数的对象/** 适用于返回集合* 如查询集合* */public RestResult(Integer code, String msg, List<T> list) {this.code = code;this.msg = msg;this.list = list;}/** 适用于返回对象* 如查询单个、添加、修改* */public RestResult(Integer code, String msg,  Object obj) {this.code = code;this.msg = msg;this.obj = obj;}/** 适用于无需返回对象时* 如删除* */public RestResult(Integer code, String msg) {this.code = code;this.msg = msg;}//用于返回集合操作成功时调用public  static<T> RestResult ok(String msg,List<T> list){return  new RestResult(0,msg,list);}//用于返回对象操作成功时调用public  static<T> RestResult ok(String msg,Object obj){return  new RestResult(0,msg,obj);}//用于操作成功时调用public  static<T> RestResult ok(String msg){return  new RestResult(0,msg);}//用于异常情况public  static<T> RestResult error(Integer code, String msg){return  new RestResult(code,msg);}}

Java技术栈

服务器端

​ JavaSE(API、OOP)、JavaEE(Servlet、Spring、SpringMVC)

​ tomcat

前端

​ HTML+CSS+JS+JQUERY、Bootstrap、LayUI、EasyUI、VUE

数据库

​ MySQL、Oracle、Redis

​ JDBC、SpringJDBC、MyBatis、MyBatisPlus、JPA

工具

​ IDEA、HBuilder、VSCode、Notepad++、Sublime、Navicat、Postman

华清远见-框架阶段技术总结相关推荐

  1. 2016华清远见年度劲爆事件盘点 一起来围观哦!

    春去秋来冬又至,2016年的华清远见带着"洪荒之力"一路狂奔,取得了令华清人骄傲的成绩.3大业务的强势崛起.2大分中心的正式入驻.2本重磅图书的出版.高校师资班的成功举办以及多个荣 ...

  2. 华清远见-重庆中心-框架阶段技术总结/知识点梳理

    文章目录 华清远见-重庆中心-框架阶段技术总结/知识点梳理/个人总结 框架 Java主流框架 Spring 概念 组成 名词解释 IOC DI Spring控制台应用 1.创建一个普通的Maven项目 ...

  3. 华清远见-重庆中心-前端阶段技术总结

    华清远见-重庆中心-前端阶段技术总结 HTML Hyper Text Markup Language 超文本标记语言 超文本:超级文本/超链接文本,超越了文本的限制,如多媒体文件.超链接等. 标记:也 ...

  4. 华清远见-重庆中心-JAVA基础阶段技术总结

    系列文章目录 第一章 华清远见--重庆中心-JAVA基础阶段技术总结 第二章 文章目录 系列文章目录 文章目录 前言 一.关于java 1.发展历程 2.编程开发 3.java架构 4.java的特点 ...

  5. 华清远见-重庆中心-JavaWeb后端阶段技术总结

    华清远见-重庆中心-JavaWeb后端阶段技术总结 JavaWeb 使用Java开发Web服务的技术,统称为JavaWeb. B/S与C/S模式 B/S:Browser/Server 浏览器/服务器模 ...

  6. 华清远见-重庆中心-JAVA面向对象阶段技术总结

    华清远见-重庆中心-JAVA面向对象阶段技术总结 面向对象和面向过程的编程思想 面向对象(OOP) 通过创建(new)对象,赋予对象对应的行为和特征,让这些对象相互配合来解决问题 面向过程(POP) ...

  7. 阶段总结:华清远见毕业总结

    在毕业之际,苦于自身水平不足,不够完善的技能不足以在就业形势严峻的今天找到一份适合的工作,由于我是一个电子信息工程专业出身的学生,在学校里通过专业选修等课程接触到了单片机和嵌入式等课程,从而产生了一点 ...

  8. 华清远见嵌入式开发学习的6大阶段

    分享一下华清远见嵌入式开发学习的6大阶段.19年嵌入式培训经验,值得一看. 第一阶段:嵌入式系统开发基础理论 阶段目标 掌握Linux操作系统的使用,精通使用C语言编程,熟练掌握嵌入式Linux的开发 ...

  9. 华清远见—重庆中心——JAVA高级阶段知识点梳理

    华清远见-重庆中心--JAVA高级阶段知识点梳理 String字符串 String是一个类,属于数据类型中的引用类型.Java中所有使用""引起来的内容都是属于这个类的实例,称为字 ...

最新文章

  1. 刚发布!2020年AI人才发展报告,这三个暗示程序员一定要知道!
  2. 生信分析流程构建的几大流派
  3. 微软表示Edge的性能更优于Chrome和Firefox
  4. mini2440系统引导(四)存储控制器
  5. 20以内究竟包不包括20?
  6. PageObjects 设计模式
  7. Thread.Join()方法的理解
  8. 更改Mysql5.7的默认编码为utf8解决database为latin1无法修改问题
  9. [转载] Python-科赫雪花(科克曲线)
  10. Access数据库学习
  11. 计算机专业毕业设计题目大全
  12. Linux-虚拟机使用:真机与虚拟机ping通
  13. qq 病毒 html,盗qq号软件里有病毒吗
  14. 小学信息技术 用计算机画画 教学目标,小学信息技术教学计划
  15. 2015年动漫电影观影指南值得收藏
  16. Minecraft一些红石技巧(1)
  17. macOS 使用 X11 运行远端 linux 中的 x11 client 图形程序
  18. java与C++中文字符的问题
  19. 人工智能机器人被任命为业务领导团队
  20. 关于zebra中thread的解析

热门文章

  1. 计算机专业的考研英语作文,2021考研英语作文范文:学校钱买书还是电脑
  2. 潮汐是如何实现fofa的banner指纹识别的
  3. CSS float 浮动属性介绍
  4. 流量三国杀:抖、快、拼的生死战
  5. Plan Stitch:一种使用缝合物理计划解决查询计划性能退化问题的方法
  6. matplotlib部件(widgets)之范围选区(SpanSelector)
  7. 使用kali下的msf对windows 2003进行测试
  8. 使命召唤mod工具下载地址
  9. WEB3.0定义与未来发展趋势
  10. 基于SSH的宾馆管理系统系统结构图_ER图_数据字典