@
7 日志
如果数据库操作出现异常需要排错, 日志是最好的助手!
7.1 日志工厂
官网列出的 'logImpl' (日志实现), 无默认值, 具体选择哪一个在 settings
中设置:
- SLF4J
- LOG4J [掌握]
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING (标准日志输出) [掌握]
- NO_LOGGING
那么, 要如何使用呢?
7.2 STDOUT_LOGGING
测试一下吧:
-
在mybatis-config.xml 中设置, 无需任何其他设置:
<!--设置--> <settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
-
运行test输出结果:
D:Softwarejdk1.8.0_71injava.exe -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:Program FilesJetBrainsIntelliJ IDEA 2019.2libidea_rt.jar=56252:D:Program FilesJetBrainsIntelliJ IDEA 2019.2in" -Dfile.encoding=UTF-8 -classpath "D:Program FilesJetBrainsIntelliJ IDEA 2019.2libidea_rt.jar;D:Program FilesJetBrainsIntelliJ IDEA 2019.2pluginsjunitlibjunit5-rt.jar;D:Program FilesJetBrainsIntelliJ IDEA 2019.2pluginsjunitlibjunit-rt.jar;D:Softwarejdk1.8.0_71jrelibcharsets.jar;D:Softwarejdk1.8.0_71jrelibdeploy.jar;D:Softwarejdk1.8.0_71jrelibextaccess-bridge-64.jar;D:Softwarejdk1.8.0_71jrelibextcldrdata.jar;D:Softwarejdk1.8.0_71jrelibextdnsns.jar;D:Softwarejdk1.8.0_71jrelibextjaccess.jar;D:Softwarejdk1.8.0_71jrelibextjfxrt.jar;D:Softwarejdk1.8.0_71jrelibextlocaledata.jar;D:Softwarejdk1.8.0_71jrelibext ashorn.jar;D:Softwarejdk1.8.0_71jrelibextsunec.jar;D:Softwarejdk1.8.0_71jrelibextsunjce_provider.jar;D:Softwarejdk1.8.0_71jrelibextsunmscapi.jar;D:Softwarejdk1.8.0_71jrelibextsunpkcs11.jar;D:Softwarejdk1.8.0_71jrelibextzipfs.jar;D:Softwarejdk1.8.0_71jrelibjavaws.jar;D:Softwarejdk1.8.0_71jrelibjce.jar;D:Softwarejdk1.8.0_71jrelibjfr.jar;D:Softwarejdk1.8.0_71jrelibjfxswt.jar;D:Softwarejdk1.8.0_71jrelibjsse.jar;D:Softwarejdk1.8.0_71jrelibmanagement-agent.jar;D:Softwarejdk1.8.0_71jrelibplugin.jar;D:Softwarejdk1.8.0_71jrelib esources.jar;D:Softwarejdk1.8.0_71jrelib t.jar;D:Software-ideaProject-QinJiangcom.zhangclmybatis-03 arget est-classes;D:Software-ideaProject-QinJiangcom.zhangclmybatis-03 argetclasses;D:Software-ideaMavenRepository epo-3.6.1orgmybatismybatis3.5.4mybatis-3.5.4.jar;D:Software-ideaMavenRepository epo-3.6.1mysqlmysql-connector-java5.1.47mysql-connector-java-5.1.47.jar;D:Software-ideaMavenRepository epo-3.6.1junitjunit4.12junit-4.12.jar;D:Software-ideaMavenRepository epo-3.6.1orghamcresthamcrest-core1.3hamcrest-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
7.3 LOG4J
什么是LOG4J
-
Apache的开源项目, 可以通过它控制日志信息输出目的地: 控制台、文件、GUI组建
-
可以精确控制每一条日志信息的输出格式
-
可以精确控制每一条日志信息的输出级别
-
可以通过配置文件来设置它们, 而不必要修改代码
测试一下吧:
-
导入Maven依赖
<!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
在 MyBatis 核心配置文件, 也就是mybatis-config.xml 中配置使用log4j.
要注意顺序性或顺序性报错的提示
<!--设置--> <settings> <!--选择一个日志Impl: 标准日志输出--> <!--<setting name="logImpl" value="STDOUT_LOGGING"/>--> <!--选择另一个日志Impl: log4j日志输出--> <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:Softwarejdk1.8.0_71injava.exe -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:Program FilesJetBrainsIntelliJ IDEA 2019.2libidea_rt.jar=61217:D:Program FilesJetBrainsIntelliJ IDEA 2019.2in" -Dfile.encoding=UTF-8 -classpath "D:Program FilesJetBrainsIntelliJ IDEA 2019.2libidea_rt.jar;D:Program FilesJetBrainsIntelliJ IDEA 2019.2pluginsjunitlibjunit5-rt.jar;D:Program FilesJetBrainsIntelliJ IDEA 2019.2pluginsjunitlibjunit-rt.jar;D:Softwarejdk1.8.0_71jrelibcharsets.jar;D:Softwarejdk1.8.0_71jrelibdeploy.jar;D:Softwarejdk1.8.0_71jrelibextaccess-bridge-64.jar;D:Softwarejdk1.8.0_71jrelibextcldrdata.jar;D:Softwarejdk1.8.0_71jrelibextdnsns.jar;D:Softwarejdk1.8.0_71jrelibextjaccess.jar;D:Softwarejdk1.8.0_71jrelibextjfxrt.jar;D:Softwarejdk1.8.0_71jrelibextlocaledata.jar;D:Softwarejdk1.8.0_71jrelibext ashorn.jar;D:Softwarejdk1.8.0_71jrelibextsunec.jar;D:Softwarejdk1.8.0_71jrelibextsunjce_provider.jar;D:Softwarejdk1.8.0_71jrelibextsunmscapi.jar;D:Softwarejdk1.8.0_71jrelibextsunpkcs11.jar;D:Softwarejdk1.8.0_71jrelibextzipfs.jar;D:Softwarejdk1.8.0_71jrelibjavaws.jar;D:Softwarejdk1.8.0_71jrelibjce.jar;D:Softwarejdk1.8.0_71jrelibjfr.jar;D:Softwarejdk1.8.0_71jrelibjfxswt.jar;D:Softwarejdk1.8.0_71jrelibjsse.jar;D:Softwarejdk1.8.0_71jrelibmanagement-agent.jar;D:Softwarejdk1.8.0_71jrelibplugin.jar;D:Softwarejdk1.8.0_71jrelib esources.jar;D:Softwarejdk1.8.0_71jrelib t.jar;D:Software-ideaProject-QinJiangcom.zhangclmybatis-03 arget est-classes;D:Software-ideaProject-QinJiangcom.zhangclmybatis-03 argetclasses;D:Software-ideaMavenRepository epo-3.6.1log4jlog4j1.2.17log4j-1.2.17.jar;D:Software-ideaMavenRepository epo-3.6.1orgmybatismybatis3.5.4mybatis-3.5.4.jar;D:Software-ideaMavenRepository epo-3.6.1mysqlmysql-connector-java5.1.47mysql-connector-java-5.1.47.jar;D:Software-ideaMavenRepository epo-3.6.1junitjunit4.12junit-4.12.jar;D:Software-ideaMavenRepository epo-3.6.1orghamcresthamcrest-core1.3hamcrest-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() */
8 分页
为什么要分页?
- 可减少多余的数据处理量
8.1 通过 limit 分页
是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, 可以注意一下.
<!--查 User 分页--> <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:Softwarejdk1.8.0_71injava.exe -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:Program FilesJetBrainsIntelliJ IDEA 2019.2libidea_rt.jar=64465:D:Program FilesJetBrainsIntelliJ IDEA 2019.2in" -Dfile.encoding=UTF-8 -classpath "D:Program FilesJetBrainsIntelliJ IDEA 2019.2libidea_rt.jar;D:Program FilesJetBrainsIntelliJ IDEA 2019.2pluginsjunitlibjunit5-rt.jar;D:Program FilesJetBrainsIntelliJ IDEA 2019.2pluginsjunitlibjunit-rt.jar;D:Softwarejdk1.8.0_71jrelibcharsets.jar;D:Softwarejdk1.8.0_71jrelibdeploy.jar;D:Softwarejdk1.8.0_71jrelibextaccess-bridge-64.jar;D:Softwarejdk1.8.0_71jrelibextcldrdata.jar;D:Softwarejdk1.8.0_71jrelibextdnsns.jar;D:Softwarejdk1.8.0_71jrelibextjaccess.jar;D:Softwarejdk1.8.0_71jrelibextjfxrt.jar;D:Softwarejdk1.8.0_71jrelibextlocaledata.jar;D:Softwarejdk1.8.0_71jrelibext ashorn.jar;D:Softwarejdk1.8.0_71jrelibextsunec.jar;D:Softwarejdk1.8.0_71jrelibextsunjce_provider.jar;D:Softwarejdk1.8.0_71jrelibextsunmscapi.jar;D:Softwarejdk1.8.0_71jrelibextsunpkcs11.jar;D:Softwarejdk1.8.0_71jrelibextzipfs.jar;D:Softwarejdk1.8.0_71jrelibjavaws.jar;D:Softwarejdk1.8.0_71jrelibjce.jar;D:Softwarejdk1.8.0_71jrelibjfr.jar;D:Softwarejdk1.8.0_71jrelibjfxswt.jar;D:Softwarejdk1.8.0_71jrelibjsse.jar;D:Softwarejdk1.8.0_71jrelibmanagement-agent.jar;D:Softwarejdk1.8.0_71jrelibplugin.jar;D:Softwarejdk1.8.0_71jrelib esources.jar;D:Softwarejdk1.8.0_71jrelib t.jar;D:Software-ideaProject-QinJiangcom.zhangclmybatis-03 arget est-classes;D:Software-ideaProject-QinJiangcom.zhangclmybatis-03 argetclasses;D:Software-ideaMavenRepository epo-3.6.1log4jlog4j1.2.17log4j-1.2.17.jar;D:Software-ideaMavenRepository epo-3.6.1orgmybatismybatis3.5.4mybatis-3.5.4.jar;D:Software-ideaMavenRepository epo-3.6.1mysqlmysql-connector-java5.1.47mysql-connector-java-5.1.47.jar;D:Software-ideaMavenRepository epo-3.6.1junitjunit4.12junit-4.12.jar;D:Software-ideaMavenRepository epo-3.6.1orghamcresthamcrest-core1.3hamcrest-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
8.2 RowBounds分页
属于 Java 代码层面的分页, 只作为了解, 据说技术比较老. 以下为基本步骤:
-
接口. 注意这里没有参数
/*查 User 分页2: rowBounds*/ List<User> getUserWithRowBounds();
-
Mapper.xml. 注意使用了
resultMap
<resultMap id="UserMap" type="User"> <result column="pwd" property="password"></result> </resultMap> <!--分页 查 User 2: RowBounds--> <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(); }
-
结果通过.
8.3 插件分页: PageHelper
据说用法和 RowBounds 比较接近, 了解即可.
如果真要学用, 可以直接上官网, 中文并且内容少而简单.
9 使用注解开发
为什么 "面向接口编程" ?
根本目的: 解耦, 定义与实现分离. leader写接口, 码农写实现.
9.1 注解开发
基本步骤
-
在核心配置文件 mybatis-config.xml 中注册绑定接口类
<mappers> <!--绑定接口, 而不再绑定映射xml文件--> <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(); }
9.2 autoTransaction (自动提交事务)
SqlSession sqlSession = sqlSessionFactory.openSqlSession(true);
参数 true
: 如果传入参数true, 则以为这开启自动提交事务
9.3 @Param
作用: 可以承上启下改名.
SQL中column: pwd
<=> @Param("pwd") String password
<=> POJO 中属性名 private String password
使用原则:
- 基本类型必加
- 只有一个参数可以不加, 但建议还是加上
- '基本类型' 的表述包括
String
- 引用类型不加
- 多个基本类型参数时更加要使用
@Param
注解修饰基本数据的参数.
9.4 #{} 还是 ${}
推荐使用 #{}
, 因为 ${}
不能防止 'SQL注入'
10 复杂查询
用到resultMap
10.1 "多对一"
理解: 多个学生共同一个老师
处理思路: 无非两种, 子查询 | 联表查询
测试1: 查询时嵌套处理
理解上有点像子查询. 以下是具体步骤:
-
准备数据库
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.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </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
设置如下, 后来出现问题, 是为后话.<!--1--> <select id="getStudent" resultType="Student"> select * from student; </select>
-
在mybatis-config.xml文件中设置绑定注册Mapper或者接口类. 这里我们先简单测试一下, 用接口, 所以这里只注册接口绑定.
<mappers> <!-- <mapper resource="com/zhangcl/dao/*.xml"></mapper>--> <!-- <mapper resource="com/zhangcl/dao/TeacherMapper.xml"></mapper>--> <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
更复杂的配置<!--1--> <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.
测试2: 对结果处理
先联表查询, 再处理结果
-
接口: StudentMapper.java
/*2*/ List<Student> getStudent2();
-
Mapper文件: StudentMapper.xml
两表联查, 查询出结果后映射给一个
resultMap
, 重点它的配置:注意:
- 标签是
result
, 而不是id
- 处理
Student
中的teacher
属性时相当于嵌套
<!--2--> <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.
10.2 一对多
理解: 一个老师拥有多个学生
测试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
<!--2--> <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> <!--"集合泛型"的类型配置用ofType javaType=ArrayList或List或不写这个属性都可以, 但是Object和LinkedList报错 --> <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 clause - 这里
collection
中的column
指teacher表的id, 所以是id
而不是tid
<!--3 按照查询嵌套 子查询--> <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> <!--根据tid查找student--> <select id="getStudentByTeacherId" resultType="Student"> select * from student where tid = #{tid} </select>
- 主
-
测试, 通过
10.3 小结
10.3.1 resultMap标签的结构
- resultMap 标签
- result - 基本类型, 名称不一致时
- association - 引用类型 Java类, 多对一
- javaType - Java中的类型, 如
Student
Teacher
, 不写也没报错
- javaType - Java中的类型, 如
- collection - 集合, 一对多
- javaType - Java的类型, 如
List
- ofType - 指定映射到List (集合)中的pojo (集合元素泛型的具体类型)
- javaType - Java的类型, 如
10.3.2 其他注意点
-
"SQL复杂+resultMap容易理解" vs "SQL简单+resultMap难以理解", 尽量选择前者.
-
如果错误不容易排查, 可以借助日志, 推荐LOG4J
-
避免"慢SQL", 要逐渐开始考虑性能
- MySQL 引擎
- InnoDB 底层
- 索引及索引优化
11 Dynamic SQL
11.1 环境搭建
准备数据库
# 动态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> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> </dependencies>
-
配置文件
<?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="db.properties"> <property name="username" value="root"/> <property name="password" value="Ms_910613"/> </properties> <!--设置--> <settings> <!--选择一个日志Impl: 标准日志输出--> <setting name="logImpl" value="STDOUT_LOGGING"/> <!--驼峰和下划线自动映射--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <!--别名: 在mapper.xml中可以只写类名!--> <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> <!-- <mapper resource="com/zhangcl/dao/*.xml"></mapper>--> <!-- <mapper resource="com/zhangcl/dao/TeacherMapper.xml"></mapper>--> <!-- <mapper resource="com/zhangcl/dao/StudentMapper.xml"></mapper>--> <!-- <mapper class="com.zhangcl.dao.TeacherMapper"></mapper>--> <!-- <mapper class="com.zhangcl.dao.StudentMapper"></mapper>--> <!-- <mapper resource="com/zhangcl/dao/*.xml"></mapper>--> <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); }
<?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.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(); } }
11.2 IF
-
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(); }
11.3 choose when otherwise
11.4 trim (where set)
11.5 foreach
11.6 SQL片段
11.6.1 示例
其实用where举例不合理
<!--声明-->
<sql id="NAME">
WHERE ...
</sql>
<!--引用-->
<select ...>
select ... from ...
<include refid="NAME"></include>
</select>
11.6.2 注意
- 最好基于单表来定义SQL片段
- 不要引用where (以上例子不合理)
12 缓存
12.1 MyBatis 缓存
简介:
MyBatis默认定义了两级缓存: 一级缓存 | 二级缓存
顺序:
-
数据一定先进一级缓存, 如果二级开启, 则关闭一级后进二级
-
但查询时候, 是先去二级查, 再去一级查, 再去数据库
以上可以通过日志看出轨迹: cache hit...
12.1 一级缓存
简介:
默认开启, 是SqlSession
级别的缓存, 也称为本地缓存、会话缓存.
SqlSession
级别: 与数据库同一次会话期间select
到的数据都会放在这个一级缓存中.
测试:
秦老师做了演示. 在同一个SqlSession中查询两次相同的东西, 只查询了一次, 并且两次查询的是同一个引用对象.
缓存失效:
- 同一个SqlSession中, 第一次查之后 (产生缓存), 此时对同表的另一条数据进行了更新, 居然也会导致首次查的缓存失效
- 跨Mapper.xml
- 手动清除:
SqlSession.clearCache();
12.2 二级缓存
12.2.1 简介
简介
需要手动开启和配置的, 是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"/>
12.2.2 最简使用方式
在SQL映射文件中添加以下代码即可
<cache/>
详解:
- 映射文件中的所有 select 语句结果会被缓存
- 所有 insert、update、delete 语句会刷新缓存
- 会使用"最近最少使用"算法 (LRU, Least Recently Used) 来清除不需要的缓存
- 不会定时刷新, 不涉及"刷新间隔"的概念
- 最多会保存1024个引用
- "读写缓存", 获取到的对象不共享, 可以安全地被调用者修改而不干扰其他调用者或线程的潜在修改
12.2.3 较高级使用方式
示例: 可以修改 cache 元素的属性, 实现比较 customized 的配置, 比如
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
详解:
- FIFO 清除策略 (first in first out, 先进先出, 队列)
- 60s 间隔刷新
- 最多 512 个引用
- "只读缓存" (readOnly), 对获取的对象修改可能会在不同线程的调用者中产生冲突
通用说明:
12.2.4 清除策略
有哪些清除策略可供使用?
- LRU - 最近最少使用
- FIFO - 先进先出
- SOFT - 基于垃圾回收和软引用规则移除对象
- WEAK - 更积极地基于依赖垃圾回收和弱引用规则移除对象
12.3 自定义二级缓存
课程以ehcache为例, 工作应该用redis
什么是自定义二级缓存?
实现MyBatis所定义的缓存接口
Cache
, 即可自定义二级缓存
13 BUG Set
13.1 漏掉 resultMapType
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.
13.2 POJO最好实现序列化接口
14 MyBatis 详细执行流程 底层原理
- Resources 获取加载全局核心配置文件
- 实例化 SqlSessionFactoryBuilder 建造器
- 解析配置文件流XMLConfigBuilder
- 所有的配置信息保存在Configuration
- 实例化 SqlSessionFactory
- transactional 事务管理器
- 创建 executor 执行器
- 创建 sqlSession 会话
- 实现 CRUD
- 失败, 回 4
- 成功, 到 8
- 提交事务
- 结束
changelog:
-
2020年4月11日 凌晨
先发布一次到blog
-
2020年4月13日
学完