MyBatis详细笔记
一、 Mybatis简介
1.1 MyBatis历史
MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下,iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github
iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBatis提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
1.2 MyBtis特性
MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录
MyBatis 是一个 半自动的ORM(Object Relation Mapping)框架
1.3 和其它持久化层技术对比
JDBC
Hibernate 和 JPA
操作简便,开发效率高
程序中的长难复杂 SQL 需要绕过框架
内部自动生产的 SQL,不容易做特殊优化
基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。
反射操作太多,导致数据库性能下降
MyBatis
轻量级,性能出色
SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
开发效率稍逊于HIbernate,但是完全能够接受
二、 搭建MyBatis
2.1 开发环境
2.2 创建maven工程
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 <dependencies> <!-- Mybatis核心 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency> <!-- junit测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.3</version> </dependency> </dependencies>
创建MyBatis的核心配置文件
MyBatis的核心配置文件习惯上命名为mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。将来整合Spring之后,这个配置文件可以省略,所以大家操作时可以直接复制、粘贴。
核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息
核心配置文件存放的位置是 src/main/resources 目录下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?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> <!--设置连接数据库的环境--> <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://localhost:3306/MyBatis"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!--引入映射文件--> <mappers> <mapper resource="mappers/UserMapper.xml"/> </mappers> </configuration>
2.4 创建mapper接口
MyBatis中的 mapper 接口相当于以前的dao。但是区别在于,mapper仅仅是接口,我们不需要提供实现类。
补充:
在Mybatis中,可以有 Mapper 实现类;只不过为了简便,只要我们(程序员)遵守Mybatis制定的语法规范,Mybatis 就可以使用 动态代理 机制通过 Mapper 接口以及 Mapper.xml 文件自动生成 Mapper 接口实现类。
规范如下:
1.获取mapper接口的方法改变了:
原方法:StudentMapper studentMapper = new StudentMapperImpl();(StudentMapperImpl 实现类中使用 Mybatis 的方法通过 Mapper.xml 文件完成数据库的操作,如:
int count = sqlSession.insert(“mapper.xml标签id”, new Student(“张三”, “123456”);)
现方法:StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
2.Mapper.xml 文件中的 namespace 必须是对应 Mapper 接口的全限定接口名(带包名);
3.Mapper接口中的方法名必须和 Mapper.xml 标签中的 id 一致。
1 2 3 4 5 6 7 8 9 10 11 * ```java package com.atguigu.mybatis.mapper; public interface UserMapper { int insertUser () ; } ```
2.5 创建MyBatis的映射文件
相关概念:ORM(Object Relationship Mapping)对象关系映射。
对象:Java的实体类对象
关系:关系型数据库
映射:二者之间的对应关系
映射文件的命名规则:表所对应的实体类的类名+Mapper.xml
例如:表t_user,映射的实体类为User,所对应的映射文件为 UserMapper.xml
一个映射文件对应一个实体类,对应一张表的操作
MyBatis映射文件用于编写SQL,访问以及操作表中的数据
MyBatis映射文件存放的位置是src/main/resources/mappers目录下
MyBatis中可以面向接口操作数据,要保证两个一致:
mapper接口的全类名和映射文件的命名空间(namespace)保持一致
mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致
1 2 3 4 5 6 7 8 9 10 11 12 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.atguigu.mybatis.mapper.UserMapper" > <insert id ="insertUser" > insert into t_user values(null,'张三','123',23,'女') </insert > </mapper >
2.6 通过junit测试功能
SqlSession:代表Java程序和数据库之间的会话。(HttpSession是Java程序和浏览器之间的会话)
SqlSessionFactory:是“生产”SqlSession的“工厂”
工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建这个对象的相关代码封装到一个“工厂类”中,以后都使用这个工厂类来“生产”我们需要的对象
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 public class UserMapperTest { @Test public void testInsertUser () throws IOException { InputStream is = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder (); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); SqlSession sqlSession = sqlSessionFactory.openSession(true ); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); int result = userMapper.insertUser(); System.out.println("result:" + result); } }
此时需要手动提交事务,如果要自动提交事务,则在获取sqlSession对象时,使用SqlSession sqlSession = sqlSessionFactory.openSession(true);,传入一个Boolean类型的参数,值为true,这样就可以自动提交。
通常SqlSessionFactoryBuilder不需要单独创建,只需要调用一次方法即可:
1 2 3 4 5 6 7 String resource = "org/mybatis/example/mybatis-config.xml" ;InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ().build(inputStream);SqlSession sqlSession = factory.openSession();
1 2 3 4 5 6 也可以进行合并: SqlSessionFactory factory = new SqlSessionFactoryBuilder () .build(Resources.getResourceAsStream("mybatis-config.xml" )); SqlSession sqlSession = factory.openSession();
小技巧:
mybatis 中的 sql 语句最后的 分号 可以省略(不同版本可能不同);
遇到 resource 这个单词时,大部分情况下,加载资源的方式都是从类的根路径(resources)下开始加载。
不使用 Reources 类获取流,而自己创建流也可以,但是这种方法的可移植性太差,当移植到其他操作系统中时,程序可能将不能运行,需要修改路径才可以运行。而由于 mybatis 配置文件始终都在类路径中,所以 Resources 解决了移植性问题。
1 2 3 4 InputStream is = Resources.getResourceAsStream("" );的源代码中是以 InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("" );实现的。
2.7 加入log4j日志功能
1.加入依赖
1 2 3 4 5 6 <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency >
2.加入log4j的配置文件
log4j的配置文件名为log4j.xml,存放的位置是src/main/resources目录下
日志的级别:FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试) 从左到右打印的内容越来越
1 <?xml version="1.0" encoding="UTF-8" ?>
<log4j:configuration xmlns:log4j=“http://jakarta.apache.org/log4j/ ”>
</log4j:configuration>
2.8 SqlSessionUtil工具类
提供一个MyBatis的工具类:SqlSessionUtil工具类。
要编写SqlSessionUtil工具类需要了解MyBatis核心对象的生命周期:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 - SqlSessionFactory - 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建。 - 对于一个应用来说,整个web容器中只有一个SqlSessionFactory对象,该对象是重量级的,只能创建一次。 - 所以该对象应该在类加载时创建,并且只创建一次,不允许重建或销毁。 - 由上可知该对象在静态代码块中完成创建。 - SqlSession - 每个线程都应该有它自己的SqlSession实例。SqlSession的实例不是线程安全的,因此是不能被共享的,所以它的最佳作用域是请求或方法作用域。(ThreadLocal) - **非常重要的原则:一个线程一个Connection,一个线程一个SqlSession。**怎么保证? - 将Connection和SqlSession放到ThreadLocal中。 ```java package com.wkcto.crm.utils;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;public class SqlSessionUtil { private SqlSessionUtil () {} private static SqlSessionFactory factory = null ; private static ThreadLocal<SqlSession> threadlocal = new ThreadLocal <>();static { try { factory = new SqlSessionFactoryBuilder () .build(Resources.getResourceAsStream("mybatis-config.xml" )); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getCurrentSqlSession () { SqlSession sqlSession = threadlocal.get(); if (sqlSession == null ){ sqlSession = factory.openSession(); threadlocal.set(sqlSession); } return sqlSession; } public static void close (SqlSession sqlSession) { if (sqlSession != null ){ sqlSession.close(); threadlocal.remove(); } } public static void rollback (SqlSession sqlSession) { if (sqlSession != null ) { sqlSession.rollback(); } } }
三、 核心配置文件详解
核心配置文件中的标签必须按照固定的顺序(有的标签可以不写,但顺序一定不能乱):
properties、settings、typeAliases、typeHandlers、objectFactory、objectWrapperFactory、reflectorFactory、plugins、environments、databaseIdProvider、mappers
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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 <?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" > </properties > <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> <setting name ="lazyLoadingEnabled" value ="true" /> </settings > <typeAliases > <package name ="com.atguigu.mybatis.bean" /> </typeAliases > <environments default ="mysql_test" > <environment id ="mysql_test" > <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 ="com.atguigu.mybatis.mapper" /> </mappers > </configuration >
注意:mapper接口和mapper映射文件在同一包下
四、 默认的类型别名
五、 MyBatis的增删改查
1 2 3 4 <insert id ="insertUser" > insert into t_user values(null,'admin','123456',23,'男','12345@qq.com') </insert >
1 2 3 4 <delete id ="deleteUser" > delete from t_user where id = 6 </delete >
1 2 3 4 <update id ="updateUser" > update t_user set username = '张三' where id = 5 </update >
1 2 3 4 <select id ="getUserById" resultType ="com.atguigu.mybatis.bean.User" > select * from t_user where id = 2 </select >
1 2 3 4 <select id ="getUserList" resultType ="com.atguigu.mybatis.bean.User" > select * from t_user </select >
六、 MyBatis获取参数值的两种方式(重点)
MyBatis获取参数值的两种方式:${} 和 #{}
但是 #{} 使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号.
6.1 单个字面量类型的参数
若mapper接口中的方法参数为单个的字面量类型,此时可以使用${}和#{}以任意的名称(最好见名识意)获取参数的值,注意${}需要手动加单引号
1 2 3 4 <!--User getUserByUsername (String username) ;--> <select id="getUserByUsername" resultType="User" > select * from t_user where username = #{username} </select>
1 2 3 4 <!--User getUserByUsername (String username) ;--> <select id="getUserByUsername" resultType="User" > select * from t_user where username = '${username}' </select>
6.2 多个字面量类型的参数
若mapper接口中的方法参数为多个时,此时 MyBatis 会自动将这些参数放在一个map集合中
以arg0,arg1…为键,以参数为值;
以param1,param2…为键,以参数为值;
因此只需要通过 ${} 和 #{} 访问map集合的键就可以获取相对应的值,注意 ${} 需要手动加单引号。
使用 arg 或者 param 都行,要注意的是,arg是从 arg0 开始的,param 是从 param1 开始的
1 2 3 4 <!--User checkLogin (String username,String password) ;--> <select id="checkLogin" resultType="User" > select * from t_user where username = #{arg0} and password = #{arg1} </select>
1 2 3 4 <!--User checkLogin (String username,String password) ;--> <select id="checkLogin" resultType="User" > select * from t_user where username = '${param1}' and pswd= '${param2}' </select>
6.3 map集合类型的参数
若 mapper 接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中只需要通过 ${} 和 #{} 访问 map 集合的键就可以获取相对应的值,注意 ${} 需要手动加单引号
1 2 3 4 <!--User checkLoginByMap (Map<String,Object> map) ;--> <select id="checkLoginByMap" resultType="User" > select * from t_user where username = #{username} and password = #{password} </select>
1 2 3 4 5 6 7 8 9 10 @Test public void checkLoginByMap () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class); Map<String,Object> map = new HashMap <>(); map.put("usermane" ,"admin" ); map.put("password" ,"123456" ); User user = mapper.checkLoginByMap(map); System.out.println(user); }
6.4 实体类类型的参数(重点)
若 mapper 接口中的方法参数为实体类对象时此时可以使用 ${} 和 #{} ,通过访问实体类对象中的属性名获取属性值,注意 ${} 需要手动加单引号
1 2 3 4 <!--int insertUser (User user) ;--> <insert id="insertUser" > insert into t_user values (null ,#{username},#{password},#{age},#{sex},#{email}) </insert>
1 2 3 4 5 6 7 8 9 @Test public void insertUser () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class); User user = new User (null ,"Tom" ,"123456" ,12 ,"男" ,"123@321.com" ); mapper.insertUser(user); } ### 6.5 使用@P
6.5aram标识参数(重点)
1 2 3 4 <!--User CheckLoginByParam (@Param("username") String username, @Param("password") String password) ;--> <select id="CheckLoginByParam" resultType="User" > select * from t_user where username = #{username} and password = #{password} </select>
1 2 3 4 5 6 @Test public void checkLoginByParam () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class); mapper.CheckLoginByParam("admin" ,"123456" ); }
总结
七、 MyBatis的各种查询功能
1.如果查询出的数据只有一条,可以通过
1.实体类对象接收
2.List集合接收
3.Map集合接收,结果{password=123456, sex=男, id=1, age=23, username=admin}
2.如果查询出的数据有多条,一定不能用实体类对象接收,会抛异常 TooManyResultsException,可以通过
1.实体类类型的LIst集合接收
2.Map类型的LIst集合接收
3.在mapper接口的方法上添加@MapKey注解
7.1 查询一个实体类对象
1 2 3 4 5 * 根据用户id查询用户信息 * @param id * @return */ User getUserById (@Param("id") int id) ;
1 2 3 4 <!--User getUserById (@Param("id") int id) ;--> <select id="getUserById" resultType="User" > select * from t_user where id = #{id} </select>
7.2 查询一个List集合
1 2 3 4 5 <!--List<User> getUserList () ;--> <!--resultType中的类型为List集合中的元素类型,而不是List类型--> <select id="getUserList" resultType="User" > select * from t_user </select>
7.3 查询单个数据
1 2 3 4 5 6 7 8 9 10 int getCount () ;
1 2 3 4 <!--int getCount () ;--> <select id="getCount" resultType="_integer" > select count (id) from t_user </select>
7.4 查询一条数据为map集合
1 2 3 4 5 6 7 Map<String, Object> getUserToMap (@Param("id") int id) ;
1 2 3 4 5 6 7 <!--Map<String, Object> getUserToMap (@Param("id") int id) ;--> <select id="getUserToMap" resultType="map" > select * from t_user where id = #{id} </select> <!--结果:{password=123456 , sex=男, id=1 , age=23 , username=admin}-->
7.5 查询多条数据为map集合
1 2 3 4 5 6 7 8 方法一 List<Map<String, Object>> getAllUserToMap () ;
1 2 3 4 5 6 7 8 9 10 11 12 <!--Map<String, Object> getAllUserToMap () ;--> <select id="getAllUserToMap" resultType="map" > select * from t_user </select> <!-- 结果: [{password=123456 , sex=男, id=1 , age=23 , username=admin}, {password=123456 , sex=男, id=2 , age=23 , username=张三}, {password=123456 , sex=男, id=3 , age=23 , username=张三}] -->
方法二
1 2 3 4 5 6 7 8 @MapKey("id") Map<String, Object> getAllUserToMap () ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 * <!--Map<String, Object> getAllUserToMap () ;--> <select id="getAllUserToMap" resultType="map" > select * from t_user </select> <!-- 结果: { 1 ={password=123456 , sex=男, id=1 , age=23 , username=admin}, 2 ={password=123456 , sex=男, id=2 , age=23 , username=张三}, 3 ={password=123456 , sex=男, id=3 , age=23 , username=张三} } -->
7.6 使用${}的几种情况
7.6.1 模糊查询
1 2 3 4 5 6 7 8 List<User> getUserByLike (@Param("username") String username) ;
1 2 3 4 5 6 <!--List<User> getUserByLike (@Param("username") String username) ;--> <select id="getUserByLike" resultType="User" > <!--select * from t_user where username like '%${mohu}%' --> <!--select * from t_user where username like concat ('%' ,#{mohu},'%' ) --> select * from t_user where username like "%" #{mohu}"%" </select>
其中select * from t_user where username like “%”#{mohu}"%"是最常用的
7.6.2 批量删除
只能使用${},如果使用#{},则解析后的sql语句为delete from t_user where id in (‘1,2,3’),这样是将1,2,3看做是一个整体,只有id为1,2,3的数据会被删除。正确的语句应该是delete from t_user where id in (1,2,3),或者delete from t_user where id in (‘1’,‘2’,‘3’)
1 2 3 4 5 6 7 8 int deleteMore (@Param("ids") String ids) ;
1 2 3 <delete id="deleteMore" > delete from t_user where id in (${ids}) </delete>
1 2 3 4 5 6 7 8 @Test public void deleteMore () {SqlSession sqlSession = SqlSessionUtils.getSqlSession();SQLMapper mapper = sqlSession.getMapper(SQLMapper.class);int result = mapper.deleteMore("1,2,3,8" );System.out.println(result); }
7.6.3 动态设置表名
只能使用${},因为表名不能加单引号
1 2 3 4 5 6 7 8 9 10 11 List<User> getUserByTable (@Param("tableName") String tableName) ;
1 2 3 4 <!--List<User> getUserByTable (@Param("tableName") String tableName) ;--> <select id="getUserByTable" resultType="User" > select * from ${tableName} </select>
7.6.4 添加功能获取自增的主键
使用场景
t_clazz(clazz_id,clazz_name)
t_student(student_id,student_name,clazz_id)
添加班级信息
获取新添加的班级的id
为班级分配学生,即将某学的班级id修改为新添加的班级的id
在mapper.xml中设置两个属性
useGeneratedKeys:设置使用自增的主键
keyProperty:因为增删改有统一的返回值是受影响的行数,因此只能将获取的自增的主键放在传输的参数user对象的某个属性中
1 2 3 4 5 6 7 void insertUser (User user) ;
1 2 3 4 <!--void insertUser (User user) ;--> <insert id="insertUser" useGeneratedKeys="true" keyProperty="id" > insert into t_user values (null ,#{username},#{password},#{age},#{sex},#{email}) </insert>
1 2 3 4 5 6 7 8 9 10 @Test public void insertUser () {SqlSession sqlSession = SqlSessionUtils.getSqlSession();SQLMapper mapper = sqlSession.getMapper(SQLMapper.class);User user = new User (null , "ton" , "123" , 23 , "男" , "123@321.com" );mapper.insertUser(user); System.out.println(user); }
八、 自定义映射resultMap
8.1 resultMap处理字段和属性的映射关系
resultMap:设置自定义映射
属性:
id:表示自定义映射的唯一标识,不能重复
type:查询的数据要映射的实体类的类型
子标签:
id:设置主键的映射关系
result:设置普通字段的映射关系
子标签属性:
property:设置映射关系中实体类中的属性名
column:设置映射关系中表中的字段名
若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射,即使字段名和属性名一致的属性也要映射,也就是全部属性都要列出来
1 2 3 4 5 6 7 - <resultMap id ="empResultMap" 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 ="email" column="email" ></result > - </resultMap>
1 2 3 4 <select id ="getAllEmp" resultMap ="empResultMap" > select * from t_emp </select >
若字段名和实体类中的属性名不一致,但是字段名符合数据库的规则(使用_),实体类中的属性名符合Java的规则(使用驼峰),此时也可通过以下两种方式处理字段名和实体类中的属性的映射关系
可以通过为字段起别名的方式,保证和实体类中的属性名保持一致
1 2 3 4 <select id ="getAllEmp" resultType ="Emp" > select eid,emp_name empName,age,sex,email from t_emp </select >
可以在MyBatis的核心配置文件中的 setting 标签中,设置一个全局配置信息mapUnderscoreToCamelCase,可以在查询表中数据时,自动将_类型的字段名转换为驼峰,例如:字段名user_name,设置了mapUnderscoreToCamelCase,此时字段名就会转换为userName。
1 2 3 <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> </settings >
8.2 多对一映射处理
查询员工信息以及员工所对应的部门信息
1 2 3 4 5 6 7 8 9 public class Emp { private Integer eid; private String empName; private Integer age; private String sex; private String email; private Dept dept; }
8.2.1 级联方式处理映射关系
对应的实体类要有 get 方法
1 2 3 4 5 6 7 8 9 <resultMap id ="empAndDeptResultMapOne" 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 ="email" column="email" ></result > <result property ="dept.did" column="did" ></result > <result property ="dept.deptName" column="dept_name" ></result > </resultMap>
1 2 3 4 5 6 7 8 9 10 11 12 13 <select id ="getEmpAndDept" resultMap ="empAndDeptResultMapOne" > select * from t_emp left join t_dept on t_emp.eid = t_dept.did where t_emp.eid = # {eid} </select >
8.2.2 使用association处理映射关系
association:处理多对一的映射关系
property:需要处理多对的映射关系的属性名
javaType:该属性的类型
1 2 3 4 5 6 7 8 9 10 11 <resultMap id ="empAndDeptResultMapTwo" 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 ="email" column="email" ></result > <association property ="dept" javaType="Dept" > <id property ="did" column="did" ></id > <result property ="deptName" column="dept_name" ></result > </association> </resultMap>
1 2 3 4 <select id ="getEmpAndDept" resultMap ="empAndDeptResultMapTwo" > select * from t_emp left join t_dept on t_emp.eid = t_dept.did where t_emp.eid = # {eid} </select >
8.2.3 分步查询
查询员工信息
select:设置分布查询的sql的唯一标识(namespace.SQLId或mapper接口的全类名.方法名)
column:设置分步查询的条件
//EmpMapper里的方法
1 2 3 4 5 6 7 8 9 1 . Emp getEmpAndDeptByStepOne (@Param ("eid" ) Integer eid);
select * from t_emp where eid = #{eid}
1 2 3 4 5 6 7 8 2 . 查询部门信息 // DeptMapper里的方法
2. /**
通过分步查询,员工及所对应的部门信息
分步查询第二步:通过did查询员工对应的部门信息
@param
@return com.atguigu.mybatis.pojo.Emp
@date 2023/4/27 20:23
*/
Dept getEmpAndDeptByStepTwo(@Param(“did”) Integer did);
select * from t_dept where did = #{did}
public class Dept {
private Integer did;
private String deptName;
private List emps;
//...构造器、get、set方法等
}
1 2 3 4 5 6 7 8 #### 8.3 .1 collection collection:用来处理一对多的映射关系 ofType:表示该属性对应的集合中存储的数据的类型
select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did = #{did}
1 2 3 4 5 6 7 8 9 #### 8.3 .2 分步查询 1. 查询部门信息
1. /**
通过分步查询,查询部门及对应的所有员工信息
分步查询第一步:查询部门信息
@param did
@return com.atguigu.mybatis.pojo.Dept
@date 2023/4/27 22:04
*/
Dept getDeptAndEmpByStepOne(@Param(“did”) Integer did);
select * from t_dept where did = #{did}
1 2 3 4 5 6 2. 根据部门id 查询部门中的所有员工
2. /**
通过分步查询,查询部门及对应的所有员工信息
分步查询第二步:根据部门id查询部门中的所有员工
@param did
@return java.util.List<com.atguigu.mybatis.pojo.Emp>
@date 2023/4/27 22:10
*/
List getDeptAndEmpByStepTwo(@Param(“did”) Integer did);
select * from t_emp where did = #{did}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ### 8.4 延迟加载 分步查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息: lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载 aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载 此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType=“lazy(延迟加载)|eager(立即加载)”
@Test
public void getEmpAndDeptByStepOne() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptByStepOne(1);
System.out.println(emp.getEmpName());
}
1 2 3 4 5 关闭延迟加载,两条SQL 语句都运行了 开启延迟加载,只运行获取emp的SQL 语句
@Test
public void getEmpAndDeptByStepOne() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptByStepOne(1);
System.out.println(emp.getEmpName());
System.out.println("----------------");
System.out.println(emp.getDept());
}
1 2 3 开启后,需要用到查询dept的时候才会调用相应的SQL 语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ## 九、 动态SQL (重点) Mybatis框架的动态SQL 技术是一种根据特定条件动态拼装SQL 语句的功能,它存在的意义是为了解决拼接SQL 语句字符串时的痛点问题 ### 9.1 if if标签可通过 test 属性(即传递过来的数据)的表达式进行判断,若表达式的结果为true ,则标签中的内容会执行;反之标签中的内容不会执行 在where 后面添加一个恒成立条件1 = 1 这个恒成立条件并不会影响查询的结果 这个1 = 1 可以用来拼接and 语句,例如:当empName为 null 时 如果不加上恒成立条件,则SQL 语句为select * from t_emp where and age = ? and sex = ? and email = ?,此时where 会与and 连用,SQL 语句会报错 如果加上一个恒成立条件,则SQL 语句为select * from t_emp where 1 = 1 and age = ? and sex = ? and email = ?,此时不报错
select * from t_emp where 1=1
and emp_name = #{empName}
and age = #{age}
and sex = #{sex}
and email = #{email}
1 2 3 4 5 6 7 8 9 10 11 ### 9.2 where where 和 if 一般结合使用:若where 标签中的 if 条件都不满足,则where 标签没有任何功能,即不会添加where 关键字 若where 标签中的if 条件满足,则where 标签会自动添加where 关键字,并将条件最前方多余的and/or去掉
select * from t_emp
emp_name = #{empName}
and age = #{age}
and sex = #{sex}
and email = #{email}
1 2 3 4 5 6 注意:where 标签不能去掉条件后多余的and/or
emp_name = #{empName} and
age = #{age}
1 2 3 4 5 6 7 8 9 10 11 12 13 ### 9.3 trim trim 用于去掉或添加标签中的内容常用属性: prefix:在trim 标签中的内容的前面添加某些内容 suffix:在trim 标签中的内容的后面添加某些内容 prefixOverrides:在trim 标签中的内容的前面去掉某些内容 suffixOverrides:在trim 标签中的内容的后面去掉某些内容 若trim 中的标签都不满足条件,则 trim 标签没有任何效果,也就是只剩下select * from t_emp
select * from t_emp
emp_name = #{empName} and
age = #{age} and
sex = #{sex} or
email = #{email}
@Test
public void getEmpByCondition() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
List emps= mapper.getEmpByCondition(new Emp(null, "张三", null, null, null, null));
System.out.println(emps);
}
1 2 3 4 5 6 7 8 9 choose、when 、otherwise 相当于if ...else if ..else when 至少要有一个,otherwise 至多只有一个
select * from t_emp
emp_name = #{empName}
age = #{age}
sex = #{sex}
email = #{email}
did = 1
@Test
public void getEmpByChoose() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
List emps = mapper.getEmpByChoose(new Emp(null, "张三", 23, "男", "123@qq.com", null));
System.out.println(emps);
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 相当于if a else if b else if c else d,只会执行其中一个 属性: collection:设置要循环的数组或集合 item :表示集合或数组中的每一个数据separator:设置循环体之间的分隔符,分隔符前后默认有一个空格,如, open :设置foreach标签中的内容的开始符close :设置foreach标签中的内容的结束符批量删除
delete from t_emp where eid in
#{eid}
@Test
public void deleteMoreByArray() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
int result = mapper.deleteMoreByArray(new Integer[]{6, 7, 8, 9});
System.out.println(result);
}
insert into t_emp values
(null,#{emp.empName},#{emp.age},#{emp.sex},#{emp.email},null)
@Test
public void insertMoreByList() {
SqlSession sqlSession = SqlSessionUtils.getSqlSession();
DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
Emp emp1 = new Emp(null,"a",1,"男","123@321.com",null);
Emp emp2 = new Emp(null,"b",1,"男","123@321.com",null);
Emp emp3 = new Emp(null,"c",1,"男","123@321.com",null);
List emps = Arrays.asList(emp1, emp2, emp3);
int result = mapper.insertMoreByList(emps);
System.out.println(result);
}
1 2 3 4 5 6 7 8 9 10 11 ### 9.6 SQL 片段 sql 片段,可以记录一段公共sql 片段,在使用的地方通过 include 标签进行引入声明sql 片段:<sql >标签 <sql id="empColumns">eid,emp_name,age,sex,email</sql > 1 引用sql 片段:<include >标签
select from t_emp
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 ## 十、 MyBatis的缓存 ### 10.1 MyBatis的一级缓存 一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问 使一级缓存失效的四种情况: 不同的SqlSession对应不同的一级缓存 同一个SqlSession但是查询条件不同 同一个SqlSession两次查询期间执行了任何一次增删改操作 同一个SqlSession两次查询期间手动清空了缓存 ### 10.2 MyBatis的二级缓存 二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取 二级缓存开启的条件 在核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置 在映射文件中设置标签<cache /> 二级缓存必须在 SqlSession 关闭或提交之后有效 查询的数据所转换的实体类类型必须实现序列化的接口 使二级缓存失效的情况:两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效 ### 10.3 二级缓存的相关配置 在mapper配置文件中添加的cache标签可以设置一些属性 eviction属性:缓存回收策略 LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。 FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。 SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。 WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 默认的是 LRU flushInterval属性:刷新间隔,单位毫秒 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句(增删改)时刷新 size属性:引用数目,正整数 代表缓存最多可以存储多少个对象,太大容易导致内存溢出 readOnly属性:只读,true/false true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。 false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false ### 10.4 MyBatis缓存查询的顺序 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用 如果二级缓存没有命中,再查询一级缓存 如果一级缓存也没有命中,则查询数据库 SqlSession关闭之后,一级缓存中的数据会写入二级缓存 ### 10.5 整合第三方缓存EHCache(了解) #### 10.5 .1 添加依赖
org.mybatis.caches
mybatis-ehcache
1.2.1
ch.qos.logback
logback-classic
1.2.3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #### 10.5 .2 各个jar包的功能 jar包名称 作用 mybatis-ehcache Mybatis和EHCache的整合包 ehcache EHCache核心包 slf4j-api SLF4J日志门面包 logback-classic 支持SLF4J门面接口的一个具体实现 #### 10.5 .3 创建EHCache的配置文件ehcache.xml 名字必须叫ehcache.xml
1 2 3 4 5 6 7 #### 10.5 .4 设置二级缓存的类型 在xxxMapper.xml文件中设置二级缓存类型
1 2 3 4 5 #### 10.5 .5 加入logback日志 存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。创建logback的配置文件logback.xml,名字固定,不可改变
[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n
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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 #### 10.5.6 EHCache配置文件说明 属性名 是否必须 作用 maxElementsInMemory 是 在内存中缓存的element的最大数目 maxElementsOnDisk 是 在磁盘上缓存的element的最大数目,若是0表示无穷大 eternal 是 设定缓存的elements是否永远不过期。 如果为true,则缓存的数据始终有效, 如果为false那么还要根据timeToIdleSeconds、timeToLiveSeconds判断 overflowToDisk 是 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上 timeToIdleSeconds 否 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时, 这些数据便会删除,默认值是0,也就是可闲置时间无穷大 timeToLiveSeconds 否 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大 diskSpoolBufferSizeMB 否 DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区 diskPersistent 否 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false diskExpiryThreadIntervalSeconds 否 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s, 相应的线程会进行一次EhCache中数据的清理工作 memoryStoreEvictionPolicy 否 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。 默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出 ## 十一、 MyBatis的逆向工程 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的 逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源: Java实体类 Mapper接口 Mapper映射文件 ### 11.1 创建逆向工程的步骤 #### 11.1.1添加依赖和插件 <dependencies > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.9</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13.2</version > <scope > test</scope > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.27</version > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency > </dependencies > <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 > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.27</version > </dependency > </dependencies > </plugin > </plugins > </build > #### 11.1.2 创建MyBatis的核心配置文件
1 2 3 4 5 6 7 #### 11.1 .3 创建逆向工程的配置文件 文件名必须是:generatorConfig.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 #### 11.1 .4 执行MBG插件的generate目标 如果出现报错:Exception getting JDBC Driver,可能是pom.xml中,数据库驱动配置错误 mybatis-generator-maven-plugin插件中的驱动 两者的驱动版本应该相同 执行结果 ### 11.2 QBC #### 11.2 .1 查询 selectByExample:按条件查询,需要传入一个example对象或者null;如果传入一个null,则表示没有条件,也就是查询所有数据 example.createCriteria().xxx:创建条件对象,通过andXXX方法为SQL添加查询添加,每个条件之间是and关系 example.or().xxx:将之前添加的条件通过or拼接其他条件
@Test public void testMBG() 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);
EmpExample example = new EmpExample();
//名字为张三,且年龄大于等于20
example.createCriteria().andEmpNameEqualTo("张三").andAgeGreaterThanOrEqualTo(20);
//或者did不为空
example.or().andDidIsNotNull();
List emps = mapper.selectByExample(example);
emps.forEach(System.out::println);
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #### 11.2 .2 增改 updateByPrimaryKey:通过主键进行数据修改,如果某一个值为null,也会将对应的字段改为null mapper.updateByPrimaryKey(new Emp(1 ,"admin" ,22 ,null,"456@qq.com" ,3 )); updateByPrimaryKeySelective():通过主键进行选择性数据修改,如果某个值为null,则不修改这个字段 mapper.updateByPrimaryKeySelective(new Emp(2 ,"admin2" ,22 ,null,"456@qq.com" ,3 )); ## 十二、 分页插件 ### 12.1 分页插件使用步骤 #### 12.1 .1 添加依赖
com.github.pagehelper
pagehelper
5.2.0
1 2 3 4 5 6 7 #### 12.1 .2 配置分页插件 在MyBatis的核心配置文件(mybatis-config.xml)中配置插件
1 2 3 4 5 6 7 8 9 10 11 ### 12.2 分页插件的使用 #### 12.2 .1 开启分页功能 在查询功能之前使用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 emps = mapper.selectByExample(null);
emps.forEach(System.out::println);
}
1 2 3 4 5 6 7 8 分页测试结果 #### 12.2 .2 分页相关数据 方法一:直接输出
@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);
//访问第一页,每页四条数据
Page page = PageHelper.startPage(1, 4);
List emps = mapper.selectByExample(null);
//在查询到List集合后,打印分页数据
System.out.println(page);
}
1 2 3 4 5 6 7 8 9 分页相关数据: Page{count =true , pageNum =1, pageSize =4, startRow =0, endRow =4, total =8, pages =2, reasonable =false , pageSizeZero =false }[Emp{eid =1, empName ='admin' , age =22, sex ='男' , email ='456@qq.com' , did =3}, Emp{eid =2, empName ='admin2' , age =22, sex ='男' , email ='456@qq.com' , did =3}, Emp{eid =3, empName ='王五' , age =12, sex ='女' , email ='123@qq.com' , did =3}, Emp{eid =4, empName ='赵六' , age =32, sex ='男' , email ='123@qq.com' , did =1}] 1 方法二:使用PageInfo 在查询获取list集合之后,使用PageInfo<T> pageInfo = new PageInfo<>(List<T> 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 emps = mapper.selectByExample(null);
PageInfo page = new PageInfo<>(emps,5);
System.out.println(page);
}
PageInfo{
pageNum=1, pageSize=4, size=4, startRow=1, endRow=4, total=8, pages=2,
list=Page{count=true, pageNum=1, pageSize=4, startRow=0, endRow=4, total=8, pages=2, reasonable=false, pageSizeZero=false}[Emp{eid=1, empName='admin', age=22, sex='男', email='456@qq.com', did=3}, Emp{eid=2, empName='admin2', age=22, sex='男', email='456@qq.com', did=3}, Emp{eid=3, empName='王五', age=12, sex='女', email='123@qq.com', did=3}, Emp{eid=4, empName='赵六', age=32, sex='男', email='123@qq.com', did=1}],
prePage=0, nextPage=2, isFirstPage=true, isLastPage=false, hasPreviousPage=false, hasNextPage=true, navigatePages=5, navigateFirstPage=1, navigateLastPage=2, navigatepageNums=[1, 2]}
```
其中list中的数据等同于方法一中直接输出的page数据
常用数据:
pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页的页码数
navigatepageNums:导航分页的页码,[1,2,3,4,5]