前言
上篇文章主要是对Mybatis的增删改查以及核心配置解析的了解。本篇文章将堆SQL语句进行分析,主要是对结果集映射的了解,同时还有怎样配置Mybatis日志、分页如何处理等等。
结果集映射——ResultMap
结果集映射顾名思义就是对结果集进行一系列的处理,得到我们想要的数据。在SQL查询,尤其是复杂查询中,所得出来结果的列名在映射实体类中并不存在,导致在Java业务层中无法得到对应数据,这种情况就需要结果集映射来完成,简单来说就是为结果列名取别名
,使得查询出来的结果类名能够对应上实体类上的属性名。
userMapper.java文件
public interface userMapper {
//设计场景:查询用户表所有用户的所有信息,要求查询出来的列名使用中文
public List<User> addUserList();
}
userMapper.xml文件
<select id="addUserList" resultMap="userMapper">
select id '主键', name '用户名', pwd '密码' from user;
</select>
异常:
java.lang.IllegalArgumentException: Result Maps collection does not contain value for com.mybatis.dao.userMapper.userMapper
有上述代码可以看到,并没有设置resultType
,同时也报了异常,异常的意思是“结果映射集合不包含的值”,出现这个异常大概率是结果集找不到对应的映射内容,所以需要配置resultMap
标签。
<resultMap id="userMapper" type="user">
<result property="id" column="主键"/>
<result property="name" column="用户名"/>
<result property="pwd" column="密码"/>
</resultMap>
resultMap
的要求是id需要和对应select
标签的resultMap
一样,而type属性要求是返回的类型,而resultMap
的作用就是给列和属性名进行映射,将属性名和当前select语句查询出来的表的列名
一一对应,这样后续在使用时能够有值。
测试.java:
@Test
public void ResultMapperTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
userMapper mapper = sqlSession.getMapper(userMapper.class);
List<User> users = mapper.addUserList();
for(User user : users){
System.out.println(user.toString());
}
}
日志
在Mybatis开发中难免会出现以下不易察觉的错误,尤其是SQL语句的错误,曾经我就因为把where
错写成when
,让我找错误找了半天。这种情况需要日志
来排查,Mybatis开启日志非常简单,需要在核心配置文件的setting
中设置开启即可。
mybatis-config.xml文件:
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
运行结果:
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 708890004.
==> Preparing: select id '主键', name '用户名', pwd '密码' from user;
==> Parameters:
<== Columns: 主键, 用户名, 密码
<== Row: 1, 枫华, 123456
<== Row: 3, 轩五, 123456
<== Row: 4, 天使, 1614700
<== Total: 3
User(id=1, name=枫华, pwd=123456)
User(id=3, name=轩五, pwd=123456)
User(id=4, name=天使, pwd=1614700)
从上述运行结果中发现日志能够把SQL语句以及结果很清晰的复现出来,甚至还将查询出来的表的信息发了出来。这是运行成功的结果,如果出现异常日志会怎样分析呢?
userMapper.xml文件
<select id="selectUser" resultType="user" parameterType="int">
select id ids from user where id=#{id}
</select>
IDEA给的异常信息:
java.lang.NullPointerException
at MybatisTest.selectById(MybatisTest.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
像这种因为属性映射问题IDEA给的异常信息很模糊,因为这本质上来讲不算异常,SQL语句查出来了,只是不知道往哪放而已。有的时候什么因为映射不成功出现空指针也未可知,程序员这个时候往往项目到是SQL语句的错误,这时候日志的辅助工作就显得尤为重要。
日志:
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 1485697819.
==> Preparing: select id ids from user where id=?
==> Parameters: 1(Integer)
<== Columns: ids
<== Row: 1
<== Total: 1
java.lang.NullPointerException
at MybatisTest.selectById(MybatisTest.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
开头的SQL语句以及查询出来的结果很容易就可以让人发现错误所在。
Log4j日志
Log4j日志是比较常使用的日志插件,在mybatis中使用日志插件也是需要配置,与STDOUT_LOGGING
相比,难点在于Log4j的配置,但是Log4j功能也更全。
第一步:导入依赖,想要使用Log4j,就先需要导入Log4j的依赖或者jar包
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
第二步:在核心配置文件中的settings
中设置日志value为LOG4J
,注意这个value必须一模一样,多一个空格都不行
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
第三步:创建log4j.properties
配置文件,对log4j进行功能配置
######################## 将等级为DEBUG的日志信息输出到consoleh和file两个目的地, console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
########################控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
#在控制台输出
log4j.appender.console.Target = System.out
#在DEBUG级别输出
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/kuang.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.sql.PreparedStatement=DEBUG
第三步:使用
public static final Logger logger = LoggerFactory.getLogger(MybatisTest.class);
分页
mybatis的分页其实可以采用原始SQL语句的方式,比较特殊的就是多了一些分页的方法,比如Rowbounds
和PageHelper
。
在本篇文章中只说明limit和Rowbounds,因为PageHelper插件需要Spring。
limit
userMapper.java
public interface userMapper {
//使用limit完成分页
//start表示从哪里开始分页,pageSize表示每页有多少记录
public List<User> userListByLimit(@Param("start") int start, @Param("pageSize") int PageSize);
}
userMapper.xml
<select id="userListByLimit" resultType="user">
select * from user limit #{start}, #{pageSize};
</select>
测试:
@Test
public void userListByLimitTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
userMapper mapper = sqlSession.getMapper(userMapper.class);
List<User> users = mapper.userListByLimit(0, 2);
for (User user : users) {
System.out.println(user.toString());
}
}
Rowbounds
userMapper.java
public interface userMapper {
//使用Rowbounds
public List<User> userListByRowbounds(RowBounds rowBounds);
}
userMapper.xml
<select id="userListByRowbounds" resultType="user">
select * from user;
</select>
测试:
@Test
public void userListByRowbounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
userMapper mapper = sqlSession.getMapper(userMapper.class);
List<User> users = mapper.userListByRowbounds(new RowBounds(0, 2));
for (User user : users) {
System.out.println(user.toString());
}
}
区别
limit
和Rowbounds
二者区别就是解决问题的方式不同,limit是从SQL语句上解决问题,Rowbounds是从Java层面解决问题。
在实际开发中不建议使用Rowbounds
来解决分页问题,原因是Rowbounds是将全部数据查询下来后,再根据所填的数据来进行分页,在大数据量的情况下这是十分影响效率的。
总结
- 结果集映射要求resultMap要和中的id属性一样
- 结果集映射主要解决的是
当前查询语句所查询出来的表的列名
和属性名
不一致的问题,并非是数据库列名和属性名不一致的问题,这点要注意 - 日志的使用在mybatis阶段主要是用来查看SQL语句,以及SQL语句执行结果
- 分页主要有三种:limit、Rowbounds,PageHelper
- 不推荐使用Rowbounds,因为它是全表查询,大数据量的情况下效率低下