0%

参考资料

1、常用快捷键

操作 快捷键
插入环境 Ctrl+E
插入行内公式 Ctrl+Shift+M
注释 / 取消注释 Ctrl+T
查看 PDF F7
预览行内数学公式 Alt +P
从 PDF 跳转至对应的 Tex 在 PDF 中按 Ctrl 单击

2、设置中文界面

01-Texstudio设置中文界面.png

3、设置编译器与编码

为了正常的输出中文,我们需要把编译器改成 XeLaTeX ,utf-8 编码(默认)

4、显示代码行号

02-Texstudio显示行号.png

5、括号匹配高亮

TexStudio 默认的括号匹配背景色好像是黄色的,有些看不清,不如设置个更亮一点的颜色吧

03-括号匹配高亮1.png

04-括号匹配高亮2.png

卸载前端准备

  1. MySQL 软件安装到哪儿
    01-MySQL8 的安装位置.png

  2. MySQL 的数据库的数据都在哪儿
    02-MySQL8存放数据的位置.png

  3. 右键计算机⇒管理⇒服务和应用程序

找到服务 MySQL80

  1. 右键电脑⇒属性⇒高级系统设置⇒环境变量,在 path 项下

03-MySQL8的环境变量.png

卸载步骤

  1. 停止 MySQL 服务
  2. 通过控制面板卸载程序的方式删除

04-卸载两个程序.png

以上截图中的两个都需要卸载

  1. 删除环境变量

  2. 服务已经自动删除,无需我们在手动删除

  3. 重启电脑

安装 MySQL 5

下载地址:MySQL :: Download MySQL Installer (Archived Versions)

下载 5.7.35 版本的 MSI Installer 安装包
05-选择安装版本以及安装包格式.png

1、选择自定义的方式
06-自定义安装.png

2、选择需要安装的内容
07-需要安装的内容.png

3、进行高级设置
08-高级设定.png

4、配置软件路径及数据库存放位置
09-配置安装路径及数据库数据路径.png

5、端口号什么的不用修改,默认 3306 即可
6、设置账户密码

配置环境变量

将软件安装目录下: bin 文件夹的整个路径复制到系统环境变量的 path 中即可

bean 基础配置

01-Bean的配置信息一览图.png

bean 别名配置

1、设置别名

问 1:那么不同的人编写代码,可能给 bean 不同的命名,如何解决命名问题呢?
答 1:可以通过给 bean 起别名的方式

1
2
3
<bean id="bookService" name="service,service2,bookEbi" class="com.itheima.service.impl.BookServiceImpl">  
<property name="bookDao" ref="bookDao"/>
</bean>

name 属性值可以起多个别名,别名间以逗号、分号或空格来分割,比如如上代码的 name="service,service2,bookEbi"

2、在到运行类中修改一下获取 bean 的 id 就可以了

1
2
BookService bookService = (BookService) ctx.getBean("service");  
bookService.save();

除此之外,在 DI 配置属性的时候,也支持 name 属性,但是我们还是推荐通过 id 来进行引用
02-可通过ID或者Name属性来进行引用.png

bean 作用范围

Bean 的单例和多例

问 1:Spring 默认给我们创建的 bean 是 单例 的;那如果我想要造一个非单例的 bean 怎么办?
03-创建的Bean默认为单例的.png

答 1:通过配置的形式,在 bean 标签中再插入一个 scope 属性(默认为 singleton)

1
<bean id="bookDao" name="dao" scope="prototype" class="com.itheima.dao.impl.BookDaoImpl"/>

问 2:那么为什么 bean 默认为单例呢?
答 2:单例的 bean 如果能够复用的话,那么下次需要使用直接去容器中拿就好了,而不是每用一次就造一个对象

问 3:那么那些 bean 适合造单例呢?
答 3:适合交给容器进行管理的 bean,包括表现层对象、业务层对象、数据层对象以及工具对象等

问 4:那么哪些 bean 不适合交给容器进行管理呢?
答 4:封装实体的域对象(如记录有成员变量的)

bean 实例化

bean 本质上就是对象,创建 bean 使用构造方法完成

构造方法(常用)

1、提供可访问的构造方法

构造方法不写也行,Spring 会为我们处理;如果我们手动写上的话,注意一定是无参的构造方法。即如果我们创建了含参的构造方法,但是没有提供无参的构造方法,将抛出异常 BeanCreationException

1
2
3
4
5
6
7
8
9
10
public class BookDaoImpl implements BookDao {  
//无参的构造方法
public BookDaoImpl() {
System.out.println("book dao constructor is running");
}

public void save() {
System.out.println("book dao save ...");
}
}

2、配置 bean

1
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>

静态工厂

通过工厂的方式造对象(而不是使用 new)也是一种解耦的方式,那么通过工厂造出来的对象如何交给 Spring 进行管理呢?

1、静态工厂创建对象

1
2
3
4
5
6
7
//静态工厂创建对象  
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
System.out.println("factory setup....");
return new OrderDaoImpl();
}
}

2、配置

1
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>

3、运行

1
2
OrderDao orderDao = OrderDaoFactory.getOrderDao();  
orderDao.save();

也就是说:

  1. 需要在配置文件中的 class 属性中指明工厂的路径
  2. 通过 factory-method 属性指明工厂中造对象的方法

实例工厂(了解)

1、实例工厂创建对象

