1. 相关文档地址
- 中文文档 https://mybatis.org/mybatis-3/zh/index.html
- Mybatis可以配置成适应多种环境,不过每个SqlSessionFactory实例只能选择一种环境。
- Mybatis默认事务管理器是JDBC,连接池:POOLED
- Maven仓库:下载地址
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
- gitHub地址
- 中文文档地址
- mybatis官方文档:文档地址
2. demo
新建普通maven项目作为父项目,导入sql驱动,mybatis,junit组件
<!--导入依赖-->
<dependencies>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
新建新组件作为子级项目,普通maven的module
添加配置文件:
在src->main->resources目录下新建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>
<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}"/>//数据库名字,默认root
<property name="password" value="${password}"/>//数据库密码,自己的数据库密码,一般为root
</dataSource>
</environment>
</environments>
</configuration>
编写mybatis工具类
//SqlSessionFactory --生产--> SqlSession
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory; //提升作用域
//获取工厂,固定代码
static {
try {
String resource="mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取sqlSession
//SqlSession完全包含了面向对象数据库执行SQL命令所需的方法
public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession();}
}
实体类
public class User {
private int id;
private String name;
private String 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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
Dao接口
public interface UserDao {
List<User> getUserList();
}
接口实现类改为以xxxMapper.xml的配置文件
注意事项:配置文件中不要写中文注释,如果非要写,解决方法见后面的异常解决方案
<?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:命名空间,绑定mapper/Dao接口-->
<mapper namespace="com.qian.dao.UserDao">
<!--id:接口的方法,resultType:接口的返回值类型-->
<select id="getUserList" resultType="com.qian.pojo.User">
select * from mybatis.user where id = #{id}
</select>
</mapper>
测试类
public class UserDaoTest {
@Test
public void test(){
//获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//获取mapper
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> list = mapper.getUserList();
for (User u:list){
System.out.println(u);
}
//不推荐使用
/*
这种方式能够正常工作,对使用旧版本 MyBatis 的用户来说也比较熟悉。但现在有了一种更简洁的方式——使用和指定语句的参数和返回值相匹配的接口(比如 BlogMapper.class),现在你的代码不仅更清晰,更加类型安全,还不用担心可能出错的字符串字面值以及强制类型转换。
*/
// List<User> list = sqlSession.selectList("com.qian.dao.UserDao.getUserList");
// for (User user : list) {
// System.out.println(user);
// }
//关闭SqlSession
sqlSession.close();
}
}
异常1:org.apache.ibatis.binding.BindingException: Type interface com.qian.dao.UserDao is not known to the MapperRegistry.
解决方法:每一个Mapper.xml文件都需要在src->main->resources目录下新建mybatis-config.xml的核心配置文件中注册
<mappers>
<mapper resource="com/qian/dao/UserMapper.xml">
</mappers>
异常2:
Error building SqlSession.
The error may exist in com/qian/dao/UserMapper.xml
Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration.
Cause: java.io.IOException: Could not find resource com/qian/dao/UserMapper.xml
解决方法:
<!-- 在maven中,约定大于配置,在pom中添加此文件可以解决 -->
<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>
异常3:
Error building SqlSession.
The error may exist in com/qian/dao/UserMapper.xml
Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration.
Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance. Cause: org.xml.sax.SAXParseException; lineNumber: 6;
解决方法:
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 把mybatis-config.xml与mybatis-config.xml文件的encoding修改成下面的 -->
<?xml version="1.0" encoding="UTF8" ?>
异常4:
Cause: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
解决方法:
useSSL=true改为false(true也可以,需要在mysql中启用SSL
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8"/>
参考文档 :https://www.kuangstudy.com/bbs/1580393245333508097
3.配置文件优先级
- 如果两个文件有同一个字段,优先使用外部配置文件
配置db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&;useUnicode=true&;
characterEncoding=UTF8&;serverTimezone=GMT%2B8&;autoConnect=true
username=root
password=root
在核心配置文件中引入
注意:在xml中,所有的标签都可以规定顺序
(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".
核心文件配置
<configuration>
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></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>
</configuration>
总结
可以直接引入外部文件
也可以在<property></property>里面配置
外部引入的文件(db.properties)的优先级要比在<property></property>要高
错误提示
4. LOG4J
Log4j是一个由Java编写可靠、灵活的日志框架,是Apache旗下的一个开源项目;现如今,Log4j已经被移植到了C、C++、Python等语言中,服务更多的Developer;`
#newhappy log4j.properties start
log4j.rootLogger=DEBUG,console,file
#控制台输出 console appender
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
#文件输出 rolling file appender
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/yu.log
log4j.appender.file.MaxFileSize=10mB
log4j.appender.file.MaxBackupIndex=2
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{mmm d,yyyy hh:mm:ss a} : %p [%t] %m%n
log4j.appender.file.threshold=DEBUG
#日志输出级别 logger
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
#newhappy log4j.properties end
解决方法:配置maven导入log4j
pom.xml
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
5. MyBatis执行流程
Resource获取加载全局配置文件
实例化SqlSessionFactoryBuilder构造器
解析配置文件流XMLConfigBuilder
Configuration所有的配置信息
SqlSessionFactory实例化
transaction事务管理器
创建executor执行器
创建sqlSession
实现CRUD
查看是否执行成功
提交事务
关闭sqlSession连接
- 注意:#{} 和KaTeX parse error: Expected 'EOF', got '#' at position 7: {} 注意:#̲{} 和{}
- 1.#{} 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符,一个 #{ } 被解析为一个参数占位符;而${}仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换。
- 2.#{} 解析之后会将String类型的数据自动加上引号,其他数据类型不会;而${} 解析之后是什么就是什么,他不会当做字符串处理。
- 3#{} 很大程度上可以防止SQL注入(SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成了恶意的执行操作);而${} 主要用于SQL拼接的时候,有很大的SQL注入隐患。
- 4.在某些特殊场合下只能用${},不能用#{}。例如:在使用排序时ORDER BY ${id},如果使用#{id},则会被解析成ORDER BY “id”,这显然是一种错误的写法。
6. Lombok
左上角File->Settings->Plugins
搜索Lombok,下载安装
导入maven
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
Lombok的支持
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val
@var
experimental @var
@UtilityClass
Lombok config system
Code inspections
Refactoring actions (lombok and delombok)
常用支持
@Data支持: 无参构造,getter&setter,toString,hashCode,equals
@AllArgsConstructor: 有参构造
@NoArgsConstructor: 无参构造
使用方法,在实体类上加注解
配置
关联 association 多对一
集合 collection 一对多
javaType 指定实体类中属性的类型
ofType 用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
7.缓存
-
什么是缓存(Cache)
每次查询都要连接数据库,比较耗资源,我们把查询到的数据暂存到内存里面,下次查询的时候,从内存读取, 这个地方就叫缓存。
存在内存中的临时数据
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询、从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题
-
为什么要使用缓存
减少和数据库的交互次数,减小系统开销,提高系统效率
-
什么样的数据适用于缓存?
经常查询且不经常改变的数据
- Mybatis缓存
Mybatis系统默认定义了两级缓存
默认情况下,只有一级缓存开启(SqlSession缓存,也称为本地缓存)
二级缓存需要手动配置,它是基于namespace级别的缓存
Mybatis定义了缓存接口Cache,可以通过实现Cache接口来自定义二级缓存
二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个命名空间,对应一个二级缓存
- 工作机制
1.一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
2.如果当前会话关闭了,这个会话对应的一级缓存就没了,但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
3.新的会话查询信息,就可以从二级缓存中获取内容;
4.新的会话查询信息,就可以从二级缓存中获取内容;
5.不同的mapper查出的数据会放在自己对应的缓存(map)中; - 也就是说一级缓存死的时候,把数据交给了二级缓存
提示:
缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。
这些属性可以通过 cache 元素的属性来修改。比如:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
可用的清除策略有:
LRU
– 最近最少使用:移除最长时间不被使用的对象。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。*** 默认的清除策略是 LRU。***
源文档: https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#cache
<settings>
<!-- 标准的日志工厂实现-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 显示开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
然后Mapper中添加 <cache>
可以带上上面的参数
原理
8.EhCache
-
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认CacheProvider。Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。
-
导包
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache --> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.2</version> </dependency>
-
写入配置文件(resources->ehcache.xml)
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"> <!-- 磁盘缓存位置 --> <diskStore path="java.io.tmpdir/ehcache"/> <!-- 默认缓存 --> <defaultCache maxEntriesLocalHeap="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap"/> </defaultCache> <!-- helloworld缓存 --> <cache name="HelloWorldCache" maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="5" timeToLiveSeconds="5" overflowToDisk="false" memoryStoreEvictionPolicy="LRU"/> </ehcache>
-
在Mapper中指定
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
在实际开发中,我们更多的使用Redis来做缓存