MyBatis
一、Mybatis基本使用步骤
1.导入依赖
<!--mybaits依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
2.配置连接池
db.properties(使用jdbc-mysql驱动)
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/database?useSSL=false&charaterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username=xxx
password=xxx
3.配置Mybatis
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>
<!--链接数据库配置文件db.properties-->
<properties resource="db.properties" />
<!--配置日志文件输出-->
<!-- <settings>-->
<!-- <setting name="logImpl" value="SLF4J"/>-->
<!--是否开启自动驼峰命名规则-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- </settings>-->
<!--类型别名-->
<typeAliases>
<package name="com.zhoupiyao.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方式-->
<mapper class="com.zhoupiyao.dao.TeacherMapper" />
<mapper resource="com/zhoupiyao/Mapper/TeacherMapper.xml"/>
</mappers>
</configuration>
4.配置工具类
MybatisUtils.java
package com.zhoupiyao.utils;
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;
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
//通过流的方式加载配置文件获取sqlSessionFactory,进而获取SqlSession对象
String resource = "Mybatis-config.xml";
try {
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
//参数表示事务自动提交
return sqlSessionFactory.openSession(true);
}
}
5.xxxMapper接口类
多对一实现(查询结果包含对象)
package com.zhoupiyao.dao;
import com.zhoupiyao.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface xxxMapper {
@Select("select * from xxx where xx=#{xx}")
Teacher getxxById(@Param("id") int id);
}
一对多实现(查询结果包含集合)
package com.zhoupiyao.dao;
import com.zhoupiyao.pojo.Teacher;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface TeacherMapper {
//查询所有教师的接口
Teacher getTeacherById(int tid);
@Select("select * from teacher")
List<Teacher> getTeachers();
}
6.xxxMapper配置文件
多对一实现(其中包含两种联表查询方式)
<?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.zhoupiyao.dao.StudentMapper">
<!--
两种联表查询方法(查询结果包含对象):
1.子查询嵌套查询结果
2.联表查询后处理结果
-->
<!--1.子查询嵌套联表查询-->
<select id="getStudentsByXml" resultMap="StuMap">
select * from student
</select>
<select id="getTeacher" resultType="Teacher">
select * from teacher where tid=#{tid}
</select>
<resultMap id="StuMap" type="Student" >
<result column="id" property="id"></result>
<result column="name" property="name"></result>
<result column="age" property="age"></result>
<!--对象对应association,返回类型用javaType标识-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"></association>
</resultMap>
<!--2.联表查询后处理结果-->
<select id="getStudentsByXml2" resultMap="StusMap">
select s.id as sid,s.name as sname,s.age as sage,t.name as tname,t.tid as tid,t.age as tage
from student s,teacher t
where s.tid=t.tid
</select>
<!--property对应实体类的属性,column对应查询结果列名-->
<resultMap id="StusMap" type="Student">
<result property="id" column="sid"></result>
<result property="name" column="sname"></result>
<result property="age" column="sage"></result>
<!--对象对应association,返回类型用javaType标识-->
<association property="teacher" javaType="Teacher">
<result property="tid" column="tid"></result>
<result property="name" column="tname"></result>
<result property="age" column="tage"></result>
</association>
</resultMap>
</mapper>
多对一实现(其中包含两种联表查询方式)
<?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.zhoupiyao.dao.TeacherMapper">
<!--
两种联表查询方法(查询结果包含集合):
1.联表查询后处理结果
2.子查询嵌套查询结果
-->
<!--1.联表查询后处理结果-->
<select id="getTeacherById" resultMap="teacherMap">
select t.tid,t.name,t.age,s.id,s.name as sname,s.age as sage
from teacher t,student s
where t.tid=s.tid and t.tid=#{tid}
</select>
<resultMap id="teacherMap" type="Teacher" >
<result property="tid" column="tid"></result>
<result property="name" column="name"></result>
<result property="age" column="age"></result>
<!--集合对应collection,返回类型用ofType标识-->
<collection property="students" ofType="Student">
<result property="id" column="id"></result>
<result property="name" column="sname"></result>
<result property="age" column="sage"></result>
</collection>
</resultMap>
<!--2.子查询嵌套查询结果-->
<select id="getTeacherByIdxml2" resultMap="teaMap">
select * from teacher where tid=#{tid}
</select>
<!--作为子查询语句-->
<select id="selectStu" resultType="Student">
select * from student where tid=#{tid}
</select>
<resultMap id="teaMap" type="Teacher">
<result property="tid" column="tid"></result>
<collection property="students" javaType="ArrayList"
ofType="Student" column="tid" select="selectStu"></collection>
</resultMap>
</mapper>
<!--1.JavaType用来指定实体类中属性的类型
2. ofType 用来指定映射到List或者集合中的pojo类型,泛型中的约束类型!
-->
二、Lombok
1.导入依赖
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
2.Lombok注解
@NonNull
@Cleanup
@Getter/@Setter
@ToString
@EqualsAndHashCode
@NoArgsConstructor/@RequiredArgsConstructor /@AllArgsConstructor
@Data
@Value
@SneakyThrows
@Synchronized
@Log
@NonNull
这个注解可以用在成员方法或者构造方法的参数前面,会自动产生一个关于此参数的非空检查,如果参数为空,则抛出一个空指针异常。
@Cleanup
这个注解用在变量前面,可以保证此变量代表的资源会被自动关闭,默认是调用资源的close()方法,如果该资源有其它关闭方法,可使用@Cleanup(“methodName”)来指定要调用的方法。
@Getter/@Setter
用在成员变量前面,相当于为成员变量生成对应的get和set方法,同时还可以为生成的方法指定访问修饰符,默认为public。
@ToString/@EqualsAndHashCode
这两个注解也比较好理解,就是生成toString,equals和hashcode方法,同时后者还会生成一个canEqual方法,用于判断某个对象是否是当前类的实例,生成方法时只会使用类中的非静态和非transient成员变量.当然,这两个注解也可以添加限制条件,例如用@ToString(exclude={“param1”,“param2”})来排除param1和param2两个成员变量,或者用@ToString(of={“param1”,“param2”})来指定使用param1和param2两个成员变量,@EqualsAndHashCode注解也有同样的用法。
@NoArgsConstructor/@RequiredArgsConstructor /@AllArgsConstructor
这三个注解都是用在类上的,为该类产生无参的构造方法和包含所有参数的构造方法,第二个注解则使用类中所有带有@NonNull注解的或者带有final修饰的成员变量生成对应的构造方法,当然,和前面几个注解一样,成员变量都是非静态的,另外,如果类中含有final修饰的成员变量,是无法使用@NoArgsConstructor注解的。
三个注解都可以指定生成的构造方法的访问权限,同时,第二个注解还可以@RequiredArgsConstructor(staticName=”methodName”)的形式生成一个指定名称的静态方法,返回一个调用相应的构造方法产生的对象。
@Data/@Value
@Data注解综合了3,4,5和6里面的@RequiredArgsConstructor注解,其中@RequiredArgsConstructor使用了类中的带有@NonNull注解的或者final修饰的成员变量,它可以使用@Data(staticConstructor=”methodName”)来生成一个静态方法,返回一个调用相应的构造方法产生的对象。
@Value注解和@Data类似,区别在于它会把所有成员变量默认定义为private final修饰,并且不会生成set方法。
@SneakyThrows
这个注解用在方法上,可以将方法中的代码用try-catch语句包裹起来,捕获异常并在catch中用Lombok.sneakyThrow(e)把异常抛出,可以使用@SneakyThrows(Exception.class)的形式指定抛出哪种异常。
@Synchronized
这个注解用在类方法或者实例方法上,效果和synchronized关键字相同,区别在于锁对象不同,对于类方法和实例方法,synchronized关键字的锁对象分别是类的class对象和this对象,而@Synchronized得锁对象分别是私有静态final对象LOCK和私有final对象LOCK和私有final对象lock,当然,也可以自己指定锁对象。
@Log
这个注解用在类上,可以省去从日志工厂生成日志对象这一步,直接进行日志记录,具体注解根据日志工具的不同而不同,同时,可以在注解中使用topic来指定生成log对象时的类名。不同的日志注解总结如下(上面是注解,下面是实际作用):
@CommonsLog
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
@JBossLog
private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
@Log
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
@Log4j
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
@Log4j2
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
@Slf4j
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
@XSlf4j
private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
三、日志处理
1.配置日志文件属性文件
log4j.properities
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
#控制控制台日志输出级别(改为ERROR可以提升输出级别,控制台会比较干净)
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/mybatis.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
2.修改配置文件
Mybatis-config.xml
<!--在configuration标签下添加此标签-->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
四、动态SQL
所谓的动态SQL只不过我们可以在SQL层面,去执行一些,根据不同条件产生不同的SQL语句。
关于驼峰命名
<settings>
<!--是否开启驼峰命名自动映射,pojo属性名aColumn映射到数据库A_COLUMN-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
1.if、where
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除,若子句的开头需要AND或OR,where 元素也会默认将它们添加,所以如果使用where 元素一般不用添加AND或OR。
if 最常见情景是根据条件包含where 子句的一部分,比如:
<select id="getBlogsByIf" parameterType="map" resultType="Blog">
select * from blog
<where>
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</where>
</select>
2.choose、when、otherwise
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。例如:
<select id="getBlogsIf2" parameterType="map" resultType="Blog">
select * from blog
<where>
<!--只会选择其中的一个条件-->
<choose>
<when test="title != null">
title=#{title}
</when>
<when test="author != null">
author=#{author}
</when>
<!--如果when中没有符合条件的,执行otherwise-->
<otherwise>
views>0
</otherwise>
</choose>
</where>
</select>
3.trim、where、set
<update id="updateBlog" parameterType="map">
update blog
<!--set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号
(这些逗号是在使用条件语句给列赋值时引入的)-->
<set>
<if test="title != null">
title=#{title}
</if>
<if test="author != null">
author=#{author}
</if>
<if test="views != null">
views=#{views}
</if>
</set>
<where>
id=#{id}
</where>
</update>
where 元素等价的自定义 trim 元素为:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。
set 元素等价的自定义 trim 元素:
<trim prefix="SET" suffixOverrides=",">
...
</trim>
上述例子会移除所有 suffixOverrides属性中指定的内容,并且插入 prefix 属性中指定的内容。
4.sql片段
如果一块sql片段在多个方法中被重复使用,这时我们可以把重复的片段用sql标签做成一个sql片段并指定一个id,在使用时通过include标签引入该id的sql片段即可,例如:
定义以下sql片段
<sql id="select-title-author-views">
<if test="title != null">
title=#{title}
</if>
<if test="author != null">
author=#{author}
</if>
<if test="views != null">
views=#{views}
</if>
</sql>
引用sql片段
<include refid="select-title-author-views"></include>
5.foreach
动态 SQL 的另一个常见使用场景是对集合进行遍历。比如:
<select id="getBlogsByforEach" parameterType="map" resultType="Blog">
select *
from blog b
<where>
<!--ids数组存在于map集合中-->
<foreach item="id" collection="ids"
open="(" separator="or" close=")">
id=#{id}
</foreach>
</where>
</select>
<!--该sql等价于select * from blog b where (id=1 or id=2 or...)-->
五、缓存
1.一级缓存
同一个 SqlSession 对象, 在参数和 SQL 完全一样的情况下, 只执行一次 SQL 语句(如果缓存没有过期)
1.会话在查询数据时,会把查询到的数据放入一级缓存中,当会话被提交或关闭时,若开启了二级缓存就会把数据放入二级缓存中。
2.不同的 SqlSession 之间的缓存是相互隔离的;
3.用一个 SqlSession,可以通过配置使得在查询前清空缓存,例如添加flushCache=“true”属性;
4.任何的增删改操作 UPDATE, INSERT, DELETE 语句都会清空缓存;
2.二级缓存
二级缓存存在于 SqlSessionFactory 生命周期中
在 mybatis 中, 二级缓存有全局开关和分开关, 全局开关, 在 mybatis-config.xml 中如下配置:
<settings>
<!--全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 -->
<setting name="cacheEnabled" value="true"/>
</settings>
分开关就是说在 *Mapper.xml 中开启或关闭二级缓存, 默认是不开启的,开启只需要在你的 SQL 映射文件中添加一行:
<cache/>
也可自定义一些属性,如下表示创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
1.eviction清除策略包括:
LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
2.flushInterval(刷新间隔) 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
3.size(引用数目) 属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
4.readOnly(只读) 只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
提示 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。
缓存原理

3.自定义缓存 -ehcache
3.1、导入依赖
mybatis-ehcache依赖
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
slf4j-log4j12依赖
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
3.2、配置文件
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"
updateCheck="false">
<!--数据在磁盘中的存储位置-->
<diskStore path="./ehcache/ehcache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
<!--
属性说明:
diskStore:指定数据在磁盘中的存储位置。
defaultCache:当借助 CacheManager.add(“demoCache”) 创建Cache时,EhCache 便会采用指定的的管理策略
以下属性是必须的:
maxElementsInMemory - 在内存中缓存的element的最大数目
maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
overflowToDisk- 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
以下属性是可选的:
timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
diskPersistent- 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
-->
3.3、使用自定义缓存
<!--在Mapper.xml中使用二级缓存-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
参考文档 : https://mybatis.org/mybatis-3/zh/getting-started.html

浙公网安备 33010602011771号