1
2
3
4
5
6
//实例工厂创建对象  
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}

2、配置

1
2
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
  1. 先造工厂的 bean,对应着第一行代码
  2. factory-bean 指的是这个工厂的实例,也就是第一行代码中的 userFactory
  3. factory-method 指明工厂中造对象的方法

3、运行

1
2
3
4
5
//创建实例工厂对象;  
UserDaoFactory userDaoFactory = new UserDaoFactory();
//通过实例工厂对象创建对象
UserDao userDao = userDaoFactory.getUserDao();
userDao.save();

bean 生命周期

通过配置文件

1、在配置文件中绑定类的初始化和销毁前方法

1
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>

2、在类(BookDaoImpl)中创建对应的方法即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BookDaoImpl implements BookDao {  
public void save() {
System.out.println("book dao save ...");
}
//表示bean初始化对应的操作
public void init(){
System.out.println("init...");
}
//表示bean销毁前对应的操作
public void destory(){
System.out.println("destory...");
}

}

需要注意的是:当容器关闭时才会执行销毁前方法

bean 的生命周期

  • 初始化容器
    • 创建对象(内存分配)
    • 执行构造方法
    • 执行属性注入(set 操作)
    • 执行 bean 初始化方法
  • 使用 bean
    • 执行业务操作
  • 关闭 / 销毁容器
    • 执行 bean 销毁方法

附注

遇到 Spring 报错信息的解决办法

  1. 找到最后一条报错信息,Caused By:哒哒哒
  2. 如果最下面的能解决就 ok,不能解决就继续看上一条报错信息
  3. 因为第一条信息是包含全部的报错信息(很长看不到重点)

Element 是什么

将页面变得美美的呀😀

Element 是饿了么公司前端开发团队提供的一套基于 Vue 的网站组件库,用于快速构建网页
组件:组件网页的部件,例如:超链接、按钮、图片、表格等等

官网:Element - 网站快速成型工具

Element 快速入门

  1. 引入 Element 的 css、js 文件和 Vue.js
  2. 创建 Vue 核心对象
  3. 官网复制 Element 组件代码

1、将整个 element-ui 文件夹拷贝至 webapp 文件夹下

因为包含了一整套组件,element-ui 包含了大量文件(差不多 8M 了)

2、新建 HTML 页面,并引入 css、js 文件

1
2
3
4
5
6
7
8
9
<script src="js/vue.js"></script>
<!--通过下载好的element-ui文件-->
<script src="element-ui/lib/index.js"></script>
<link rel="stylesheet" href="element-ui/lib/theme-chalk/index.css">
<!--或者-->
<!--
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
-->

3、创建 Vue 核心对象 (搭建框架)

将代码写到 div 标签中即可;若需要引入 CSS 样式,可将其复制到 </head> 之上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>  
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>


</head>
<body>

<div id="app">

</div>

<script src="js/vue.js"></script>
<script src="element-ui/lib/index.js"></script>
<link rel="stylesheet" href="element-ui/lib/theme-chalk/index.css">

<script>
new Vue({
el:"app",

})
</script>

</body>
</html>

4、去官网上找组件的代码

比如去官网上找着这个按钮,看起来还不错,复制它的代码到 div 标签中即可

1
<el-button type="danger">危险按钮</el-button>

5、小案例的完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>

<div id="app">
<el-button type="danger">危险按钮</el-button>
</div>

<script src="js/vue.js"></script>
<script src="element-ui/lib/index.js"></script>
<link rel="stylesheet" href="element-ui/lib/theme-chalk/index.css">

<script>
new Vue({
el:"app"
})
</script>

</body>
</html>

Element 布局

Element 中有两种布局方式:Layout 布局和 Container 布局容器

Layout 布局

通过基础的 24 分栏,迅速简便地创建布局

官方案例:基础布局 | Element,直接复制 CSS 和 HTML 标签到我们的页面当中即可

01-从实例代码中复制HTML和CSS实现布局的引入.png

Container 容器布局

用于布局的容器组件,方便快速搭建页面的基本结构

官网示例:Container 布局容器 | Element

同样也是复制 css 和 html。需要注意到是,如果布局中包含数据(比如含有表格等),那么我们只要将 data 复制到我们创建的 vue 对象中即可

官方给出代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
export default {
data() {
const item = {
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
};
return {
tableData: Array(20).fill(item)
}
}
};
</script>

将 以上代码中的 data 复制到我们创建的 HTML 页面中的 vue 对象中去;同理,如果实例代码中有 methods 等属性的话,也一并复制到新建的 Vue 对象中去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>  
new Vue({
el:"app",
data() {
const item = {
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
};
return {
tableData: Array(20).fill(item)
}
}
})
</script>

Element 组件

参考官方示例:组件 | Element

参考资料

  1. Mermaid 官方文档:mermaid - Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.
  2. Github:Mermaid 中文说明文档
  3. Mermaid 在线编辑 Github:mermaid-live-editor
  4. VScode 插件:Markdown Preview Mermaid Support
  5. VScode 插件:Mermaid Markdown Syntax Highlighting

推荐阅读

  1. 个人博客:Mermaid 流程图 - Sherwood 的博客

流程图快速入门

案例 1:常规流程图

