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

posted @ 2021-03-16 12:01  小新超人  阅读(101)  评论(0)    收藏  举报