MyBatis - 系统化复习加强 (2020年4月11日始 持续更新)
MyBatis - 系统化复习加强 (2020年4月11日始 持续更新)
1 简介
内容根据B站秦疆老师的课编排, 顺便基于MyBatis官网
1.1 什么是MyBatis
- 是一款 first class 持久层框架
- 支持定制化 (custom) SQL、stored procedure、高级映射
- 它避免了几乎所有的JDBC代码、手动设置参数、获取结果集等
- 可以使用简单的XML、注解来配置和映射原生类型、接口、POJO (Plain Old Java Objects)
1.2 持久层
持久化
将程序的数据从瞬时状态转化为持久状态的过程
以前也曾经也通过IO, 把数据直接写在磁盘的文档里, 但非常浪费IO资源
为什么要持久化?
- 重要对象不能断电丢失 (内存特点)
- 且内存成本较高
持久层
完成持久化工作的代码块, 以‘层‘分解
1.3 为什么要用 MyBatis
- 传统 JDBC 太复杂
- 帮助程序员将数据存入数据库
- 简单易学
- SQL 在 XML, 避免与代码耦合
- 提供对象关系映射标签, 支持对象与数据的映射
- 支持动态 SQL
- 市场占有率高
1.4 如何获取 MyBatis
Maven仓库
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency>
GItHub, 源码已经托管至此
MyBatis-3官方网站
2 第一个MyBatis程序
学新东西大致路径: 搭建环境 --> 导入依赖 --> 编写代码 --> 测试
2.1 搭建环境
创建数据库
CREATE DATABASE mybatis; USE mybatis; CREATE TABLE USER( id INT(20) NOT NULL, NAME VARCHAR(30) DEFAULT NULL, pwd VARCHAR(30) DEFAULT NULL, PRIMARY KEY(id) )ENGINE=INNODB DEFAULT CHARSET=utf8; INSERT INTO USER(id, NAME, pwd) VALUES (1, ‘狂神‘, ‘123456‘), (2, ‘张三‘, ‘123456‘), (3, ‘李四‘, ‘1234454‘);
新建项目
新建普通Maven项目
删除 src 目录 (把工程变成父工程)
导入依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <!--父工程--> <groupId>com.zhangcl</groupId> <artifactId>mybatis-study</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!--mybatis--> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>
2.2 创建Module
这样子项目不需要每次都导包, 可以直接用父工程中的
1. 编写核心配置文件
编写Mybatis的核心配置文件, mybatis-config.xml, 在图示位置( resources 目录)
![image-20200408091254615](D:\微云同步助手\364291826\同步文件夹\知识库\10 - md文库 秦疆老师\框架\Mybatis.assets\image-20200408091254615.png)
内容复制自官网
<?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> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:192.168.159.128:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="910613"/> </dataSource> </environment> </environments> </configuration>
**2. 编写MyBatis工具类 **
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"; InputStream inputStream = null; inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }
3. 编写代码
实体类
package com.zhangcl.pojo; public class User { private int id; private String name; private String pwd; @Override public String toString() { return "User{" + "id=" + id + ", name=‘" + name + ‘\‘‘ + ", pwd=‘" + pwd + ‘\‘‘ + ‘}‘; } public User() { } public User(int id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }
Dao接口
package com.zhangcl.dao; import com.zhngcl.pojo.User; import java.util.List; public interface UserDao { List<User> getUserList(); }
Dao实现
不再写UserDaoImpl, 转而用哦一个Mapper配置文件
UserMapper.xml in dao 包
<?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"> <!--namespace 绑定一个对应的Dao\Mapper接口--> <mapper namespace="com.zhngcl.dao.UserDao"> <!--select 查询语句--> <select id="getUserList" resultType="com.zhngcl.pojo.User"> select * from user; </select> </mapper>
2.3 运行测试
package com.zhangcl.dao; import com.zhangcl.pojo.User; import com.zhangcl.util.MybatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; public class UserDaoTest { @Test public void test(){ /*获得SqlSession对象*/ SqlSession sqlSession = MybatisUtils.getSqlSession(); /*执行SQL * 方式一 * */ UserDao userDao = sqlSession.getMapper(UserDao.class); List<User> userList = userDao.getUserList(); 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=57912: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-01\target\test-classes;D:\Software-idea\Project-QinJiang\com.zhangcl\mybatis-01\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.dao.UserDaoTest 打桩1mybatis-config.xml 打桩 打桩8bee4 Wed Apr 08 15:37:09 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. User{id=1, name=‘狂神‘, pwd=‘123456‘} User{id=2, name=‘张三‘, pwd=‘123456‘} User{id=3, name=‘李四‘, pwd=‘1234454‘} */
2.4 常见BUG
MapperRegistry问题
org.apache.ibatis.binding.BindingException: Type interface com.zhngcl.dao.UserDao is not known to the MapperRegistry.
解决:
确保核心配置文件中有Mapper的注册配置
in mybatis-config.xml
<!--每个*Mapper.xml都需要在这里注册--> <mappers> <mapper resource="com/zhngcl/dao/UserMapper.xml"></mapper> </mappers>
★ Could not find resource .............UserMapper.xml
Maven资源没能导出到target问题
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=62645: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-01\target\test-classes;D:\Software-idea\Project-QinJiang\com.zhangcl\mybatis-01\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.dao.UserDaoTest,test java.lang.ExceptionInInitializerError at com.zhangcl.dao.UserDaoTest.test(UserDaoTest.java:16) 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:497) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58) Caused by: org.apache.ibatis.exceptions.PersistenceException: ### Error building SqlSession. ### The error may exist in com/zhngcl/dao/UserMapper.xml ### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource com/zhngcl/dao/UserMapper.xml at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30) at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:80) at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:64) at com.zhngcl.util.MybatisUtils.<clinit>(MybatisUtils.java:20) ... 23 more Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource com/zhngcl/dao/UserMapper.xml at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:121) at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:98) at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:78) ... 25 more Caused by: java.io.IOException: Could not find resource com/zhngcl/dao/UserMapper.xml at org.apache.ibatis.io.Resources.getResourceAsStream(Resources.java:114) at org.apache.ibatis.io.Resources.getResourceAsStream(Resources.java:100) at org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement(XMLConfigBuilder.java:372) at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:119) ... 27 more Process finished with exit code -1
解决:
需要在 pom.xml
中引入对资源导入导出的配置, 为了保证成功, 我们在父工程和module中的 pom.xml 里都添加了
<!--在build中配置resources, 防止资源导入导出失败问题--> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build>
Error creating document instance.
java.lang.ExceptionInInitializerError at com.zhangcl.dao.UserDaoTest.test(UserDaoTest.java:16) 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:497) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58) Caused by: org.apache.ibatis.exceptions.PersistenceException: ### Error building SqlSession. ### Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance. Cause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 1 字节的 UTF-8 序列的字节 1 无效。 at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30) at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:80) at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:64) at com.zhngcl.util.MybatisUtils.<clinit>(MybatisUtils.java:21) ... 23 more Caused by: org.apache.ibatis.builder.BuilderException: Error creating document instance. Cause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 1 字节的 UTF-8 序列的字节 1 无效。 at org.apache.ibatis.parsing.XPathParser.createDocument(XPathParser.java:263) at org.apache.ibatis.parsing.XPathParser.<init>(XPathParser.java:127) at org.apache.ibatis.builder.xml.XMLConfigBuilder.<init>(XMLConfigBuilder.java:81) at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:77) ... 25 more Caused by: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 1 字节的 UTF-8 序列的字节 1 无效。 at com.sun.org.apache.xerces.internal.impl.io.UTF8Reader.invalidByte(UTF8Reader.java:701) at com.sun.org.apache.xerces.internal.impl.io.UTF8Reader.read(UTF8Reader.java:567) at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.load(XMLEntityScanner.java:1793) at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.scanData(XMLEntityScanner.java:1292) at com.sun.org.apache.xerces.internal.impl.XMLScanner.scanComment(XMLScanner.java:778) at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanComment(XMLDocumentFragmentScannerImpl.java:1039) at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:904) at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:606) at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:510) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:848) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:777) at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141) at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:243) at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:339) at org.apache.ibatis.parsing.XPathParser.createDocument(XPathParser.java:261) ... 28 more
解决: 修改整个Idea的字符集, 然后新建这个xml文件, 内容转移进去即可
还可能会连接失败
- 可能有linux防火墙的锅, firewalld
- 也可能需要把url中的useSSL暂时移除, 已解决!
3 CRUD
3.1 补充知识
记录几个容易出问题的地方:
- 增删改需要提交事务
- mapers标签的resource路径要用斜杠分隔, 而不是包路径那种点
- IDEA报错信息从后往前读
3.2 万能Map
用Map来保存数据
可以在保存数据时自定义任何字段名:
编写Mapper接口: UserDao.java (也可以改为UserMapper)
/*增2: Map保存User信息*/ int addUserInMap(Map<String, Object> map);
编写*Mapper.xml: UserMapper.xml
<!--增2 User in a Map--> <insert id="addUserInMap" parameterType="map"> insert into user(id, name, pwd) values(#{userId}, #{userName}, #{password}); </insert>
注意:
parameterType="map"
, 这里是map编写单元测试代码: UserDaoTest.java
/*增2 User in a Map*/ @Test public void addUserInMap(){ Map<String, Object> map = new HashMap<String, Object>(); map.put("userId", 4); map.put("userName", "老四"); map.put("password", "4444"); userDao.addUserInMap(map); sqlSession.commit(); sqlSession.close(); }
注意: 这里的亮点是, map 的 key 值设定是故意区别于数据库中的 columnName, 是我们要展现的万能Map的特性之一
Map用法小结
如果用POJO对象传递参数, sql执行时底层会直接从中取对象属性, sql语句标签规定参数:
parameterType="User"
如用Map传递参数, 也要在标签中规定参数:
parameterType="map"
如参数只是一个 + 是基本数据类型, 可以避免规定参数Type而自动取用.
多个参数时, 可用map或者注解!
3.3 模糊查询
通配符问题
传参时包含
%
通配符, 但不安全SQL拼接通配符
<select id="getUserLikeInMap" resultType="com.zhangcl.pojo.User"> select * from user where name like concat("%",#{value},"%"); </select>
4 Configuration XML
4.1 简介
XML 配置
元素顺序性
XML特性, 所有标签都可以固定\规定顺序, 否则会IDE会报错
The content of element type "configuration" must match "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".
需要掌握的
- properties (属性)
- settings (设置)
- typeAliases (类型别名)
4.2 properties 标签
可以通过它引用别的外部配置文件.
顺序\优先级问题
外部文件可以覆盖标签中的属性, 反则不可
测试1 纯外部文件
编写配置文件: db.properties 在 resources目录
driver=com.mysql.jdbc.Driver url="jdbc:mysql://192.168.159.128:3306/mybatis?useUnicode=true&characterEncoding=utf8 user=root password=Ms_910613
在mybatis-config.xml 核心配置文件中引入
<?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"></properties> <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/UserMapper.xml"></mapper> </mappers> </configuration>
测试通过!
测试2, 标签中填写属性
外部文件中取消两个属性
driver=com.mysql.jdbc.Driver url=jdbc:mysql://192.168.159.128:3306/mybatis?useUnicode=false&characterEncoding=utf8 # username=root # password=Ms_910613
转而在配置s标签中替代
<!--引入外部配置文件--> <properties resource="db.properties"> <property name="username" value="root"/> <property name="password" value="Ms_910613"/> </properties>
测试通过!
4.3 environments 标签
One important thing to remember though: While you can configure multiple environments, you can only choose ONE per SqlSessionFactory instance.
译\理解: 可以有多个环境, 但同时使用的只能是其中之一.
官网示例
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <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>
选择环境: 属性default="被选择环境的id"
transactionManager子标签
包含:
- JDBC, 默认
- MANAGER, 官网说这个很"无为", 要用它同时要配置关闭一个东西
dataSource子标签
连接池, 包含: POOLED (默认) | UNPOOLED | JNDI
4.4 typeAliases (类型别名)
作用
可以写
User
, 而不是com.zhangcl.pojo.User
(POJO比较少时候使用)<typeAliases> <typeAlias type="com.zhangcl.pojo.User" alias="User"/> </typeAliases>
- 可以 DIY 别名
可以让 MyBatis 扫描指定包中的 Java Bean
<typeAliases> <package name="com.zhangcl.pojo"/> </typeAliases>
如果也需要DIY别名, 也可以利用注解
@Alias("userAlias") public class User{ // ... }
4.5 settings
需要重点掌握的
- cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存, 默认true
- lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置
fetchType
属性来覆盖该项的开关状态, 默认false - mapUnderscoreToCamelCase, 驼峰映射, 默认false
4.6 其他
用到概率极低:
- typeHandler (类型处理器)
- objectFactory (对象工厂)
- plugins (插件)
- MyBatis-plus
4.7 映射器 (mappers)
MapperRegistry: 注册绑定存了我们SQL的Mapper文件
方式一: 通过绑定
.xml
文件注册<mapppers> <mapper resource="com/zhangcl/dao/UserMapper.xml"></mapper> </mapppers>
方式二: 通过绑定 DAO 接口
.java
文件注册<mappers> <mapper class="com.zhangcl.dao.UserDao"></mapper> </mappers>
[注意]
- 接口 .java 文件与对应的 .xml 文件名必须相同
- 且必须位于同一目录
方式三: 通过扫描 package 注册
<mappers> <package name="com.zhangcl.dao"></package> </mappers>
[注意] (同方式二)
5 作用域 (Scope) 和生命周期
错误使用此项会产生严重的并发问题
SqlSessionFactoryBuilder
一旦创建完成, 就不再需要, 所以局部变量
SqlSessionFactory
最核心, 可以想象为一个数据库连接池. 一旦创建就一直存在, 没有理由丢弃或者再次创建, 所以
最佳作用域是应用作用域, 单例模式或者静态单例
SqlSession
连接到连接池的一个请求, 可以对应多个Mapper文件
不是线程安全的, 不能被共享, 最佳作用域是
请求或方法作用域, 用完之后就要关闭, 否则资源被占用
大致关系图
![image-20200410182859903](D:\微云同步助手\364291826\同步文件夹\知识库\10 - md文库 秦疆老师\框架\Mybatis.assets\image-20200410182859903.png)
注意: 这里的每个Mapper, 代表一个具体业务. (个人理解, 代表一个sql 一个方法)
6 fieldName 和 columnName 一致性问题
测试: 不一致的问题
数据库字段
![image-20200410192346711](D:\微云同步助手\364291826\同步文件夹\知识库\10 - md文库 秦疆老师\框架\Mybatis.assets\image-20200410192346711.png)
新建项目, 其实是拷贝修改的. 且修改 User 类
public class User { private int id; private String name; private String password; // 注意数据库是pwd, 而这里是password }
运行, 结果:
User{id=2, name=‘张三‘, password=‘null‘}
解决:
方法一, sql中select起别名 as, 不过太简单了, 没技术含量
方法二,
resultMap
<resultMap id="UserMap" type="User"> <result column="pwd" property="password"></result> </resultMap> <select id="getUserById" resultMap="UserMap" > select * from mybatis.user where id = #{id}; </select>
changelog:
2020年4月11日 先发布一次到blog