上一篇Blog了解了基本的MyBatis操作后,我们本篇来学习一下如何实现一些较为高级的操作。分别是返回值的模型映射、日志处理以及分页查询的实现。
模型映射
如果是一个初始化的项目,我们当然尽量的让我们的数据对象,也就是Data Model和数据表中的字段名称保持一致,但是要维护一些老的项目或者说确实有一些我们认为名称应该有所区别的,就需要做一个映射了,类似Hibernate里的Person.hbm.xml 文件,我们的Mapper文件也需要做返回结果的映射。
命名不一致的问题
首先我们在上篇【MyBatis学习笔记 二】MyBatis基本操作及配置解析 Blog的基础上继续做处理,我们在库内增加一个字段:hobby ,兴趣爱好,并给两个字段设置值: 
同时给我们的类Person增加属性,但是命名为interests :
package com.example.MyBatis.dao.model;
import lombok.Data;
@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(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
PersonDao personDao = sqlSession.getMapper(PersonDao.class);
List<Person> personList = personDao.getPersonList();
for (Person person : personList) {
System.out.println(person);
}
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手动映射来解决这个问题更好。它相当于数据表字段和模型直接的一个映射关系配置:
<resultMap id="PersonMap" type="com.example.MyBatis.dao.model.Person">
<id column="id" property="id"/>
<result column="age" property="age"/>
<result column="hobby" property="interests"/>
</resultMap>
<select id="getPersonList" resultMap="PersonMap">
select * from person
</select>
以上我们可以发现,其实不写所有的属性也可以,也就是优先走配置,如果配置没有,还是会走自动映射去找属性的,所以对于命名一致的数据字段和类的属性就无需再加了  但是为了规范和不易出错,我们最好把对应关系都写全了,这样也能对全表字段有正确认知:
<resultMap id="PersonMap" type="com.example.MyBatis.dao.model.Person">
<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"/>
<result column="email" property="email"/>
<result column="hobby" property="interests"/>
</resultMap>
<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>
<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的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
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
log4j.appender.file.layout=org.apache.log4j.PatternLayout
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 日志包,进行测试:
static Logger logger = Logger.getLogger(PersonTest.class);
@Test
public void test(){
logger.info("info:进入test方法");
logger.debug("debug:进入test方法");
logger.error("error: 进入test方法");
SqlSession sqlSession = MybatisUtils.getSqlSession();
PersonDao personDao = sqlSession.getMapper(PersonDao.class);
List<Person> personList = personDao.getPersonList();
for (Person person : personList) {
System.out.println(person);
}
sqlSession.close();
}
实现效果如下:  在本地日志文件中也可以看到:

分页查询
在学习mybatis等持久层框架的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进行查询操作,如果查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。分页语法如下:
SELECT * FROM table LIMIT stratIndex,pageSize
SELECT * FROM table LIMIT 5,10;
SELECT * FROM table LIMIT 95,-1;
SELECT * FROM table LIMIT 5;
1 调整PersonDao接口定义
首先我们可以在PersonDao接口中增加一个方法getPersonListPage用来分页查找数据:
package com.example.MyBatis.dao.mapper;
import com.example.MyBatis.dao.model.Person;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface PersonDao {
List<Person> getPersonList();
Person getPersonByUsername(@Param("username")String username, @Param("password")String password);
Person getPersonByMap(Map<String,Object> map);
int addPerson(Person person);
int updatePerson(Person person);
int deletePerson(@Param("id")int id);
List<Person> getPersonListLike(String username);
List<Person> getPersonListPage(Map<String,Integer> map);
}
2 编写personMapper.xml
其次依据PersonDao接口中增加的方法我们可以在personMapper中进行sql语句编写来实现:
<?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.example.MyBatis.dao.mapper.PersonDao">
<resultMap id="PersonMap" type="com.example.MyBatis.dao.model.Person">
<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"/>
<result column="email" property="email"/>
<result column="hobby" property="interests"/>
</resultMap>
<select id="getPersonList" resultMap="PersonMap">
select * from person
</select>
<select id="getPersonByUsername" resultMap="PersonMap" >
select * from person where password=#{password} and username = #{username}
</select>
<select id="getPersonByMap" resultMap="PersonMap" parameterType="map">
select * from person where password=#{password} and username = #{username}
</select>
<insert id="addPerson" parameterType="com.example.MyBatis.dao.model.Person" >
insert into person (id,username,password,age,phone,email) values (#{id},#{username},#{password},#{age},#{phone},#{email})
</insert>
<update id="updatePerson" parameterType="com.example.MyBatis.dao.model.Person" >
update person set username=#{username},phone=#{phone} where id=#{id}
</update>
<delete id="deletePerson" parameterType="int" >
delete from person where id = #{id}
</delete>
<select id="getPersonListLike" resultMap="PersonMap" >
select * from person where username like "%"#{username}"%"
</select>
<select id="getPersonListPage" parameterType="map" resultMap="PersonMap">
select * from person limit #{startIndex},#{pageSize}
</select>
</mapper>
3 编写单元测试实现
最后我们编写单元测试查看实现效果:
@Test
public void testGetPersonListPage(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
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.close();
}
实现效果如下,获取到了第一页共一条数据: 
总结一下
本篇Blog比较杂,主要了解了三部分内容:结果集的映射模型、日志处理以及分页查询这三种我们日常使用时的Mybatis实现方式,看来映射模型也是需要的,和Hibernate类似,现在看来最大的不同就是,Mybatis既运用了SQL的灵活又实现了对象的封装,而Hibernate则是对数据库语法SQL做了完全的封装,自己实现了HQL语句,那么其实比起自己封装的,使用原生的SQL语法更能降低理解难度。
|