0%

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}

什么是 MyBatis

  • MyBatis 是一款优秀的 持久层框架,用于简化 JDBC 开发

持久层

  • 负责将数据保存到数据库的那一层代码
  • JavaEE 三层结构:表现层(页面展示)、业务层(逻辑处理)、持久层(数据库相关)

框架

  • 框架是一个 半成品 软件,是一套可重用的、通用的、软件基础代码模型
  • 在框架的基础之上构建编写更加高效、规范、通用、可扩展

为什么 MyBatis

JDBC 缺点

  1. 硬编码问题

比如注册驱动,获取连接对象时,需要通过变量接收这些字符串,都是写死在代码中的,但是后期可能会对这些字符串进行修改

  1. 操作繁琐

对于处理对象中的问号占位符,手动封装结果集,都需要手动设置参数,操作繁琐

MyBatis 简化

  1. 硬编码问题 ⇒ 通过配置文件解决

比如字符串硬编码问题,将字符串写到 mybatis-config.xml 文件中;将 mysql 语句写到 UserMapper.xml 文件中

  1. 操作繁琐 ⇒ 自动完成

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

MyBatis 快速入门

案例 1:查询 user 表中的所有数据

  1. 创建 user 表,添加数据
  2. 创建模块,导入坐标
  3. 创建 MyBatis 核心配置文件⇒替换连接信息,解决硬编码问题
  4. 创建 SQL 映射文件⇒统一管理 SQL 语句,解决硬编码问题
    1. 在项目下新建 mapper 包,创建 UserMapper 接口
    2. 在 resources 资源文件夹下,创建 UserMapper.xml 配置文件
  5. Demo 主程序
    1. 定义 POJO 类
    2. 加载核心配置文件,获取 SqlSessionFactory 对象
    3. 获取 SqlSession 对象,执行 SQL 语句
    4. 释放资源

1、创建 user 表,添加数据

1
2
3
4
5
6
7
8
9
10
11
12
13
create database mybatis;
use mybatis;
drop table if exists tb_user;
create table tb_user(
id int primary key auto_increment,
username varchar(20),
password varchar(20),
gender char(1),
addr varchar(30)
);
INSERT INTO tb_user VALUES (1, 'zhangsan', '123', '男', '北京');
INSERT INTO tb_user VALUES (2, '李四', '234', '女', '天津');
INSERT INTO tb_user VALUES (3, '王五', '11', '男', '西安');

2、创建模块,导入坐标

  1. 新建空项目
  2. 点击项目结构,模块,新建模块,选择 Maven 项目
  3. 在 pom.xml 文件中导入如下坐标
1
2
3
4
5
6
7
8
9
10
11
12
13
<dependencies>
<!--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>

3、创建 MyBatis 核心配置文件

  1. 在 resources 文件下新建 mybatis-config.xml 文件,将以下内容粘贴到 xml 文件中。注:代码模板可从 mybatis 官方入门 复制

注意修改数据库的连接信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库的连接信息-->
<property name="driver" value="com.mysql.jdbc.Driver "/>
<property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--加载sql映射文件-->
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>

4、创建 SQL 映射文件及接口

  1. 在 mapper 包下新建 UserMapper 接口
1
2
3
4
5
public interface UserMapper {
List<User> selectAll();
//User selectById(int id);

}
  1. 在 resources 文件夹下,新建 UserMapper.xml 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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:名称空间
-->

<mapper namespace="test">
<!--查询标签,id为这条SQL语句的唯一标识,resultType为返回结果类(POJO类)-->
<select id="selectAll" resultType="com.itheima.pojo.User">
select * from tb_user;
</select>
</mapper>

注:此处的代码模板也可以官方的实例模板中粘贴:探究已映射的 SQL 语句

5、Demo 主程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MyBatisDemo {
public static void main(String[] args) throws IOException {
//1.加载mybatis的核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

//2.获取SqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession();

//3.执行SQl语句,名称空间.ID,返回list集合
List<User> users = sqlSession.selectList("test.selectAll");

System.out.println(users);

//4.释放资源
sqlSession.close();
}
}

需求

需要使用 VScode 来编辑 md 文件,并且希望每次在创建 md 文档时,都能够自动补全 YAML 文件信息

配置 setting.json 文件

  1. Ctrl+Shift+P,搜索 setting,选择 首选项:打开用户设置 (json)
  2. 在这给文件中添加如下代码
