目录
一、MyBatis简介
MyBatis特性
1)、MyBatis是优秀的持久层框架
2)、MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
3)、MyBatis可以使用xml或注解用于配置和原始映射 4)、MyBatis是一个半自动化的ORM框架
二、 搭建Mybatis
搭建MyBatis的核心配置文件
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.3</version>
</dependency>
</dependencies>
创建MyBatis的核心配置文件
这个配置文件一般是mybatis-config.xml。将来整合Spring之后,这个配置文件可以省略。
配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息。
核心配置文件存放的位置是src/main/resources目录下
<configuration>
<!--设置连接数据库的环境-->
<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="jjdbc:mysql://localhost:3306/qsh?useUnicode=true&serverTimezone=GMT&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
创建mapper接口
MyBatis种的mapper接口相当于以前的dao。但是区别,mapper仅仅是接口,我们不需要提供实现类
public interface mapper_user {
}
创建mybatis映射文件
相关概念:ORN对象映射
- 对象:java的实体类对象
- 关系:关系型数据库
- 映射:二者之间的对应关系
java概念 | 数据库概念 |
---|---|
类 | 表 |
属性 | 字段/列 |
对象 | 记录/行 |
映射文件的命名规则:
1)-----表对应的实体类的类名+Mapper.xml
如:User对的的映射文件,UserMapper.xml(一张表对应一个接口)
2)-----MyBatis映射文件用于编写SQL,访问以及操作表中的数据
3)-----MyBatis中可以面向接口操作数据,要保证两个一致
- mapper接口的全类名和映射文件的命名空间(namespace)保持一致
- mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致
<mapper namespace="com.guigu.mapper.mapper_user">
<insert id="insert" >
insert into t_user values (3,"qsh","123456",20,"男");
</insert>
</mapper>
测试
- SqlSession:代表Java程序和数据库之间的会话。(HttpSession是Java程序和浏览器之间的会话)
- SqlSessionFactory:是“生产”SqlSession的“工厂”
ublic class mybatis {
@Test
public void test() throws IOException {
//加载核心配置文件
Reader resourceAsReader = Resources.getResourceAsReader("mybatis-config.xml");
//获取sqlSessionFactoryBuilder
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//获取sqlSessionFactory
SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsReader);
//获取sqlSession
SqlSession sqlSession = build.openSession(true);
//获取Mapper对象
mapper_user mapper1 = sqlSession.getMapper(mapper_user.class);
//测试功能
int insert = mapper1.insert();
System.out.println(insert);
}
优化
在获取sqlSession对象时,使用SqlSession sqlSession = sqlSessionFactory.openSession(true);,传入一个Boolean类型的参数,值为true,这样就可以自动提交
加入 log4j日志功能
1、加入依赖
2、加入log4j配置文件
------logj的配置文件必须为log4j.xml,存放在src/main/resources目录下
------日志的级别:FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试) 从左到右打印的内容越来越详细
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
三、 Mybatis查询语句
查询功能的标签必须设置resultType或者resulmap
表示查询出来的语句要转换为那个功能。
resultType:表示默认映射关系(字段名和属性名一致的情况)
resultMap:表示自定义的映射关系(字段名和属性名不一致的情况)
User Select_id();
<select id="Select_id" resultType="com.guigu.pojo.User">
select * from t_user where id=1;
</select>
List<User> Select_list();
<select id="Select_list" resultType="com.guigu.pojo.User">
select * from t_user
</select>
四、核心配置文件
核心配置文件中的标签必须按照固定的顺序(写出来的标签顺序一定不能乱):
properties、settings、typeAliases、typeHandlers、objectFactory、objectWrapperFactory、reflectorFactory、plugins、environments、databaseIdProvider、mappers
<typeAliases>
<!--以包为单位,将包下所有的类型设置默认的类型别名,即类名不区分大小写-->
<package name="com.guigu.pojo"/>
</typeAliases>
mapprs映射文件
<!--
以包名为单位引入映射文件
要求:1、maper接口所在的包要和映射文件所在的包一致
2、mapper接口要和映射文件的名字一致
-->
<mappers>
<package name="com.guigu.mybatis.mapper"/>
</mappers>
五、 mybatis获取值的两种方式
MyBatis获取数值的两种方式:${}和#{}
${}本质就是字符串拼接,#{}本质就是占位符赋值
${}使用字符串的方式拼接sql若字符串类型或日期类型的字段进行赋值时,需要手动添加单引号;#{}使用占位符赋值的方式拼接sql,此时字符串类型或日期类型的字段进行赋值时,可以自动添加单引号
1、Mybatis通过单个字面类型
可以通过${}和#{}以任意的名称获取参数值,但需要注意 ${}的单引号类型
<select id="Userbyusername" resultType="User">
select * from t_user where username=#{username}
</select>
select id="Userbyusername" resultType="User">
select * from t_user where username='${username}'
</select>
2、Mybatis接口方法的参数为多个时
此时Mybatis会将这些参数放在一个map集合中
------以arg0,arg1…为键,以参数为值
------以param1,param2 …为键,以参数为值
<select id="Userbyusernamebypassword" resultType="user">
select * from t_user where username=#{arg0} and password=#{arg1}
</select>
3、 若mapper接口方法的参数有多个时,可以手动将这些参数放在一个map中存储。
List <User> Usersbymap(Map<String,String> map);
map.put("username","qsh");
map.put("password","123456");
List<User> usersbymap = mapper.Usersbymap(map);
usersbymap.forEach(user -> System.out.println(user));
<!--List <User> Usersbymap(Map<String,Object> map);-->
<select id="Usersbymap" resultType="User">
select * from t_user where username=#{username} and password=#{password}
</select>
4、mapper接口方法的参数是实体类类型的参数
只需要通过#{}和${}以属性的方式访问属性值即可
int insertbyuser(User user);
<!-- User insertbyuser();-->
<insert id="insertbyuser">
insert into t_user values (#{id},#{username},#{password},#{age},#{sex})
</insert>
//通过user的方法
public void inserbyuser() throws IOException {
SqlSession sqlSession = Mybatis_Util.getsqlSesion();
mapper_user mapper = sqlSession.getMapper(mapper_user.class);
int insertbyuser = mapper.insertbyuser(new User(12,"lm","321654",18,"男"));
System.out.println(insertbyuser);
}
5、使用@Param命名参数
此时mybatis会放在map集合中,以两种方式进行存储
-----以@param注解为值为键,以参数为值
-----以param注解为值,以参数没键
List <User> Userbyparam(@Param("username") String username, @Param("password") String password);
}
//通过user的方法
public void userbyparam(){
SqlSession sqlSession = Mybatis_Util.getsqlSesion();
mapper_user mapper = sqlSession.getMapper(mapper_user.class);
List<User> qsh = mapper.Userbyusernamebypassword("qsh", "123456");
qsh.forEach(q -> System.out.println(q));
}
}
六、Mybatis的各种查询功能
MyBatis中设置默认类的别名
查询处理的数据只有一条,可以通过实体类对象或者集合接收
查询处理的数据只有多条,只可以通过集合对象接收
查询多条和单条数据
//查询出单条记录 User Selectbyid (@Param("id") Integer id);
User Selectbyid (@Param("id") Integer id);
//查询出多条记录 List<User> Selectall(@Param("username") String username);
List<User> Selectall(@Param("username") String username);
查询结果集类型是map集合
以字段为键,以字段对应的值为值
Map<String,Object> selectbyidtomapp( @Param("id") Integer id);
@Test
public void Selectbyidtocount(){
SqlSession sqlSession = Mybatis_Util.getsqlSesion();
Selectmapper mapper = sqlSession.getMapper(Selectmapper.class);
Map<String, Object> selectbyidtomapp = mapper.selectbyidtomapp(1);
System.out.println(selectbyidtomapp);
}
结果集以键值对的方式出现
查询多个数据为map
可以在mapper接口的方法上添加@Mapkey(”某个字段的键,必须唯一“)注解,将每条数据转换的map集合作为值,以某个字段的值做为键,放在同一个map集合中
@MapKey("id")
Map<String,Object> selectbyidtomapall(@Param("username") String username);
<select id="selectbyidtomapall" resultType="map">
select * from t_user where username=#{username}
</select>
结果集:
七、特殊SQL执行
模糊查询
批量删除
#{}会给参数自动添加 ’ ‘,所以批量删除的时候格式不正确,我们使用${}
例如:string a=“1,2,3”,若使用#{}系统会认为是’1,2,3’
<!--//批量删除int SQLdelete(@Param("ids") String ids);-->
<delete id="SQLdelete">
delete from t_user where id in (${ids})
</delete>
动态设置表面
<!-- 动态设置表名 //动态设置表名List<User> User_Table(@Param("username_Table") String username_Table); -->
<select id="User_Table" resultType="user">
select * from ${username_Table}
</select>
添加功能获取自增的主键
UserGeeneratedkey=“ture” 表示当前sql使用了自动递增的主键
keyproperty:将自增的主键的值赋值给传输映射文件中参数的某个属性
<!--//设置自动添加主键void insert_key (User user);-->
<insert id="insert_key" useGeneratedKeys="true" keyProperty="id">
insert into t_user values(null,#{username},#{password},#{age},#{sex})
</insert>
八、自定义映射
解决字段名和属性名不一致的情况
-----a>为字段起别名,保存和属性名的一致
-----b>设置全局配置,将_自动映射为驼峰
------c>通过resulMap设置自定义的映射关系
b:
<!--设置mybatis的全局变量-->
<settings>
<!--将_自动映射为驼峰,emp_name=empName-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
c:
<!--
resultMap:设置自定义映射关系
id:唯一标识,不可重复
type:设置映射关系中的关系类型
子标签:1)id:用于设置主键
2)result:设置普通字段的类型
子标签的属性:1)property:所设置的实体类类型中的字段
2)column:设置sql语句查询出来的字段
-->
<resultMap id="emp" type="Emp">
<id property="eid" column="eid"></id>
<result property="EmpName" column="emp_name" ></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="did" column="did"></result>
</resultMap>
解决多对一、一对多关系
多对一
多对一,我们需要创建一所创建的对象
处理多对一的关系:-----a)通过级联方式赋值
-----b)通过association
-----c)分步查询
a、
<resultMap id="resltmapbyid" type="emp">
<id property="eid" column="eid"></id>
<result property="EmpName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="did" column="did"></result>
<result property="dept.DeptName" column="dept_name"></result>
<result property="dept.did" column="did"></result>
</resultMap>
<!--public List<Emp> Select_Empbydid(@Param("eid") Integer eid);-->
<select id="Select_Empbydid" resultMap="resltmapbyid">
SELECT * FROM t_emp LEFT JOIN t_dept ON t_emp.`did`=t_dept.`did` WHERE t_emp.`eid`='${eid}'
</select>
b、
<!--
association:处理多对一关系
property:处理多对的映射关系的属性名
javaType:该属性的类型
-->
<resultMap id="resltmapbyidtwo" type="emp">
<id property="eid" column="eid"></id>
<result property="EmpName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="did" column="did"></result>
<association property="dept" javaType="Dept">
<id property="did" column="did"></id>
<association property="DeptName" column="dept_name"></association>
</association>
</resultMap>
c
<!--
select:设置分步查询的sql的唯一标识,mapper接口的全类名,方法名
colum:设置分布查询的条件
fetchType:当开启了全局的延迟加载之后,可通过手动来开启延迟加载的效果,fetchType=“lazy(延迟加载) | eager(立即加载)”
-->
<resultMap id="Select_Emp_by1_resultmap" type="Emp">
<id property="eid" column="eid"></id>
<result property="EmpName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="did" column="did"></result>
<association property="dept" select="com.guigu.mybaitis.mapper.Dept_mapper.Select_Dept_by02" column="did">
</association>
</resultMap>
<!--public List<Emp> Select_Emp_by1(@Param("eid") Integer eid);-->
<select id="Select_Emp_by1" resultMap="Select_Emp_by1_resultmap">
select * from t_emp where eid=#{eid}
</select>
延迟加载:
延迟加载:只有当你真正需要数据时,才加载。
立即加载:无论你是否需要数据,都要加载
比如:分步查询中如果你只需要访问一张表的内容那就执行一张表的sql儿
<settings>
<!--是否开启延迟加载-->
<setting name="lazyLoadingEnabled" value="True"/>
</settings>
一对多关系
一对多,我们需要创建一个集合
处理一对多关系-------a)通过collection:
--------b)分布查询
a)
<!--
collection :处理一对多的映射关系
ofType:表示该属性所对应的集合中存储的数据类型
-->
<resultMap id="resultmap_Dept" type="Dept">
<id property="did" column="did"></id>
<result property=" DeptName" column="dept_name"></result>
<collection property="emps" ofType="Emp">
<id property="eid" column="eid"></id>
<result property="EmpName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
</collection>
b)
<!--public Dept getdeptbyemp(@Param("did") Integer did)-->
<resultMap id="getdeptbyemp1" type="Dept">
<id property="did" column="did"></id>
<result property="DeptName" column="Dept_name"></result>
<collection property="emps" select="com.guigu.mybaitis.mapper.Emp_mapper.DeptbyEmp" column="did">
</collection>
</resultMap>
<select id="getdeptbyemp" resultMap="getdeptbyemp1">
select * from t_dept where did='${did}'
</select>
九、MyBatis的逆向工程
- 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的
- 逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
– Java实体类
– Mapper接口
– Mapper映射文件
创建逆向工程的步骤
添加依赖
<dependencies>
<!-- MyBatis核心依赖包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!-- log4j日志 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<!-- 控制Maven在构建过程中相关配置 -->
<build>
<!-- 构建过程中用到的插件 -->
<plugins>
<!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.0</version>
<!-- 插件的依赖 -->
<dependencies>
<!-- 逆向工程的核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
创建mybatis核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties"/>
<typeAliases>
<package name=""/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name=""/>
</mappers>
</configuration>
创建逆向工程的配置文件
文件名必须是:generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--
targetRuntime: 执行生成的逆向工程的版本
MyBatis3Simple: 生成基本的CRUD(清新简洁版)
MyBatis3: 生成带条件的CRUD(奢华尊享版)
-->
<context id="DB2Tables" targetRuntime="MyBatis3Simple">
<!-- 数据库的连接信息 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis"
userId="root"
password="123456">
</jdbcConnection>
<!-- javaBean的生成策略-->
<javaModelGenerator targetPackage="com.atguigu.mybatis.pojo" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="com.atguigu.mybatis.mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.atguigu.mybatis.mapper" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
<!-- domainObjectName属性指定生成出来的实体类的类名 -->
<table tableName="t_emp" domainObjectName="Emp"/>
<table tableName="t_dept" domainObjectName="Dept"/>
</context>
</generatorConfiguration>
执行MBG插件的generate目标
测试豪华至尊版
- selectByExample:按条件查询,需要传入一个example对象或者null;如果传入一个null,则表示没有条件,也就是查询所有数据
- example.createCriteria().xxx:创建条件对象,通过andXXX方法为SQL添加查询添加,每个条件之间是and关系
- example.or().xxx:将之前添加的条件通过or拼接其他条件
- updateByPrimaryKey:通过主键进行数据修改,如果某一个值为null,也会将对应的字段改为null
- updateByPrimaryKeySelective():通过主键进行选择性数据修改,如果某个值为null,则不修改这个字段
Reader resourceAsReader = Resources.getResourceAsReader("Mybatis-config.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsReader);
SqlSession sqlSession = build.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//查询出所有的数据
// List<Emp> emps = mapper.selectByExample(null);
//emps.forEach(emp -> System.out.println(emp));
//根据条件查询
EmpExample empExample = new EmpExample();
//设置条件
empExample.createCriteria().andAgeEqualTo(21);
empExample.or().andAgeEqualTo(22);
List<Emp> emps = mapper.selectByExample(empExample);
emps.forEach(emp -> System.out.println(emp));
十分页插件
分页插件使用步骤
添加依赖
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
配置分页插件
- 在MyBatis的核心配置文件(mybatis-config.xml)中配置插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
分页插件使用步骤
- 在查询功能之前使用PageHelper.startPage(int pageNum, int pageSize)开启分页功能
– pageNum:当前页的页码
– pageSize:每页显示的条数
@Test
public void testPageHelper() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//访问第一页,每页四条数据
PageHelper.startPage(1,4);
List<Emp> emps = mapper.selectByExample(null);
emps.forEach(System.out::println);
}
分页相关数据
- 在查询获取list集合之后,使用PageInfo pageInfo = new PageInfo<>(List list, intnavigatePages)获取分页相关数据
– list:分页之后的数据
– navigatePages:导航分页的页码数
@Test
public void testPageHelper() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
PageHelper.startPage(1, 4);
List<Emp> emps = mapper.selectByExample(null);
PageInfo<Emp> page = new PageInfo<>(emps,5);
System.out.println(page);
}
常用数据
- pageNum:当前页的页码
- pageSize:每页显示的条数
- size:当前页显示的真实条数
- total:总记录数
- pages:总页数
- prePage:上一页的页码
- nextPage:下一页的页码
- isFirstPage/isLastPage:是否为第一页/最后一页
- hasPreviousPage/hasNextPage:是否存在上一页/下一页
- navigatePages:导航分页的页码数
- navigatepageNums:导航分页的页码,[1,2,3,4,5]