如果数据库操作出现异常需要排错, 日志是最好的助手!
官网列出的 ‘logImpl’ (日志实现), 无默认值, 具体选择哪一个在 settings
中设置:
那么, 要如何使用呢?
测试一下吧:
在mybatis-config.xml 中设置, 无需任何其他设置:
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
运行test输出结果:
D:\Software\jdk1.8.0_71\bin\java.exe -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=56252:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\plugins\junit\lib\junit5-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\plugins\junit\lib\junit-rt.jar;D:\Software\jdk1.8.0_71\jre\lib\charsets.jar;D:\Software\jdk1.8.0_71\jre\lib\deploy.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\access-bridge-64.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\cldrdata.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\dnsns.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\jaccess.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\jfxrt.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\localedata.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\nashorn.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunec.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunjce_provider.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunmscapi.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunpkcs11.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\zipfs.jar;D:\Software\jdk1.8.0_71\jre\lib\javaws.jar;D:\Software\jdk1.8.0_71\jre\lib\jce.jar;D:\Software\jdk1.8.0_71\jre\lib\jfr.jar;D:\Software\jdk1.8.0_71\jre\lib\jfxswt.jar;D:\Software\jdk1.8.0_71\jre\lib\jsse.jar;D:\Software\jdk1.8.0_71\jre\lib\management-agent.jar;D:\Software\jdk1.8.0_71\jre\lib\plugin.jar;D:\Software\jdk1.8.0_71\jre\lib\resources.jar;D:\Software\jdk1.8.0_71\jre\lib\rt.jar;D:\Software-idea\Project-QinJiang\com.zhangcl\mybatis-03\target\test-classes;D:\Software-idea\Project-QinJiang\com.zhangcl\mybatis-03\target\classes;D:\Software-idea\MavenRepository\repo-3.6.1\org\mybatis\mybatis\3.5.4\mybatis-3.5.4.jar;D:\Software-idea\MavenRepository\repo-3.6.1\mysql\mysql-connector-java\5.1.47\mysql-connector-java-5.1.47.jar;D:\Software-idea\MavenRepository\repo-3.6.1\junit\junit\4.12\junit-4.12.jar;D:\Software-idea\MavenRepository\repo-3.6.1\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.zhangcl.test.UserDaoTest,getUserById
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
Class not found: org.jboss.vfs.VFS
JBoss 6 VFS API is not available in this environment.
Class not found: org.jboss.vfs.VirtualFile
VFS implementation org.apache.ibatis.io.JBoss6VFS is not valid in this environment.
Using VFS adapter org.apache.ibatis.io.DefaultVFS
Find JAR URL: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
Not a JAR: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
Reader entry: User.class
Listing file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
Find JAR URL: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo/User.class
Not a JAR: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo/User.class
Reader entry: ���� 1 <
Checking to see if class com.zhangcl.pojo.User matches criteria [is assignable to Object]
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
Sat Apr 11 09:49:23 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Created connection 128359175.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7a69b07]
==> Preparing: select * from mybatis.user where id = ?;
==> Parameters: 2(Integer)
<== Columns: id, name, pwd
<== Row: 2, 张三, 123456
<== Total: 1
User{id=2, name='张三', password='123456'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7a69b07]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@7a69b07]
Returned connection 128359175 to pool.
Process finished with exit code 0
什么是LOG4J
Apache的开源项目, 可以通过它控制日志信息输出目的地: 控制台、文件、GUI组建
可以精确控制每一条日志信息的输出格式
可以精确控制每一条日志信息的输出级别
可以通过配置文件来设置它们, 而不必要修改代码
测试一下吧:
导入Maven依赖
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
在 MyBatis 核心配置文件, 也就是mybatis-config.xml 中配置使用log4j.
要注意顺序性或顺序性报错的提示
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
在 resource 目录下新建LOG4J的配置文件, log4j.properties
#将等级为DEBUG的日志信息输出到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/zhangcl.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
单元测试, 输出结果:
D:\Software\jdk1.8.0_71\bin\java.exe -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=61217:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\plugins\junit\lib\junit5-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\plugins\junit\lib\junit-rt.jar;D:\Software\jdk1.8.0_71\jre\lib\charsets.jar;D:\Software\jdk1.8.0_71\jre\lib\deploy.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\access-bridge-64.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\cldrdata.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\dnsns.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\jaccess.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\jfxrt.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\localedata.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\nashorn.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunec.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunjce_provider.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunmscapi.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunpkcs11.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\zipfs.jar;D:\Software\jdk1.8.0_71\jre\lib\javaws.jar;D:\Software\jdk1.8.0_71\jre\lib\jce.jar;D:\Software\jdk1.8.0_71\jre\lib\jfr.jar;D:\Software\jdk1.8.0_71\jre\lib\jfxswt.jar;D:\Software\jdk1.8.0_71\jre\lib\jsse.jar;D:\Software\jdk1.8.0_71\jre\lib\management-agent.jar;D:\Software\jdk1.8.0_71\jre\lib\plugin.jar;D:\Software\jdk1.8.0_71\jre\lib\resources.jar;D:\Software\jdk1.8.0_71\jre\lib\rt.jar;D:\Software-idea\Project-QinJiang\com.zhangcl\mybatis-03\target\test-classes;D:\Software-idea\Project-QinJiang\com.zhangcl\mybatis-03\target\classes;D:\Software-idea\MavenRepository\repo-3.6.1\log4j\log4j\1.2.17\log4j-1.2.17.jar;D:\Software-idea\MavenRepository\repo-3.6.1\org\mybatis\mybatis\3.5.4\mybatis-3.5.4.jar;D:\Software-idea\MavenRepository\repo-3.6.1\mysql\mysql-connector-java\5.1.47\mysql-connector-java-5.1.47.jar;D:\Software-idea\MavenRepository\repo-3.6.1\junit\junit\4.12\junit-4.12.jar;D:\Software-idea\MavenRepository\repo-3.6.1\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.zhangcl.test.UserDaoTest,getUserById
[org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
[org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
[org.apache.ibatis.io.VFS]-Class not found: org.jboss.vfs.VFS
[org.apache.ibatis.io.JBoss6VFS]-JBoss 6 VFS API is not available in this environment.
[org.apache.ibatis.io.VFS]-Class not found: org.jboss.vfs.VirtualFile
[org.apache.ibatis.io.VFS]-VFS implementation org.apache.ibatis.io.JBoss6VFS is not valid in this environment.
[org.apache.ibatis.io.VFS]-Using VFS adapter org.apache.ibatis.io.DefaultVFS
[org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
[org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
[org.apache.ibatis.io.DefaultVFS]-Reader entry: User.class
[org.apache.ibatis.io.DefaultVFS]-Listing file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
[org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo/User.class
[org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo/User.class
[org.apache.ibatis.io.DefaultVFS]-Reader entry: ���� 1 <
[org.apache.ibatis.io.ResolverUtil]-Checking to see if class com.zhangcl.pojo.User matches criteria [is assignable to Object]
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
Sat Apr 11 12:50:59 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1484171695.
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5876a9af]
[com.zhangcl.dao.UserMapper.getUserById]-==> Preparing: select * from mybatis.user where id = ?;
[com.zhangcl.dao.UserMapper.getUserById]-==> Parameters: 2(Integer)
[com.zhangcl.dao.UserMapper.getUserById]-<== Total: 1
User{id=2, name='张三', password='123456'}
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5876a9af]
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5876a9af]
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 1484171695 to pool.
Process finished with exit code 0
**简单使用, 需要注意: **
导包: 注意不是 java.util 的, 而是 org.apache.Logger 的.
声明Logger对象. 考虑对象作用域问题, 可以把声明语句提升到类成员级别
static Logger logger = Logger.getLogger(UserDaoTest.class);
日志的级别, 常用三种级别:
logger.info("info: 进入了testLog4j()");
logger.debug("debug: 进入了testLog4j()");
logger.error("error: 进入了testLog4j()");
/*
在日志file中输出效果如下:
[INFO][20-04-11[com.zhangcl.test.UserDaoTest]info: 进入了testLog4j()
[DEBUG][20-04-11[com.zhangcl.test.UserDaoTest]debug: 进入了testLog4j()
[ERROR][20-04-11[com.zhangcl.test.UserDaoTest]error: 进入了testLog4j()
*/
为什么要分页?
是SQL层面的分页, 以下是基本语法:
#[syntax] select * from user limit startIndex, pageSize;
select * from user limit 0, 3;
select * from user limit 3; #两者等效
使用 MyBatis 测试一下:
接口: UserMapper.java
/*查 User 分页*/
List<User> getUserWithLimit(Map<String, Integer> map);
映射文件: UserMapper.xml. 这里使用了万能Map, 可以注意一下.
<select id="getUserWithLimit" resultType="User" parameterType="map">
SELECT * FROM user LIMIT #{startIndex}, #{pageSize};
select>
单元测试: UserMapperTest.java
/*查 User Limit*/
@Test
public void getUserWithLimit(){
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex", 1);
map.put("pageSize", 2);
List<User> userList = userMapper.getUserWithLimit(map);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
结果输出: 测试通过
D:\Software\jdk1.8.0_71\bin\java.exe -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=64465:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\plugins\junit\lib\junit5-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\plugins\junit\lib\junit-rt.jar;D:\Software\jdk1.8.0_71\jre\lib\charsets.jar;D:\Software\jdk1.8.0_71\jre\lib\deploy.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\access-bridge-64.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\cldrdata.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\dnsns.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\jaccess.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\jfxrt.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\localedata.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\nashorn.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunec.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunjce_provider.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunmscapi.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunpkcs11.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\zipfs.jar;D:\Software\jdk1.8.0_71\jre\lib\javaws.jar;D:\Software\jdk1.8.0_71\jre\lib\jce.jar;D:\Software\jdk1.8.0_71\jre\lib\jfr.jar;D:\Software\jdk1.8.0_71\jre\lib\jfxswt.jar;D:\Software\jdk1.8.0_71\jre\lib\jsse.jar;D:\Software\jdk1.8.0_71\jre\lib\management-agent.jar;D:\Software\jdk1.8.0_71\jre\lib\plugin.jar;D:\Software\jdk1.8.0_71\jre\lib\resources.jar;D:\Software\jdk1.8.0_71\jre\lib\rt.jar;D:\Software-idea\Project-QinJiang\com.zhangcl\mybatis-03\target\test-classes;D:\Software-idea\Project-QinJiang\com.zhangcl\mybatis-03\target\classes;D:\Software-idea\MavenRepository\repo-3.6.1\log4j\log4j\1.2.17\log4j-1.2.17.jar;D:\Software-idea\MavenRepository\repo-3.6.1\org\mybatis\mybatis\3.5.4\mybatis-3.5.4.jar;D:\Software-idea\MavenRepository\repo-3.6.1\mysql\mysql-connector-java\5.1.47\mysql-connector-java-5.1.47.jar;D:\Software-idea\MavenRepository\repo-3.6.1\junit\junit\4.12\junit-4.12.jar;D:\Software-idea\MavenRepository\repo-3.6.1\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.zhangcl.test.UserDaoTest,getUserWithLimit
[org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
[org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
[org.apache.ibatis.io.VFS]-Class not found: org.jboss.vfs.VFS
[org.apache.ibatis.io.JBoss6VFS]-JBoss 6 VFS API is not available in this environment.
[org.apache.ibatis.io.VFS]-Class not found: org.jboss.vfs.VirtualFile
[org.apache.ibatis.io.VFS]-VFS implementation org.apache.ibatis.io.JBoss6VFS is not valid in this environment.
[org.apache.ibatis.io.VFS]-Using VFS adapter org.apache.ibatis.io.DefaultVFS
[org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
[org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
[org.apache.ibatis.io.DefaultVFS]-Reader entry: User.class
[org.apache.ibatis.io.DefaultVFS]-Listing file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo
[org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo/User.class
[org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/D:/Software-idea/Project-QinJiang/com.zhangcl/mybatis-03/target/classes/com/zhangcl/pojo/User.class
[org.apache.ibatis.io.DefaultVFS]-Reader entry: ���� 1 <
[org.apache.ibatis.io.ResolverUtil]-Checking to see if class com.zhangcl.pojo.User matches criteria [is assignable to Object]
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
Sat Apr 11 14:37:33 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1529060733.
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5b239d7d]
[com.zhangcl.dao.UserMapper.getUserWithLimit]-==> Preparing: SELECT * FROM user LIMIT ?, ?;
[com.zhangcl.dao.UserMapper.getUserWithLimit]-==> Parameters: 1(Integer), 2(Integer)
[com.zhangcl.dao.UserMapper.getUserWithLimit]-<== Total: 2
User{id=2, name='张三', password='null'}
User{id=3, name='李四', password='null'}
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5b239d7d]
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5b239d7d]
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 1529060733 to pool.
Process finished with exit code 0
属于 Java 代码层面的分页, 只作为了解, 据说技术比较老. 以下为基本步骤:
接口. 注意这里没有参数
/*查 User 分页2: rowBounds*/
List<User> getUserWithRowBounds();
Mapper.xml. 注意使用了 resultMap
<resultMap id="UserMap" type="User">
<result column="pwd" property="password">result>
resultMap>
<select id="getUserWithRowBounds" resultMap="UserMap">
SELECT * FROM user;
select>
单元测试
/*分页查User2 用RowBounds*/
@Test
public void getUserWithRowBounds(){
RowBounds rowBounds = new RowBounds(1, 2);
List<User> userList = sqlSession.selectList("com.zhangcl.dao.UserMapper.getUserWithRowBounds", null, rowBounds);
for (User u : userList) {
System.out.println("分页2"+u);
}
sqlSession.close();
}
结果通过.
据说用法和 RowBounds 比较接近, 了解即可.
如果真要学用, 可以直接上官网, 中文并且内容少而简单.
为什么 “面向接口编程” ?
根本目的: 解耦, 定义与实现分离. leader写接口, 码农写实现.
基本步骤
在核心配置文件 mybatis-config.xml 中注册绑定接口类
<mappers>
<mapper class="com.zhangcl.dao.UserMapper">mapper>
mappers>
编写接口, 并且编写相应注解+SQL
@Select("select * from user")
List<User> getUsers();
测试
@Test
public void test01(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.getUsers();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
SqlSession sqlSession = sqlSessionFactory.openSqlSession(true);
参数true
: 如果传入参数true, 则以为这开启自动提交事务
作用: 可以承上启下改名.
SQL中column: pwd
<=> @Param("pwd") String password
<=> POJO 中属性名 private String password
使用原则:
String
@Param
注解修饰基本数据的参数.推荐使用 #{}
, 因为 ${}
不能防止 ‘SQL注入’
用到resultMap
理解: 多个学生共同一个老师
处理思路: 无非两种, 子查询 | 联表查询
理解上有点像子查询. 以下是具体步骤:
准备数据库
CREATE TABLE teacher(
id INT(10) NOT NULL PRIMARY KEY,
NAME VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO teacher(id,NAME) VALUES(1, '秦老师');
CREATE TABLE student(
id INT(10) NOT NULL,
NAME VARCHAR(30) DEFAULT NULL,
tid INT(10) DEFAULT NULL,
PRIMARY KEY(id),
KEY fktid(tid),
CONSTRAINT fktid FOREIGN KEY(tid) REFERENCES teacher(id)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
新建module, 引入lombok的依赖包
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.12version>
dependency>
创建POJO: Student 和 Teacher
package com.zhangcl.pojo;
import lombok.Data;
@Data
public class Teacher {
private int id;
private String name;
}
// ..........................
package com.zhangcl.pojo;
import lombok.Data;
@Data
public class Student {
private int id;
private String name;
private Teacher teacher;
}
创建Mapper.xml: StudentMapper.xml 和 TeacherMapper.xml. 在StudentMapper.xml编写需要的SQL.
起初我只是非常单纯的将resultType
设置如下, 后来出现问题, 是为后话.
<select id="getStudent" resultType="Student">
select * from student;
select>
在mybatis-config.xml文件中设置绑定注册Mapper或者接口类. 这里我们先简单测试一下, 用接口, 所以这里只注册接口绑定.
<mappers>
<mapper class="com.zhangcl.dao.TeacherMapper">mapper>
<mapper class="com.zhangcl.dao.StudentMapper">mapper>
mappers>
注意: 我们这次把两个文件放在resources目录下的对应的com.zhangcl.dao目录下, 最后target输出的mapper文件和mapper类居然在同一个文件夹中!
异常: 之前用的resource + *.xml
的表述, 结果出现Could not found …的异常, 换了之后就解决了
测试
// 1
@Test
public void testGetStudent(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.getStudent();
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
运行结果: teacher的值都是null, 引出难点.
Created connection 957465255.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@3911c2a7]
==> Preparing: select S.id,S.name,T.name from student S, teacher T where S.tid = T.id;
==> Parameters:
<== Columns: id, name, name
<== Row: 1, 小明, 秦老师
<== Row: 2, 小红, 秦老师
<== Row: 3, 小张, 秦老师
<== Row: 4, 小李, 秦老师
<== Row: 5, 小王, 秦老师
<== Total: 5
Student(id=1, name=小明, teacher=null)
Student(id=2, name=小红, teacher=null)
Student(id=3, name=小张, teacher=null)
Student(id=4, name=小李, teacher=null)
Student(id=5, name=小王, teacher=null)
[重点] 针对null的情况, 引出重点, 对resultMap
更复杂的配置
<select id="getStudent" resultMap="StudentTeacher">
select * from student;
select>
<resultMap id="StudentTeacher" type="Student">
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher">association>
resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id=#{id}
select>
写错: 一开始我在teacher的where中写的#{tid}, 结果不行, 按照视频抄写成id, 暂时可以. 原理我也有点蒙蔽.
注意: 这里查询老师的#{tid}
可以随便写
执行结果Teacher有点像嵌套查询, 如下所示:
Created connection 961712517.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@39529185]
==> Preparing: select * from student;
==> Parameters:
<== Columns: id, name, tid
<== Row: 1, 小明, 1
====> Preparing: select * from teacher where id=?
====> Parameters: 1(Integer)
<==== Columns: id, name
<==== Row: 1, 秦老师
<==== Total: 1
<== Row: 2, 小红, 1
<== Row: 3, 小张, 1
<== Row: 4, 小李, 1
<== Row: 5, 小王, 1
<== Total: 5
Student(id=1, name=小明, teacher=Teacher(id=1, name=秦老师))
Student(id=2, name=小红, teacher=Teacher(id=1, name=秦老师))
Student(id=3, name=小张, teacher=Teacher(id=1, name=秦老师))
Student(id=4, name=小李, teacher=Teacher(id=1, name=秦老师))
Student(id=5, name=小王, teacher=Teacher(id=1, name=秦老师))
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@39529185]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@39529185]
Returned connection 961712517 to pool.
先联表查询, 再处理结果
接口: StudentMapper.java
/*2*/
List<Student> getStudent2();
Mapper文件: StudentMapper.xml
两表联查, 查询出结果后映射给一个resultMap
, 重点它的配置:
注意:
result
, 而不是id
Student
中的teacher
属性时相当于嵌套
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid, s.name sname,t.id tid, t.name tname
from student s, teacher t
where s.tid = t.id;
select>
<resultMap id="StudentTeacher2" type="Student">
<result column="sid" property="id">result>
<result column="sname" property="name">result>
<association property="teacher" javaType="Teacher">
<result column="tname" property="name">result>
<result column="tid" property="id">result>
association>
resultMap>
执行结果,
逻辑异常: 一开始老师的id都为0, 实际应该是null, 没有结果. 后在association中配置tid之后就可以了
Created connection 367746789.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@15eb5ee5]
==> Preparing: select s.id sid, s.name sname,t.id tid, t.name tname from student s, teacher t where s.tid = t.id;
==> Parameters:
<== Columns: sid, sname, tid, tname
<== Row: 1, 小明, 1, 秦老师
<== Row: 2, 小红, 1, 秦老师
<== Row: 3, 小张, 1, 秦老师
<== Row: 4, 小李, 1, 秦老师
<== Row: 5, 小王, 1, 秦老师
<== Total: 5
2Student(id=1, name=小明, teacher=Teacher(id=1, name=秦老师))
2Student(id=2, name=小红, teacher=Teacher(id=1, name=秦老师))
2Student(id=3, name=小张, teacher=Teacher(id=1, name=秦老师))
2Student(id=4, name=小李, teacher=Teacher(id=1, name=秦老师))
2Student(id=5, name=小王, teacher=Teacher(id=1, name=秦老师))
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@15eb5ee5]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@15eb5ee5]
Returned connection 367746789 to pool.
理解: 一个老师拥有多个学生
测试1: 结果处理方式 (相当于联表查询)
修改POJO
Student.java
@Data
public class Student {
private int id;
private String name;
private int tid;
}
Teacher.java
@Data
public class Teacher {
private int id;
private String name;
// 每个老师拥有多个学生
private List<Student> student;
}
接口: TeacherMapper.java
/*2 获取指定老师名下的所有学生信息及老师信息, 返回值确实是一个teacher*/
Teacher getTeacher2(@Param("tid") int id);
TeacherMapper.xml
<select id="getTeacher2" resultMap="TeacherStudent2">
select s.id sid, s.name sname, t.name tname, t.id tid
from student s, teacher t
where s.tid=t.id and t.id=#{tid};
select>
<resultMap id="TeacherStudent2" type="Teacher">
<result property="id" column="tid">result>
<result property="name" column="tname">result>
<collection property="student" javaType="ArrayList" ofType="Student">
<result property="id" column="sid">result>
<result property="name" column="sname">result>
<result property="tid" column="tid">result>
collection>
resultMap>
Tests.java
/*2*/
@Test
public void testGetTeacher2(){
InputStream inputStream = null;
SqlSession sqlSession = null;
try {
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
sqlSession = sqlSessionFactory.openSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher2(1);
System.out.println(teacher);
} catch (IOException e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
测试2: 按照查询嵌套 (相当于子查询)
TeacherMapper.java 接口
/*3 按照查询嵌套实现*/
Teacher getTeacher3(@Param("tid") int id);
TeacherMapper.xml
注意,
select
中的SQL, 一开始我没想起来要写where clausecollection
中的column
指teacher表的id, 所以是id
而不是tid
<select id="getTeacher3" resultMap="TeacherStudent3">
select * from teacher where id = #{tid};
select>
<resultMap id="TeacherStudent3" type="Teacher">
<collection property="student" javaType="List" ofType="Student" select="getStudentByTeacherId" column="id"/>
resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from student where tid = #{tid}
select>
测试, 通过
Student
\ Teacher
, 不写也没报错List
“SQL复杂+resultMap容易理解” vs “SQL简单+resultMap难以理解”, 尽量选择前者.
如果错误不容易排查, 可以借助日志, 推荐LOG4J
避免"慢SQL", 要逐渐开始考虑性能
准备数据库
# 动态SQL
USE mybatis;
CREATE TABLE blog(
id VARCHAR(50) NOT NULL COMMENT '博客id',
title VARCHAR(100) NOT NULL COMMENT '博客标题',
author VARCHAR(30) NOT NULL COMMENT '博客作者',
create_time DATETIME NOT NULL COMMENT '创建时间',
views INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8
创建一个project: mybatis-08-dynamicsql
导入依赖
<dependencies>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.12version>
dependency>
dependencies>
配置文件
<configuration>
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="Ms_910613"/>
properties>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
<typeAliases>
<package name="com.zhangcl.pojo"/>
typeAliases>
<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>
<package name="com.zhangcl.dao"/>
mappers>
configuration>
实体类
package com.zhangcl.pojo;
import lombok.Data;
import java.util.Date;
@Data
public class Blog {
private String id;
private String title;
private String author;
private Date createTime; // 注意: 属性名, 字段名问题
private int views;
}
Mapper接口和.XML
文件
package com.zhangcl.dao;
import com.zhangcl.pojo.Blog;
public interface BlogMapper {
/*插入数据*/
int addBlog(Blog blog);
}
<mapper namespace="com.zhangcl.dao.BlogMapper">
<insert id="addBlog" parameterType="Blog">
insert into blog(id, title, author, create_time, views)
values(#{id},#{title},#{author},#{createTime},#{views})
insert>
mapper>
工具类
package com.zhangcl.util;
import org.junit.Test;
import java.util.UUID;
/**
* 用于生成唯一ID
*/
public class IDUtils {
public static String getId(){
return UUID.randomUUID().toString().replace("-","");
}
@Test
public void testGet(){
System.out.println(IDUtils.getId());
}
}
//##############################################
package com.zhangcl.util;
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;
import java.io.InputStream;
// sqlSessionFactory --> sqlSession
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml"; //src/main/resources/
// System.out.println("打桩1" + resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
// System.out.println("打桩2" + inputStream);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// System.out.println("打桩3" + sqlSessionFactory);
} catch (IOException e) {
System.out.println("工具类出错了!");
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
BlogMapper.java
List<Blog> queryBlogIF(Map map);
BlogMapper.xml. 其中where 1=1
是个一比较巧妙的用法, 避免了where
关键字可能存在的尴尬处境
<select id="queryBlogIF" parameterType="map" resultType="Blog">
select * from blog where 1=1
<if test="title != null">
and title = #{title}
if>
<if test="author != null">
and author = #{author}
if>
select>
Tests.java
/*1*/
@Test
public void queryBlogIF(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map map = new HashMap();
map.put("title", "Java不难");
List<Blog> blogs = mapper.queryBlogIF(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
其实用where举例不合理
<sql id="NAME">
WHERE ...
sql>
<select ...>
select ... from ...
<include refid="NAME">include>
select>
简介:
MyBatis默认定义了两级缓存: 一级缓存 | 二级缓存
顺序:
数据一定先进一级缓存, 如果二级开启, 则关闭一级后进二级
但查询时候, 是先去二级查, 再去一级查, 再去数据库
以上可以通过日志看出轨迹: cache hit…
简介:
默认开启, 是SqlSession
级别的缓存, 也称为本地缓存、会话缓存.
SqlSession
级别: 与数据库同一次会话期间select
到的数据都会放在这个一级缓存中.
测试:
秦老师做了演示. 在同一个SqlSession中查询两次相同的东西, 只查询了一次, 并且两次查询的是同一个引用对象.
缓存失效:
SqlSession.clearCache();
简介
需要手动开启和配置的, 是nameSpace
级别的缓存, 也称"全局缓存", 也可以自定义二级缓存
nameSpace
级别: 每个mapper查出的数据, 会在各自对应的缓存 (map) 中 [个人理解]
基本工作机制:
开启大致步骤
MyBatis 核心配置文件: mybatis-config.xml 中
<setting name="cacheEnable" value="true">setting>
将要使用缓存的某个 Mapper.xml 中
<cache/>
或者
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
在SQL映射文件中添加以下代码即可
<cache/>
详解:
示例: 可以修改 cache 元素的属性, 实现比较 customized 的配置, 比如
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
详解:
通用说明:
有哪些清除策略可供使用?
课程以ehcache为例, 工作应该用redis
什么是自定义二级缓存?
实现MyBatis所定义的缓存接口
Cache
, 即可自定义二级缓存
Caused by: org.apache.ibatis.executor.ExecutorException: A query was run and no Result Maps were found for the Mapped Statement ‘com.zhangcl.dao.BlogMapper.queryBlogIF’. It’s likely that neither a Result Type nor a Result Map was specified.
changelog:
2020年4月11日 凌晨
先发布一次到blog
2020年4月13日
学完