1
2
3
"[markdown]":  {  
"editor.quickSuggestions": true
}

以下图片显示的是之前就对 setting.json 文件配置过,找到 markdown 的位置添加上述的那一行代码即可;如果之前没有配置过,只需要将上述的那一串代码全部复制到下图中最大的一对黄括号中即可
配置SettingJson.png

配置 md 模板

01-VScode配置用户代码片段.png

  1. Ctrl+Shift+P,搜索 snippets,选则 配置用户代码片段,再选择 markdown
  2. 将一下代码复制进去保存
  3. 新建 md 文档之后,输入 log 敲 tab 即可自动补全配置好的模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
// Place your snippets for markdown here. Each snippet is defined under a snippet name and has a prefix, body and
// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
// same ids are connected.
// Example:
// 联想时显示的文字内容
"create blog": {
"prefix": "log", // 输入log,即显示模板提示
"body": [
// body里是模板内容
"---",
"title: $1", // $1表示生成模板后,光标最先在此位置
"date: $2", // $2表示,在$1处输入完成后,按tab键,光标便跳转到这里,以此类推
"tags:",
" - $3",
"---",
"",
],
"description": "blog模板"
}
}

参考资料

导出高清图片

导出图片时的关键设置

  • 颜色格式设置为 256 位(实际上默认 24 位色也是足够了的)
  • 分辨率设置为打印机模式(400×400 效果已经不错了)
  • 分辨率设置为自定义,300 × 300 像素 /in. 效果已经非常不错

绘制圆角箭头

有时候直接使用直线工具绘制箭头比自带的箭头工具好用得多,以下图片中演示的就是使用直线工具来绘制箭头
Visio绘制圆角箭头.gif

格式刷的应用

比下图所示,箭头可以通过格式化统一成一致的样式
Visio使用格式刷绘制箭头.gif

标准色块的使用

将图片统一调整 / 裁剪成一样的比例 —> 如何快速的设置选框 —> 新建一个固定的标准矩形

Pasted image 20231020154604.png

  1. 新建标准矩形,添加填充色
  2. 裁剪的时候,会有一些透明,可以透过这层透明查找裁剪的边缘,很方便可以找到标准色块的边

上下角标

Visio 不像 Word 那样有上角标,下角标按钮,需要使用到快捷键来完成这个操作

上角标:Ctrl + Shift + =

下角标:Ctrl + =

嵌入 Word 中

如何调整 Visio 对象的嵌入格式以及 Visio 对象尺寸

右键 Visio 对象 —> 图片 —> 调整大小和版式等操作

Visio对象嵌入Word当中调整图片.gif

如何插入表格

从 Excel 中复制表格,粘贴到 Visio 中,粘贴的时候选择性粘贴,选择 Excel 格式。此时 Visio 中的数据是和 Excel 表格中的数据是关联的,还可以实现动态更新呢

如果在 Visio 中调整表格的格式,比如字体的颜色,那么需要双击进入到关联的 Excel 表格中,在 Excel 中修改格式

粘贴到 word 中白边太宽

将 Visio 图直接粘贴到 Word 中时,可能会遇到 Visio 图片显示不全的情况。word 中双击这个 Visio 对象,可以按住 control/Ctrl 键,拖动画布边缘,调整画布尺寸至合适大小,鼠标离开 Visio 对象编辑区域单击即可自动保存

导出图片时最下方出现一行空白

这可能是图片的图注造成的

双击图片时,自动会弹出一个文本框,如果敲了一个空格,再退出编辑,此时图片的图注就仅有一个空白的空格,导致出图的时候把这个空白的图注也带上了

解决方案:可以通过查询空格字符,如果能够查询得到,就会自动跳转到图注位置,删除这个空格即可

visio 取消首字母自动大写

Visio取消首字母自动大写.png

两形状之间直的连接线

这种情况有两种解决办法:

  1. 微调:选中下面的矩形通过 shift+ 方向键 微调来完成
  2. 对齐:选中整个图形,选择排列,选中水平居中进行调整即可
  3. 对不齐的时候,试试按着 Alt 键拖动形状试试:)

绘图时保证图片与文字大小协调

  1. 首先输入 8-12 号的字体,插入图片调整至和文字协调的大小(图片尽可能是由大图调整至小图),也就是说图片的大小按照标准的文字大小进行调整
  2. 最好提前想好这张图在投稿时是打算单栏排版还是双栏排版,或者两个排版方式各导出一张
  3. 图片更新时,最好在同一个 Visio 文件中新建页面,在新建的页面中对图片进行编辑,保存旧版本图片

