上一篇Blog了解了基本的MyBatis操作后,我们本篇来学习一下如何实现一些较为高级的操作。分别是返回值的模型映射、日志处理以及分页查询的实现。
模型映射
如果是一个初始化的项目,我们当然尽量的让我们的数据对象,也就是Data Model和数据表中的字段名称保持一致,但是要维护一些老的项目或者说确实有一些我们认为名称应该有所区别的,就需要做一个映射了,类似Hibernate里的Person.hbm.xml
文件,我们的Mapper文件也需要做返回结果的映射。
命名不一致的问题
首先我们在上篇【MyBatis学习笔记 二】MyBatis基本操作及配置解析 Blog的基础上继续做处理,我们在库内增加一个字段:hobby
,兴趣爱好,并给两个字段设置值:
同时给我们的类Person增加属性,但是命名为interests
:
package com.example.MyBatis.dao.model; import lombok.Data; /* * person表对应对象 * */ @Data public class Person { private int id; private String username; private String password; private int age; private int phone; private String email; private String interests; }
接下来在我们的处理方法中进行查询:
<select id="getPersonList" resultType="com.example.MyBatis.dao.model.Person"> select * from person </select>
通过测试类进行测试:
@Test public void test(){ //1.获取SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); //2.执行SQL PersonDao personDao = sqlSession.getMapper(PersonDao.class); List<Person> personList = personDao.getPersonList(); for (Person person : personList) { System.out.println(person); } //关闭sqlSession sqlSession.close(); }
返回结果如下:
可以看到由于字段名不对称,无法获取,mybatis会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 去对应的实体类中查找相应列名的set方法设值 , 由于找不到interests的set方法, 所以返回null。我们调整查询语句如下:
<select id="getPersonList" resultType="com.example.MyBatis.dao.model.Person"> select username,password,age,phone,email,hobby as interests from person </select>
通过hobby as interests
设置别名,确定返回属性为interests,然后再去找对应的set方法就可以了:
使用ResultMap解决
通过设置别名的方式确实可以解决,但是每个方法都需要设置,如果不一致的字段多还需要写大量冗余的sql代码,所以使用ResultMap手动映射来解决这个问题更好。它相当于数据表字段和模型直接的一个映射关系配置:
<!-- id确定返回map, type为模型全类名 --> <resultMap id="PersonMap" type="com.example.MyBatis.dao.model.Person"> <!-- id为主键 --> <id column="id" property="id"/> <!-- column写错会因为查到数据匹配不上,给默认值, type写错会报错,找不到属性--> <result column="age" property="age"/> <!-- column是数据库表的列名 , property是对应实体类的属性名 --> <result column="hobby" property="interests"/> </resultMap> <select id="getPersonList" resultMap="PersonMap"> select * from person </select>
以上我们可以发现,其实不写所有的属性也可以,也就是优先走配置,如果配置没有,还是会走自动映射去找属性的,所以对于命名一致的数据字段和类的属性就无需再加了
但是为了规范和不易出错,我们最好把对应关系都写全了,这样也能对全表字段有正确认知:
<!-- id确定返回map, type为模型全类名 --> <resultMap id="PersonMap" type="com.example.MyBatis.dao.model.Person"> <!-- id为主键 --> <id column="id" property="id"/> <result column="username" property="username"/> <result column="password" property="password"/> <result column="age" property="age"/> <result column="phone" property="phone"/> <!-- column写错会因为查到数据匹配不上,给默认值, type写错会报错,找不到属性--> <result column="email" property="email"/> <!-- column是数据库表的列名 , property是对应实体类的属性名 --> <result column="hobby" property="interests"/> </resultMap> <!--注意返回类型是resultMap而不是resultType --> <select id="getPersonList" resultMap="PersonMap"> select * from person </select>
日志处理
如果一个数据库相关的操作出现了问题,我们可以根据输出的SQL语句快速排查问题。对于以往的开发过程,我们会经常使用到debug模式来调节,跟踪我们的代码执行过程。但是现在使用Mybatis是基于接口,配置文件的源代码通过反射执行,我们无法debug。因此,我们必须选择日志工具来作为我们开发,调节程序的工具。
日志工具分类
Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:
1. SLF4J 2. Apache Commons Logging 3. Log4j-2 4. Log4j 5. JDK logging
具体选择哪个日志实现工具由MyBatis的内置日志工厂确定。它会按优先级使用最先找到的,如果一个都未找到,日志功能就会被禁用。我们可以通过核心配置文件mybatis-config.xml
进行日志配置【注意settings节点顺序】:
<?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核心配置文件--> <configuration> <!--导入properties文件--> <properties resource="properties/db.properties"/> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mappers/personMapper.xml"/> </mappers> </configuration>
然后我们再执行单元测试就可以看到打印出日志记录了:
使用Log4j进行日志处理
什么是Log4j?Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件;可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程;可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
1 导入Log4j的Maven依赖
在pom.xml中导入日志依赖,在maven中搜索到然后选择最新版本号:
导入方式如下:
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
2 配置log4j.properties文件
在resources下配置文件:log4j.properties,只要是该目录下即可:
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 # 设置日志输出级别以及输出目的地,可以设置多个输出目的地,开发环境下,日志级别要设置成DEBUG或者ERROR # 前面写日志级别,逗号后面写输出目的地 # log4j.rootLogger = [level],appenderName1,appenderName2,… log4j.rootLogger=Debug,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender # 输出到控制台 log4j.appender.console.Target = System.out # 指定控制台输出日志级别 log4j.appender.console.Threshold=DEBUG # 默认值是 true, 表示是否立即输出 log4j.appender.console.ImmediateFlush = true # 设置编码方式 log4j.appender.console.Encoding = UTF-8 # 日志输出布局 log4j.appender.console.layout = org.apache.log4j.PatternLayout # 如果日志输出布局为PatternLayout 自定义级别,需要使用ConversionPattern指定输出格式 log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender # 指定输出文件路径 log4j.appender.file.File=./log/tml.log # 指定单个日志文件最大值 log4j.appender.file.MaxFileSize=10mb # 指定输出日志级别 log4j.appender.file.Threshold=DEBUG # 默认值是 true, 表示是否立即输出 log4j.appender.file.ImmediateFlush = true # 设置编码方式 log4j.appender.file.Encoding = UTF-8 # 日志输出布局 log4j.appender.file.layout=org.apache.log4j.PatternLayout # 如果日志输出布局为PatternLayout 自定义级别,需要使用ConversionPattern指定输出格式 log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sq1.PreparedStatement=DEBUG
DEBUG指的是日志输出级别,一共有 7 个级别(OFF、 FATAL、 ERROR、 WARN、 INFO、 DEBUG、 ALL),一般常用的日志输出级别分别为 DEBUG、 INFO、 ERROR 以及 WARN,分别表示 “调试级别”、 “标准信息级别”、 “错误级别”、 “异常级别”。
- 如果需要查看程序运行的详细步骤信息,一般选择 “DEBUG” 级别,因为该级别在程序运行期间,会在控制台才打印出底层的运行信息,以及在程序中使用 Log 对象打印出调试信息。
- 如果是日常的运行,选择 INFO 级别,该级别会在控制台打印出程序运行的主要步骤信息
- ERROR和 WARN级别分别代表 不影响程序运行的错误事件和 潜在的错误情形。
3 测试类查看日志输出
引入org.apache.log4j.Logger
日志包,进行测试:
//注意导包:org.apache.log4j.Logger static Logger logger = Logger.getLogger(PersonTest.class); @Test public void test(){ logger.info("info:进入test方法"); logger.debug("debug:进入test方法"); logger.error("error: 进入test方法"); //1.获取SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); //2.执行SQL PersonDao personDao = sqlSession.getMapper(PersonDao.class); List<Person> personList = personDao.getPersonList(); for (Person person : personList) { System.out.println(person); } //关闭sqlSession sqlSession.close(); }
实现效果如下:
在本地日志文件中也可以看到:
分页查询
在学习mybatis等持久层框架的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进行查询操作,如果查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。分页语法如下:
#为了检索从某一个偏移量开始的指定条数 SELECT * FROM table LIMIT stratIndex,pageSize SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15 #为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1: SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last. #如果只给定一个参数,它表示返回最大的记录行数目,换句话说,LIMIT n 等价于 LIMIT 0,n。 SELECT * FROM table LIMIT 5; //检索前 5 个记录行
1 调整PersonDao接口定义
首先我们可以在PersonDao接口中增加一个方法getPersonListPage用来分页查找数据:
List<Person> getPersonListPage(Map<String,Integer> map);
2 编写personMapper.xml
其次依据PersonDao接口中增加的方法我们可以在personMapper中进行sql语句编写来实现:
<select id="getPersonListPage" parameterType="map" resultMap="PersonMap"> select * from person limit #{startIndex},#{pageSize} </select>
3 编写单元测试实现
最后我们编写单元测试查看实现效果:
@Test public void testGetPersonListPage(){ //1.获取SqlSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); //2.执行SQL PersonDao personDao = sqlSession.getMapper(PersonDao.class); int currentPage = 1; //第几页 int pageSize = 1; //每页显示几个 Map<String,Integer> map = new HashMap<>(); map.put("startIndex",(currentPage-1)*pageSize); map.put("pageSize",pageSize); List<Person> personList = personDao.getPersonListPage(map); for (Person person : personList) { System.out.println(person); } //关闭sqlSession sqlSession.close(); }
实现效果如下,获取到了第一页共一条数据:
总结一下
本篇Blog比较杂,主要了解了三部分内容:结果集的映射模型、日志处理以及分页查询这三种我们日常使用时的Mybatis实现方式,看来映射模型也是需要的,和Hibernate类似,现在看来最大的不同就是,Mybatis既运用了SQL的灵活又实现了对象的封装,而Hibernate则是对数据库语法SQL做了完全的封装,自己实现了HQL语句,那么其实比起自己封装的,使用原生的SQL语法更能降低理解难度。