1
2
3
4
5
6
graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[Car]
1
2
3
4
5
6
graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[Car]
  • 第一行:TD(Top down)自上而下;
    除此之外,还有上 (Top) 下 (Bottom) 左 (Left) 右 (Right),可以两两组合来确定流程图的方向

  • 第二行:中括号表示图形中的矩形,圆括号为圆角矩形,两竖线之间的表示箭头上的文字
    除此之外,还有各种箭头和图形样式,这里不再列举,详细请查看官方文档和 Mermaid 流程图 - Sherwood 的博客

案例 2:带有子图的流程图

子图的基本语法为

1
2
3
subgraph title
graph definition
end

完整案例如下

1
2
3
4
5
6
7
8
9
10
11
flowchart RL
b1-->|"username = &quot;zhangsan&quot;"|servlet1
servlet1-->|data|b1
    subgraph Browsers
    b1
    b2
    end
    subgraph servers
    servlet1
    servlet2
    end
1
2
3
4
5
6
7
8
9
10
11
flowchart RL
b1-->|"username = &quot;zhangsan&quot;"|servlet1
servlet1-->|data|b1
    subgraph Browsers
    b1
    b2
    end
    subgraph servers
    servlet1
    servlet2
    end

特别注意:两竖线中的内容(也就是箭头上的文字)不能包含双引号,在 Mermaid 中算是特殊符号,请查阅 Mermaid 流程图 - Sherwood 的博客,需要额外进行处理。&quot; 来表示双引号。所以,在箭头上还是尽量少用双引号吧😀

以下方式均会导致绘图出错

1
2
3
b1-->|1.username = '"zhangsan"'|servlet1
b1-->'|1.username = "zhangsan"|'servlet1
b1-->|'1.username = "zhangsan"'|servlet1

正确的处理方式

1
b1-->|"username = &quot;zhangsan&quot;"|servlet1

基础表达形式

直线结构

1
A[Christmas] -->|Get money| B(Go shopping)

![[Pasted image 20240327083121.png]]

多分支结构

1
2
3
4
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[Car]

![[Pasted image 20240327083210.png]]

多合一结构

1
2
3
A --> C
B --> C
D --> C

![[Pasted image 20240327083404.png]]

需求

比如说一个任务流程图,需要同时确认了 A 和 B,才能执行 C,那么流程图该怎么表达

参考案例 1:

1
2
3
4
5
6
graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[Car]

参考案例 2:

1
2
3
4
graph TD
A[First node] --> C
B --> C
D --> C

根据案例 2,如何给 A,B 添加样式,以区别显示呢:

1
2
3
4
5
6
7
8
graph TD
style A fill:#9f9,stroke:#333,stroke-width:2px;
style B fill:red;
style D fill:cyan,stroke:dark gray;
style C fill:yellow;
A --> C
B --> C
D --> C

AJAX 简介

AJAX(Asynchronous JavaScirpt And XML):异步 的 JavaScript 和 XML

同步和异步的比较如图所示
03-同步和异步的比较.png

AJAX 作用:

  1. 与服务器进行数据交换:通过 AJAX 可以给服务器发送请求,并获取服务器响应的数据
    1. 使用 AJAX 和服务器进行通信,就可以使用 HTML+AJAX 来替换 JSP 页面了
    2. 使用它的一个重要原因是可以用来替换 JSP 页面;JSP 做不到前后端分离
  2. 异步交互:可以在 不重新加载整个页面 的情况下,与服务器交换数据并 更新部分 网页端技术,如:搜索联想,用户名是否可用校验等等

之前的做法:JSP
01-响应请求JSP做法.png

现在的做法:AJAX
02-响应请求AJAX做法.png

AJAX 快速入门

  1. 编写 AjaxServlet,并使用 response 输出字符串(后端代码)
  2. 创建 XMLHttpRequest 对象:损失和服务器交换数据(前端代码)
  3. 想服务器发送请求(前端代码)
  4. 获取服务器响应数据(前端代码)

一、编写 AjaxServlet,并使用 response 输出字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@WebServlet("/ajaxServlet")  
public class AjaxServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

//1.响应数据
response.getWriter().write("hello AJAX");
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}

二、创建核心对象

1
2
3
4
5
6
7
var xhttp;  
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
} else {
// code for IE6, IE5
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}

代码可参考 W3C:AJAX - XMLHttpRequest 对象

三、发送请求

1
2
xhttp.open("GET", "http://localhost:8080/ajax-dmeo/ajaxServlet");
xhttp.send();

注意:这里的路径是全路径,因为后期项目的前端和后端需要部署在不同的服务器上,需要使用绝对路径而不是相对路径

代码可参考 W3C:AJAX - XMLHttpRequest

四、获取响应

1
2
3
4
5
xhttp.onreadystatechange = function() {  
if (this.readyState == 4 && this.status == 200) {
alert(this.responseText)
}
};

代码参考 W3C:AJAX - 服务器响应

AJAX 完整案例

注意:还需要我们在服务器端创建一个用于响应浏览器的 Servlet,如以上步骤一所示(一、编写 AjaxServlet,并使用 response 输出字符串)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>  
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>

<script>
//1.创建核心对象
var xhttp;
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
} else {
// code for IE6, IE5
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}

//2.发送请求
xhttp.open("GET", "http://localhost:8080/ajax-dmeo/ajaxServlet");
xhttp.send();

//3.获取响应
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
alert(this.responseText)
}
};
</script>

</body>
</html>