辅助线

从标尺处拖拉可添加辅助线

连接线与线条

能用线条的地方尽量不要用连接线。连接线通常用来连接两个形状,但是这两个形状位置或大小发生变化或者整张大图的比例需要调整时,这个连接线就会自动更着变化,非常容易导致排版问题。连续画几条线条,这几条线条会自动连接在一起,并且在属性中也可以设置圆角,箭头等属性,这样已经完全可以实现连接线的功能了,所以,尽可能不要再绘图中使用连接线。

编辑 Xmind 导出的形状

  1. Xmind 导出形状为 SVG 格式
  2. Visio 自带 SVG 格式编辑功能
  3. 还是矢量图格式的,妙啊!

Visio2PPT

直接框选复制到 ppt 当中,会自动转换为 emf 格式的矢量图片,而且背景是透明的哦,不会受 vision 背景底图的影响

思维导图样式

![[Visio 一对多多对一线条绘制 1.gif]]

导出 PDF

1、如果有多个页面,导出 PDF 时默认会将所有的页面都导出。如果只想导出当前页面,记得勾选

image.png

2、PDF 页面是按照画布来的,有空白的地方在 PDF 中也会跟着导出,故需要提前调整画布尺寸

image.png

CAD2Visio

在 Visio 中,可将复制到 CAD 图形当成一个对象,双击还可进入 CAD 中进行编辑

收藏夹的应用

1、可应用 Visio 中自带的一些形状,例如水平基线和垂直基线来给图形添加尺寸

image.png

2、可将常用的形状加入收藏夹,后期方便反复调用

image.png

插件官网: Add-ons for Anki 2.1

插件

正文

注:本文内容的主体结构转载自博客园 LaTeXmath:LaTeX 中列表环境的使用,本人仅对案例中的一些代码、图片及一些宏包的使用做了一些补充。

列表就是将所要表达的内容分为若干个条目并按一定的顺序排列,达到简明、直观的效果。LaTeX 中常见的列表环境有 enumerate、itemize 和 description。这三种列表环境的主要区别是列表项标签的不同:

有序列表和无序列表无需在进行介绍,对于 description 列表来说,可指定其标签

1
2
3
4
5
6
7
8
9
\documentclass{ctexart}
\usepackage{pifont}
\begin{document}
\begin{description}
\item[\ding{47}] This is the first item
\item[\ding{47}] This is the second item
\item[\ding{47}] This is the third item
\end{description}
\end{document}

01-指定description标签.png

了解关于 pifont 宏包的更多信息,请参考:pifont – Access to PostScript standard Symbol and Dingbats fonts,如下为 pifont 的一些字符

02-pifont的一些字符.png

或者参阅以下的简要信息快速了解 pifont 宏包

The package provides commands for Pi fonts (Dingbats, Symbol, etc.); all commands assume you know the character number within the font of the symbol within the font.

列表环境也可以互相嵌套,默认情况下不同层级的标签不同,以体现分级层次。

进阶用法

可参考个人博客:enumitem 宏包中的长度设置 @无锤乙醇

以如下代码对自定义列表环境进行案例分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
\documentclass{ctexart}
\usepackage{enumerate}
\usepackage{enumitem}
\setlist[enumerate,1]{label=(\arabic*).,font=\textup,
leftmargin=7mm,labelsep=1.5mm,topsep=0mm,itemsep=-0.8mm}
\setlist[enumerate,2]{label=(\alph*).,font=\textup,
leftmargin=7mm,labelsep=1.5mm,topsep=-0.8mm,itemsep=-0.8mm}
\begin{document}
\begin{enumerate}
\item 这是一个一级列表
\item 看我在嵌套一个二级列表
\begin{enumerate}
\item 这是一个二级列表
\end{enumerate}
\end{enumerate}
\end{document}

 \setlist[enumerate,1] 表示对一级列表进行设置,\setlist[enumerate,2] 表示对二级列表进行设置。这样一级列表的标签就是括号加阿拉伯数字加点,二级标签是括号加小写英文字母加点。输出效果为:

04-自定义修改列表样式.png

font=\textup 表示使用直立体(可参考官方入门手册)

05-字体命令.png

参考资料