什么是 Filter

  • Filter 表示过滤器,是 JavaWeb 三大组件 (Servlet、Filter、Listener) 之一
  • 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能
  • 过滤器一般完成一些通用的操作,比如:权限控制、统一编码处理、敏感字符处理等

02-Filter过滤器的执行流程.png

比如需要登录我这个网站,才能访问我的 web 资源,而如果每个 web 资源都需要进行登录判断的话,那干脆将这一层提取出来放到 Filter 中来实现是否登录判断

Filter 快速入门

  1. 定义类,实现 Filter 接口,并重写其所有方法
  2. 配置 Filter 拦截资源的路径:在类上定义 @WebFilter 注解
  3. 在 doFilter 方法中输出一句话,并放行

PS:

  1. 和 Servlet 的开发非常相似
  2. Filter 是 web 的三大组件之一,项目结构中一般将其放在 web 文件夹下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.itheima.web.filter;  

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter("/*")
public class FilterDemo implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("FilterDemo...");
//放行
chain.doFilter(request,response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {

}
@Override
public void destroy() {

}
}

注:@WebFilter("/*") 表示拦截所有资源

这里容易出现导错包的问题,如下所示:

1
2
3
4
5
6
7
package com.itheima.web.filter;  

//import java.util.logging.Filter; //错误的包
import javax.servlet.Filter;

public class FilterDemo implements Filter {
}

Filter 执行流程

02-Filter过滤器的执行流程.png

  1. 放行后访问对象资源,资源访问完成后,还会回到 Filter 中吗?会
  2. 如果回到 Filter 中,是重新执行还是执行放行后的逻辑呢?放行后的逻辑

执行放行前逻辑 ⇒ 放行 ⇒ 访问资源 ⇒ 执行放行后逻辑

1
2
3
4
5
6
7
8
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {  
//放行前逻辑;对request数据进行处理,response中还没有数据
System.out.println("FilterDemo...");
//放行
chain.doFilter(request,response);//访问到资源,response中有数据了

//放行后逻辑:对response数据进行处理
}

Filter 使用细节

Filter 拦截路径配置

  • 拦截具体的资源:/index.jsp:只有访问 index.jps 时才会被拦截
  • 目录拦截:/user/*
  • 后缀名拦截:/*.jsp
  • 拦截所有:/*

过滤器链

一个 Web 应用,可以配置多个过滤器,这多个过滤器称为过滤器链

03-Filter过滤器链.png

注解配置的 Filter,优先级按照过滤器类名(字符串)的自然排序。比如 FilterDemo1 就排在 FilterDemo2 的前面

mybatis-config.xml

使用说明

  1. 注意修改别名
  2. 注意修改数据库信息中的 URL
  3. 注意驱动对应的 MySQL 版本为 8
  4. 注意修改 SQL 的映射文件名
  5. 文件存放在 resource 资源文件夹下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?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>
<!--起别名-->
<typeAliases>
<package name="com.itheima.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库的连接信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///db1?useSSL=false&amp;useServerPrepStmts=true"/>
<property name="username" value="root"/>
<property name="password" value="wwww"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--包扫描-->
<package name="com.itheima.mapper"/>
</mappers>
</configuration>

UserMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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:名称空间,需要链接到UserMapper接口
-->

<mapper namespace="com.itheima.mapper.UserMapper">
<!--以下开始填写SQL语句-->
<select id="selectAll" resultType="Brand">
select * from tb_user;
</select>

</mapper>

logback.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--
CONSOLE :表示当前的日志信息是可以输出到控制台的。
-->
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%level] %blue(%d{HH:mm:ss.SSS}) %cyan([%thread]) %boldGreen(%logger{15}) - %msg %n</pattern>
</encoder>
</appender>

<logger name="com.itheima" level="DEBUG" additivity="false">
<appender-ref ref="Console"/>
</logger>


<!--

level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF
, 默认debug
<root>可以包含零个或多个<appender-ref>元素,标识这个输出位置将会被本日志级别控制。
-->
<root level="DEBUG">
<appender-ref ref="Console"/>
</root>
</configuration>

常用依赖

使用说明

  1. 内容添加到 pom.xml 文件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!--加载mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!--junit单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>

<!-- 添加slf4j日志api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.20</version>
</dependency>
<!-- 添加logback-classic依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 添加logback-core依赖 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>

<!--防止运行Tomcat报错-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>14</java.version>
<maven.compiler.source>14</maven.compiler.source>
<maven.compiler.target>14</maven.compiler.target>
</properties>

<!--Tomcat的插件-->
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!--<port>80</port>-->
<!--<path>/</path>-->
</configuration>
</plugin>
</plugins>
</build>

JSP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--JSP-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<scope>provided</scope>
</dependency>

<!--JSTL标签-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>

使用 JSTL 标签时,需要在 jsp 文件中引入 JSTL 标签库

1
2
<%@ page contentType="text/html;charset=UTF-8" language="java" %>  
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

SqlSessionFactoryUtil.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.itheima.util;  

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class SqlSessionFactoryUtils {

private static SqlSessionFactory sqlSessionFactory;

static {
//静态代码块会随着类的加载而自动执行,且只执行一次

try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}


public static SqlSessionFactory getSqlSessionFactory(){
return sqlSessionFactory;
}
}

Servlet 简介

Servlet 是 Java 提供的一门动态 web 资源开发技术
01-Servelet功能示意图.png

Servlet 是 JavaEE 规范之一,其实就是一个 接口,将来我们需要定义 Servlet 类实现 Servlet 接口,并在 web 服务器运行 Servlet。Servlet 接口 API 文档中的五个方法,实现这个接口的话需要实现这五个方法

02-Servlet需要重写的5个方法.png

Servlet 入门案例

  1. 创建 Web 项目,导入 Servlet 依赖坐标

pom.xml 文件中,插入如下代码

1
2
3
4
5
6
<dependency>  
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>

注意:需要加上 scope,设置为 provided

  1. 创建:定义一个类,实现 Servlet 接口,并重写接口中的所有方法,并在 service 方法中输入一句话

根据提示(Alt+Enter),重写类中的五个方法,这里我们重点关注 service 方法;因为 serlvet 在被访问的时候,service 方法被自动执行。

1
2
3
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {  
System.out.println("servlet hello world");
}
  1. 配置:在类上使用 @WebServlet 注解,配置该 Servlet 的访问路径
1
2
3
@WebServlet("/demo1")  
public class ServletDemo1 implements Servlet {
}
  1. 访问:启动 Tomcat,浏览器输入 URL 访问该 Servlet

Servlet 的执行流程

我们并没有创建 Servlet 对象,也没有调用 service 方法,那么程序是如何执行的呢?
Servlet 对象是由服务器 Tomcat 创建的,并且 service 方法也是 Tomcat 调用的

03-Servlet的执行流程.png

服务器怎么知道 Servlet 中一定有 service 方法呢?
因为实现 Sevlet 接口必须复写其方法,而 Servlet 接口中有 service 方法

Sevlet 生命周期

那 Tomcat 什么时候给我们创建的 servlet 对象呢?

Servlet 运行在 Servlet 容器(Web 服务器)中,其生命周期由容器来管理,分为四个阶段

  1. 加载和实例化:默认情况下,当 Servlet 第一次被访问时,由容器创建 Servlet 对象
  2. 初始化:在 Servlet 实例化之后,容器将调用 Servlet 的 init () 方法初始化这个对象,完成一些如加载配置文件、创建连接等初始化的工作。该方法只调用一次
  3. 请求处理:每次请求 Servlet 时,Servlet 容器都会调用 Servlet 的 service () 方法对请求进行处理
  4. 服务终止:当需要释放内存或者容器关闭时,容器就会调用 Servlet 实例的 destroy () 方法完成资源的释放。在 destroy () 方法调用之后,容器会释放这个 Servlet 实例,该实例随后会被 Java 的垃圾收集器所回收

初始化方法

1
2
3
4
5
6
//初始化方法  
//1.调用时机:默认情况下,servlet被第一次访问时调用
//2.调用次数:1次
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("服务器正在初始化");
}

注:也可以通过配置手动改变 Servlet 的创建时机

  • 负整数:第一次被访问时创建 Servlet 对象;默认值是 -1
  • 0 或正整数:服务器启动时创建 Servlet 对象,数字越小优先级越高;创建的时机提前了
1
@WebServlet(urlPatterns = "/demo",loadOnStartup = 1)

服务方法

1
2
3
4
5
//1.调用时机:每一次servlet被访问时调用  
//2.调用次数:每一次
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("服务器正在服务中");
}

销毁方法

1
2
3
4
5
//1.调用时机:内存释放或服务器关闭,servlet对象被销毁  
//2.调用次数:1次
public void destroy() {
System.out.println("服务器已被关闭");
}

Servlet 体系结构

我们常用的就是 service 方法,其他四个方法都用的少,但是每次都还是得去实现它们,如何才能更简化一些呢?

04-HttpServlet.png

我们将来开发 B/S 架构的 Web 项目,都是针对 HTTP 协议,所以我们自定义 Servlet,直接继承 HttpServlet 即可(用于简化开发)⇒ 复写 HttpServlet 中的方法即可(doPost 方法和 doGet 方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
@WebServlet("/demo4")  
public class ServletDemo4 extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get...");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post...");
}
}

在项目的 webapp 文件夹下新建一个 html 页面,用表单来模拟 post 请求

1
2
3
<form action="/web-demo/demo4" method="post">  
<input type="text" name="username"><input type="submit">
</form>

注意:form 标签中,action 属性值为虚拟目录的路径

HttpServlet 中为什么要根据请求方式的不同,调用不同的方法呢?

实现 Servlet 接口,重写那五个方法,并且在 service 方法中用来处理逻辑(获取请求参数信息),但是不同的请求方式,请求参数所在的位置不同,post 请求请求参数在请求体中,而 get 请求请求方式在请求行中。因此,应该写不同的处理逻辑代码,根据请求方式的不同进行分别的处理 ⇒ 因此,出现了 HttpServlet

准备环境

  • 为了开发更加方便推荐安装 MyBatisX 插件
  • 数据库:准备数据库表 tb_brand
  • pom.xml 文件:已导入 MaBatis 和 MySQL 坐标
  • 已配置好 mybatis-config.xml 配置文件
    • 设置好了 typeAliases 标签
    • 设置好了数据库的连接信息
  • 实体类:在项目下新建 pojo 软件包,创建 Brand 实体类
  • 测试用类:将主程序写在 test 文件夹的项目包下

测试用类及项目结构如下图所示
01-测试用类及项目结构.png

MyBatisX 插件

MyBatisX 是一款基于 IDEA 的快速开发插件,为效率而生

插件的主要功能有:

  1. XML 和接口方法相互跳转
  2. 根据接口方法生成 statement

02-安装MaBatisX插件.png

安装完重启 IDEA,相应的 xml 文件图标就变成小鸟的图标了
红色的小鸟:SQL 的映射文件
蓝色的小鸟:Mapper 的接口

03-蓝色小鸟与红色小鸟.png

查询所有数据

1、编写接口方法:Mapper 接口

  • 参数:无
  • 返回类型:List<Brand>
1
List<Brand> selectAll();

2、编写 Mapper 配置文件

1
2
3
4
<!--查询标签,id为这条SQL语句的唯一标识,resultType为返回结果类型,不区分大小写-->
<select id="selectAll" resultType="brand">
select * from tb_brand;
</select>

04-利用MaBatisx插件自动在配置文件中生成对应标签.png

3、执行方法,测试

MaBatis 完成操作只需要三步

  1. 编写接口方法
  2. 编写 SQL
  3. 执行方法

查询结果中部分字段显示为 null

查询出来的结果中,部分字段显示 NULL,为什么呢?

05-部分字段显示为null.png

因为这些字段,原本在数据库中比如说 brand_name,到了 Java 当中变量名就变成了 brandName(在 POJO 的实体类),数据库表的字段名称和实体类的属性名称不一样,所有就不会为我们自动封装了

解决办法 1

在 SQL 查询语句中为相应的字段设置别名,让别名和实体类的属性名一致即可

原来的 SQL 查询

1
2
3
<select id="selectAll" resultType="brand">  
select * from tb_brand;
</select>

修改后的 SQL 查询

1
2
3
4
<select id="selectAll" resultType="brand">  
select id,brand_name brandName,company_name companyName,ordered,status
from tb_brand
</select>

但是这样每进行一次查询都需要重新设置一次,而且不能复用

解决办法 2

使用 resultMap 标签(最为常用)

06-ResultMap实现字段名和类的属性名一一对应.png

1
2
3
4
5
6
7
8
9
10
11
12
<resultMap id="brandResultMap" type="brand">
<!--id标签完成主键的映射,result标签完成非主键的映射-->
<!--<id></id>-->
<result column="brand_name" property="brandName"></result>
<result column="company_name" property="companyName"></result>
</resultMap>

<select id="selectAll" resultMap="brandResultMap">
select
*
from tb_brand
</select>

resultMap 标签中:

  • id:唯一标识
  • type:映射的类型,支持别名
  • id 子标签:完成主键字段的映射,具有属性 column 表的列名和 property 实体类的属性,以上代码中并没有演示 id 子标签的使用
  • result 子标签:完成一半字段的映射,具有属性 column 表的列名和 property 实体类的属性

注意:select 标签中原先的 resultType="brand" 改为了 resultMap="brandResultMap",因为在 resultMap 标签中 type 属性也定义了类型

根据 id 查询数据

1、编写接口方法:Mapper 接口

  • 参数:id
  • 返回类型:Brand 类
1
2
//传入一个id,最终返回一个Brand类型
Brand selectById(int id);

2、编写 SQL 语句

1
2
3
4
5
<select id="selectById" resultMap="brandResultMap" >
select *
from tb_brand
where id = #{id};
</select>

3、执行方法,测试

只需要修改执行方法这一部分代码,其他地方不需要改动

1
2
3
4
//4.执行方法  
int id = 1;
Brand brand = brandMapper.selectById(id); //返回一个brand对象
System.out.println(brand);

条件查询 - 多条件查询

1、编写接口方法:Mapper 接口

  • 参数:所有查询条件
  • 返回类型:List<Brand>

2、编写 SQL 语句

1
2
3
4
5
6
7
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
where status = #{status}
and company_name like #{companyName}
and brand_name like #{brandName};
</select>

注意:以上代码中需输入三个参数才能查出结果,如果某一个参数没有填写就查询不出来,因此需要 SQL 语句动态变化(SQL 语句会随着用户的输入或外部条件的变化而变化)

解决方案 1:在 where 后面添加一个恒等式,然后所有的判断条件语句中都可以加上 and 这个关键词

1
2
3
4
5
6
7
8
9
10
11
12
select *  
from tb_brand
where 1 = 1
<if test="status!=null">
and status = #{status}
</if>
<if test="companyName!=null and companyName!='' ">
and company_name like #{companyName}
</if>
<if test="brandName!=null and brandName!='' ">
and brand_name like #{brandName}
</if>

解决方案 2:利用 MaBatis 提供的 where 标签

这样的话,就不需要管到底用户传递了几个参数过来,会动态判断是否需要添加 and 关键字

1
2
3
4
5
6
7
8
9
10
11
<where>  
<if test="status!=null">
and status = #{status}
</if>
<if test="companyName!=null and companyName!='' ">
and company_name like #{companyName}
</if>
<if test="brandName!=null and brandName!='' ">
and brand_name like #{brandName}
</if>
</where>

3、执行方法

1
2
3
//执行方法
List<Brand> brands = brandMapper.selectByCondition(brand);//传入一个brand对象
System.out.println(brands);

三种接收参数的格式

1. 散装参数格式

1
2
//1.散装参数的格式,需要使用@Param("SQL参数占位符名称")  
List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName, @Param("brandName") String brandName);

2. 对象参数

对象的属性名称要和参数占位符一致

1
List<Brand> selectByCondition(Brand brand);

注意:对于对象参数,在处理参数的时候,需要有封装对象的处理

1
2
3
4
5
6
7
8
9
//接收参数  
int status = 1;
String companyName = "华为";
String brandName = "华为";
//封装对象
Brand brand = new Brand();
brand.setStatus(status);
brand.setBrandName(brandName);
brand.setCompanyName(companyName);

三、Map 对象格式

1
2
3
4
5
6
7
8
9
10
11
12
//接收参数  
int status = 1;
String companyName = "华为";
String brandName = "华为";
//处理参数,用作模糊匹配
companyName = "%" + companyName + "%";
brandName = "%" + brandName + "%";
//map对象
Map map = new HashMap();
map.put("status",status);
map.put("companyName",companyName);
map.put("brandName",brandName);

MaBatis 对动态 SQL 有很强大的支撑

  • if
  • choose(when,otherwise)
  • trim(where,set)
  • foreach

条件查询 - 多个条件中选择一个

多个条件中选择一个,如下图片所示的下拉选项一样
07-下拉菜单(多选一).png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--单条件查询-->
<select id="selectByConditionSingle" resultMap="brandResultMap">
select *
from tb_brand
where
<choose><!--相当于switch-->
<when test="status!=null"><!--相当于case-->
status = #{status}
</when>
<when test="companyName!=null and companyName!=''">
company_name like #{companyName}
</when>
<when test="brandName!=null and brandName!='' ">
and brand_name like #{brandName}
</when>
</choose>
</select>

对于如上代码,当给定一个值时好说,但是如果一个值都没有给的话,此时就需要一个保底的方案,不然 SQL 会报语法错误。

那么我们可以使用 <otherwise> 标签,标签中边放一个恒定值,那么当用户给了一个参数之后,就不会执行 otherwise 中的内容,如果没有给参数的话,恒等式派上用场

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<choose><!--相当于switch-->
<when test="status!=null"><!--相当于case-->
status = #{status}
</when>
<when test="companyName!=null and companyName!=''">
company_name like #{companyName}
</when>
<when test="brandName!=null and brandName!='' ">
and brand_name like #{brandName}
</when>
<otherwise><!--相当于default-->
1 = 1
</otherwise>
</choose>

那么更进一步,可以和前面的 where 标签结合起来,就不需要使用到 otherwise 标签了

1
2
3
4
5
6
7
8
9
10
11
12
13
<where>  
<choose><!--相当于switch-->
<when test="status!=null"><!--相当于case-->
status = #{status}
</when>
<when test="companyName!=null and companyName!=''">
company_name like #{companyName}
</when>
<when test="brandName!=null and brandName!='' ">
and brand_name like #{brandName}
</when>
</choose>
</where>

添加数据

1、编写接口方法:Mapper 接口

  • 参数:除了 id 主键之外的所有参数
  • 结果:void
1
void add(Brand brand);

2、编写 SQL 语句

1
2
3
4
<!--添加功能-->  
<insert id="add">
insert into tb_brand (brand_name, company_name, ordered, description, status)
values(#{brandName},#{companyName},#{ordered},#{description},#{status})</insert>

3、执行方法

1
2
3
4
//4.执行方法  
brandMapper.add(brand);
//提交事务
sqlSession.commit();

在没有手动提交事务以前,虽然没有报错,但是查看数据库,并没有添加数据,原来是 autocommit 设置成了 false,所以需要我们手动提交一下
![[Pasted image 20221203151443.png]]

注:那么每次都需要手动提交事务,那不是挺麻烦。可以在获取 sqlSession 对象的时候就设置好自动提交事务

1
SqlSession sqlSession = sqlSessionFactory.openSession(true);

08-对数据库进行了修改操作后需要手动提交事务.png

MaBatis 事务

  • openSession ():默认开启事务,进行增删改操作后需要使用 sqlSession.commit () 手动提交事务
  • openSession (true):可以设置为自动提交事务(即关闭事务)

添加数据并主键返回

在数据添加成功后,需要获取插入数据库数据的主键的值

通过之前的添加方法,实际上是已经添加了一条数据,但是这个新添加的数据的 id 并没有绑定在这个新添加的对象上,所以 brand.getId() 返回的是一个 null

1
2
3
4
//4.执行方法  
brandMapper.add(brand);
Integer id = brand.getId();
System.out.println(id);

那么如何在添加数据的时候,将主键值和这个新添加的对象绑定在一起呢?

1
2
3
4
<!--添加功能并主键返回-->  
<insert id="add" useGeneratedKeys="true" keyProperty="id">
insert into tb_brand (brand_name, company_name, ordered, description, status)
values(#{brandName},#{companyName},#{ordered},#{description},#{status})</insert>

在 insert 标签中添加两个属性 useGeneratedKeys="true"keyProperty="id"

修改 - 修改全部字段

1、编写接口方法:Mapper 接口

  • 参数:所有数据
  • 返回类型:void
1
2
3
void update(Brand brand); //不返回结果值

int update(Brand brand); //返回影响的行数

2、编写 SQL 语句

1
2
3
4
5
6
7
8
9
10
<!--修改功能-->
<update id="update">
update tb_brand
set brand_name = #{brandName},
company_name = #{companyName},
ordered = #{ordered},
status = #{status},
description = #{description}
where id = #{id};
</update>

3、执行方法

需要在传入参数的时候把 id 也传进去,然后封装对象的时候,也 brand.setid (id)

1
2
3
//4.执行方法  
int count = brandMapper.update(brand); //返回影响的行数
System.out.println(count);

会存在一个问题,当我们之传入了某几个参数之后,另外几个没有传参,那么运行的话,就会把没有传参的字段赋值为 NULL

修改 - 动态修改字段

比如设置新密码,此时账号就不需要修改,而是只需要修改某几个数据,那么就需要使用到动态 SQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!--修改动态字段-->  
<update id="update2">
update tb_brand
<set>
<if test="brandName!=null and brandName!='' ">
brand_name = #{brandName},
</if>
<if test="companyName!=null and companyName!='' ">
company_name = #{companyName},
</if>
<if test="description!=null and description!='' ">
description = #{description},
</if>
<if test="ordered!=null">
ordered = #{ordered},
</if>
<if test="status!=null">
status = #{status}
</if>
</set>
where id=#{id};
</update>

那么此时,我们可以之修改某几个参数,另外几个参数没有修改的话,也不会被 NULL 值覆盖了

删除一个

1、编写接口方法:Mapper 接口

  • 参数:id
  • 返回类型:void
1
void deleteById(int id);

2、编写 SQL 语句

1
2
3
<delete id="deleteById">  
delete from tb_brand where id = #{id};
</delete>

3、执行方法

传递参数的时候只要将 id 传进来就可以了

1
2
//4.执行方法  
brandMapper.deleteById(id);

批量删除

1、编写接口方法:Mapper 接口

  • 参数:id 数组
  • 返回类型:void
1
void deleteByIds(@Param("ids") int[] ids);

如果没有加注解的话,就报了下面的错误

1
Parameter 'ids' not found. Available parameters are [array, arg0]

2、编写 SQL 语句

占位符的个数需要根据删除的个数进行变化,也就是需要遍历这个数组,MyBatis 中提供了相应的标签 <foreach>,可以用来遍历数组

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--MaBatis会将数组参数封装为一个Map集合,默认情况下
key为array
value为数组
但是通过使用@param注解之后,使用ids已经替换了默认的array,因此collection="ids"
-->
<delete id="deleteByIds">
delete from tb_brand
where id in (
<foreach collection="ids" item="id">
#{id}
</foreach>
)
</delete>

以上 SQL 语句仍然有问题,当有多个 id 时,就变成了如下

1
2
3
<foreach collection="ids" item="id">
#{id} #{id} #{id}
</foreach>

占位符之间少了逗号分隔符,因此还需要在 foreach 标签中添加属性

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--MaBatis会将数组参数封装为一个Map集合,默认情况下
key为array
value为数组
但是通过使用@param注解之后,使用ids已经替换了默认的array,因此collection="ids"
-->
<delete id="deleteByIds">
delete from tb_brand
where id in (
<foreach collection="ids" item="id" separator=",">
#{id}
</foreach>
)
</delete>

3、执行方法

传入一个 ids 数组

1
2
//4.执行方法  
brandMapper.deleteByIds(ids);

改进

如下代码可以正常执行功能,但是 foreach 标签包裹在小括号中,可不可以去掉外边的小括号呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--MaBatis会将数组参数封装为一个Map集合,默认情况下
key为array
value为数组
但是通过使用@param注解之后,使用ids已经替换了默认的array,因此collection="ids"
-->
<delete id="deleteByIds">
delete from tb_brand
where id in (
<foreach collection="ids" item="id" separator=",">
#{id}
</foreach>
)
</delete>

改为如下代码,即在 foreach 标签中添加 open 和 close 属性

1
2
3
4
5
6
7
<delete id="deleteByIds">
delete from tb_brand
where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>

MyBatis 参数传递

MyBatis 接口方法中可以接收各种各样的参数,MyBatis 底层对于这些参数进行不同的封装处理方式。扩展阅读:Mybatis @Param 注解的作用_DoUUnderstand 的博客 - CSDN 博客

  • 单个参数
    • POJO 类型:直接使用,属性名和参数占位符名称一致
    • Map 集合:直接使用,键名和参数占位符名称一致
    • Collection:封装为 Map 集合
      • map.put (“arg0”,collection 集合)
      • map.put (“collection”,collection 集合)
    • List:封装成 Map 集合
      • map.put (“arg0”,list 集合)
      • map.put (“collection”,list 集合)
      • map.put (“list”,list 集合)
    • Array:封装成 Map 集合
      • map.put (“arg0”, 数组)
      • map.put (“array”, 数组)
    • 其他类型:直接使用
  • 多个参数
    • 比如查询时的散装参数

总之,当有多个参数时,不要使用 Map 集合中的默认键名,使用 @Param 注解的方式来替换默认的键名

Map 集合中默认的键名如下

1
2
3
4
map.put("arg0",参数值1)
map.put("param1",参数值2)
map.put("arg1",参数值1)
map.put("param2",参数值2)

设置好 @Param 注解之后呢,会将新的键名覆盖掉默认的 arg0、arg1 键名,可读性更强

1
List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName);
1
2
3
4
5
6
7
8
9
10
11
<!--添加@Param前-->
select *
from ...
where status = #{arg0}
and company_name = #{arg1}

<!--添加@Param后-->
select *
from ...
where status = #{status}
and company_name = #{companyName}