ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Mybatis - 基础篇

2021-09-16 15:34:46  阅读:116  来源: 互联网

标签:语句 缓存 映射 基础 MyBatis SQL Mybatis 属性


文章目录

Mybatis - 基础篇

⭐简述

mybatis – MyBatis 3 | 简介 - 官方地址

可以对着官方地址来进行学习,本文大部分内容也引用此处

1 Mybatis是什么

一款持久层框架,也是半ORM(半自动化)框架,底层封装JDBC代码,可以让程序员更加专注于业务,不用过于在重复冗余的架构代码上浪费时间

2 ORM是什么

​ ORM(Object Relational Mapping),对象关系映射,是一种为了解决关系型数据库数据与简单Java对象(POJO)的映射关系的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系型数据库中。

3 Hibernate 和 MyBatis 的区别

相同点

底层都是对JDBC进行了封装,并且是持久化层框架

不同点

映射关系

  • Hibernate 是全自动ORM框架,Java对象和数据库表进行联系,在多表关系上处理较为复杂
  • Mybatis 是半自动ORM框架,Java对象和SQL执行结果进行联系,在多表关系上处理相对来说比Hibernate轻松

性能及移植性

  • Hibernate 对SQL语句完成封装(无需程序员手动编写),且支持市面上大多类型的数据库,但是性能消耗上比Mybatis较大,尤其是在越复杂的场景下越明显
  • Mybatis 需要手动编写SQL,开发工作量相对较大,但是可优化空间也相对较大,能够进行动态的SQL拼接、逆向工程、第三方缓存支持,不支持数据库无关性

总结

MyBatis 是一个小巧、方便、高效、简单、直接、半自动化的持久层框架

Hibernate 是一个强大、方便、高效、复杂、间接、全自动化的持久层框架

⭐入门案例

hello world demo

1 基于XML的构建方式

较为常用的构建方式,将SQL代码写在XML文本中

1.1 基本流程

  • db.properties 数据库的连接信息
  • mybatis-config.xml 数据源配置
  • Java实体类
  • Dao接口
  • Mapper.xml SQL语句编写

1.2 代码示例

db.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://47.120.146.140:3306/book
jdbc.username=***
jdbc.password=****

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表示配置Mybatis的开发环境,可以配置多个环境,
    在众多具体环境中,使用default属性指定实际运行时使用的环境 -->
    <properties resource="dbconfig/db.properties"></properties>
     <settings>
        <!--全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    <environments default="mysql">

        <!-- environment表示配置Mybatis的一个具体的环境 -->
        <environment id="mysql">

            <!-- Mybatis的内置的事务管理器 -->
            <transactionManager type="JDBC"/>

            <!-- 配置数据源 -->
            <dataSource type="POOLED">
                <!--
                数据源有三种
                UNPLLED:即不使用连接池方式,每次都重新建一个链接
                POOLED:使用连接池
                JNDI:使用JNDI方式链接
                 -->

                <!-- 建立数据库连接的具体信息 -->
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>

        <!-- 指定Mybatis映射文件的具体位置 就是你写sql的那个文件-->
        <mapper resource="sql/UserMapper.xml"/>
    </mappers>
</configuration>

UserMapper.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">
<!--SQL语句配置文件,需要在mybatis配置文件中进行加载-->
<!--mapper标签用于做SQL语句和方法之间的映射-->
<mapper namespace="main.UserDao">

    <select id="getAll" resultType="main.User">
        select USERNAME from USER;
    </select>

    <insert id="insert" parameterType="main.User">
        INSERT INTO USER VALUES (#{id},#{userName},#{jobS},#{phone});
    </insert>

    <delete id="deleteUser" parameterType="int">
        DELETE FROM USER WHERE id = #{id};
    </delete>
</mapper>

UserDao.java

import java.util.List;
public interface UserDao {
    List<User> getAll();

    int insert(User userName);

    int deleteUser(Integer id);

    List<USER> findByName(String value);
}

User.java

@Data
public class User {
    private Integer id;
    private String userName;
    private String jobS;
    private String phone;
}

Test测试方法

public class Run {
    public static void main(String[] args) throws IOException {
        String configPath = "mybatis-config.xml";
        // 通过输入流读取XML文件
        // 创建sql会话的工厂对象
        InputStream resourceAsStream = Resources.getResourceAsStream(configPath);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        // 利用工厂对象,打开一个SQL会话
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 利用反射读取接口的字节码文件,返回接口对象
        UserDao mapper = sqlSession.getMapper(UserDao.class);

        // 通过这个对象调用方法,这个方法跟 Mapper.xml 会进行关联
        List<User> UserList = mapper.getAll();
        UserList.forEach(System.out::println);
    }
}

2 基于注解的构建方式

由于通过注解的构建方式在企业不常见,在这里也不去进行过多的阐述,相关的注解开发支持在官网上的介绍也比较少,所以我引入了这篇其它博主写的博客可以了解一下

mybatis基于注解实现增删改查-安琪拉2020的CSDN博客

⭐配置/映射文件解析

此章节主要针对日常Mapper映射/配置文件内的元素进行简单说明

1 配置文件

这里的配置较为复杂,后期将由SpringBoot application.yaml文件进行代替

1.1 属性

这里的属性代表的大概有如下几点,其中之一就是数据库的连接信息

db.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://47.120.146.140:3306/book
jdbc.username=root
jdbc.password=szsti@123

mybatis-config.xml

<!-- dataSource:数据源配置 -->
<dataSource type="POOLED">
  <property name="driver" value="${driver}"/>
  <property name="url" value="${url}"/>
  <property name="username" value="${username}"/>
  <property name="password" value="${password}"/>
</dataSource>

如果一个属性在不只一个地方进行了配置,那么,MyBatis 将按照下面的顺序来加载:

  • 首先读取在 properties 元素体内指定的属性
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性
  • 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性

因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性

甚至,在属性配置上可以添加占位参数指定默认值,但是我觉得用处不大就是了,这里不作说明

1.2 设置

示例

<settings>
    <!-- 开启二级缓存 -->
  <setting name="cacheEnabled" value="true"/>
    <!-- 延迟加载 -->
  <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 单语句返回多结果集 -->
  <setting name="multipleResultSetsEnabled" value="true"/>
    <!-- 使用列标签代替列名 -->
  <setting name="useColumnLabel" value="true"/>
    <!-- 主键自生成 -->
  <setting name="useGeneratedKeys" value="false"/>
    <!-- 字段自动映射 -->
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

属性说明

设置名描述有效值默认值
cacheEnabled全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。true | falsetrue
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。true | falsefalse
aggressiveLazyLoading开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。true | falsefalse (在 3.4.1 及之前的版本中默认为 true)
multipleResultSetsEnabled是否允许单个语句返回多结果集(需要数据库驱动支持)。true | falsetrue
useColumnLabel使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。true | falsetrue
useGeneratedKeys允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。true | falseFalse
autoMappingBehavior指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。NONE, PARTIAL, FULLPARTIAL
autoMappingUnknownColumnBehavior指定发现自动映射目标未知列(或未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARNFAILING: 映射失败 (抛出 SqlSessionException)NONE, WARNING, FAILINGNONE
defaultExecutorType配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。SIMPLE REUSE BATCHSIMPLE
defaultStatementTimeout设置超时时间,它决定数据库驱动等待数据库响应的秒数。任意正整数未设置 (null)
defaultFetchSize为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。任意正整数未设置 (null)
defaultResultSetType指定语句默认的滚动策略。(新增于 3.5.2)FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置)未设置 (null)
safeRowBoundsEnabled是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。true | falseFalse
safeResultHandlerEnabled是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。true | falseTrue
mapUnderscoreToCamelCase是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。true | falseFalse
localCacheScopeMyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。SESSION | STATEMENTSESSION
jdbcTypeForNull当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。OTHER
lazyLoadTriggerMethods指定对象的哪些方法触发一次延迟加载。用逗号分隔的方法列表。equals,clone,hashCode,toString
defaultScriptingLanguage指定动态 SQL 生成使用的默认脚本语言。一个类型别名或全限定类名。org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5)一个类型别名或全限定类名。org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。true | falsefalse
returnInstanceForEmptyRow当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2)true | falsefalse
logPrefix指定 MyBatis 增加到日志名称的前缀。任何字符串未设置
logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING未设置
proxyFactory指定 Mybatis 创建可延迟加载对象所用到的代理工具。CGLIB | JAVASSISTJAVASSIST (MyBatis 3.3 以上)
vfsImpl指定 VFS 的实现自定义 VFS 的实现的类全限定名,以逗号分隔。未设置
useActualParamName允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1)true | falsetrue
configurationFactory指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3)一个类型别名或完全限定类名。未设置
shrinkWhitespacesInSql从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5)true | falsefalse
defaultSqlProviderTypeSpecifies an sql provider class that holds provider method (Since 3.5.6). This class apply to the type(or value) attribute on sql provider annotation(e.g. @SelectProvider), when these attribute was omitted.A type alias or fully qualified class nameNot set

这些都是引入官网的资料,总结来说,主要还是针对数据库的相关配置,如缓存,超时控制等,以及第三方的集成

1.3 类型别名

​ 当在xml中填写类型名称时,通常会用全限定类名进行编写,而类型别名就是为了简化代码量,将全限定类名的编写简化,并且Mybatis也有针对通用数据类型做好内置的类型别名,我觉得可有可无还是小鸡肋的,但是这个还是因人而异吧,所以这里我就不作阐述了,See 官网

1.4 类型处理器

​ 当我们调用select查询单个对象时,从数据库查询到的字段总能自动为我们赋值到JavaBean上,其中就是利用到了类型处理器,当然平时我们也会去通过resultMap完成手动映射,我认为方法有很多,如果针对Mybatis无法处理的数据类型,我们也可以自定义类型处理器

具体做法为:实现 org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler

重学Mybatis:类型处理器,这个你得会玩! - 知乎 (zhihu.com)

mybatis – MyBatis 3 | 配置 | 类型处理器

1.5 对象工厂

“我们在使用MyBatis执行查询语句的时候,通常都会有一个返回类型,这个是在mapper文件中给sql增加一个resultType(或resultMap)属性进行控制。resultType和resultMap都能控制返回类型,只要定义了这个配置就能自动返回我想要的结果”,返回结果的新实例就由对象工厂创建

MyBatis配置文件(五)--objectFactory对象工厂 - bug改了我 - 博客园 (cnblogs.com)

1.6 插件

又称为拦截器,采用责任链模式,动态的组织多个插件(拦截器)形成链路,默认情况下允许使用自定义插件拦截的方法如下

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

    执行器

  • ParameterHandler (getParameterObject, setParameters)

    参数处理

  • ResultSetHandler (handleResultSets, handleOutputParameters)

    结果集处理

  • StatementHandler (prepare, parameterize, batch, update, query)

    SQL语法构建处理

1.7 环境配置

1

1.8 数据库厂商标识

1

1.9 映射器

1

2 映射文件

该章节主要说明 xxxMapper.xml,即SQL文件

2.1 SQL映射文件的顶级元素

  • cache – 该命名空间的缓存配置。
  • cache-ref – 引用其它命名空间的缓存配置。
  • resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
  • resultType - SQL语句结果返回类型
  • parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
  • parameterType - SQL语句输入参数类型
  • sql – 可被其它语句引用的可重用语句块。
  • insert – 映射插入语句。
  • update – 映射更新语句。
  • delete – 映射删除语句。
  • select – 映射查询语句。

2.2 Select

一个简单查询的 select 元素举例

<select id="selectPerson" parameterType="int" resultType="hashmap">
  SELECT * FROM PERSON WHERE ID = #{id}
</select>

这个语句名为 selectPerson,接受一个 int(或 Integer)类型的参数,并返回一个 HashMap 类型的对象,其中的键是列名,值便是结果行中的对应值。

注意参数符号:

#{id}

这就告诉 MyBatis 创建一个预处理语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL 中会由一个占位符“?”来标识,并被传递到一个新的预处理语句中。

Select 元素的属性

属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
parameterMap用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。
resultType期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
resultMap对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
useCache将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
fetchSize这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。 默认值为未设置(unset)(依赖驱动)。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetTypeFORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。
resultOrdered这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。 这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false
resultSets这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。

2.3 insert, update 和 delete

跟Select玩法相同,下面查看Insert, Update, Delete 元素的属性

属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
parameterMap用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。
keyProperty(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn(仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。

2.4 SQL重用

这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值。比如:

<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>

这个 SQL 片段可以在其它语句中使用,例如:

<select id="selectUsers" resultType="map">
  select
    <include refid="userColumns"><property name="alias" value="t1"/></include>,
    <include refid="userColumns"><property name="alias" value="t2"/></include>
  from some_table t1
    cross join some_table t2
</select>

(这个太复杂了我觉得不常用)也可以在 include 元素的 refid 属性或内部语句中使用属性值,例如:

<sql id="sometable">
  ${prefix}Table
</sql>

<sql id="someinclude">
  from
    <include refid="${include_target}"/>
</sql>

<select id="select" resultType="map">
  select
    field1, field2, field3
  <include refid="someinclude">
    <property name="prefix" value="Some"/>
    <property name="include_target" value="sometable"/>
  </include>
</select>

2.5 主键返回

通常我们会将数据库表的主键id设为自增

在插入一条记录时,我们不设置其主键id,而让数据库自动生成该条记录的主键id

那么在插入一条记录后,如何得到数据库自动生成的这条记录的主键id呢?

  • 使用useGeneratedKeyskeyProperty属性
<insert id="insert" parameterType="com.yogurt.po.Student" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO student (name,score,age,gender) VALUES (#{name},#{score},#{age},#{gender});
    </insert>
  • 使用<selectKey>子标签
<insert id="insert" parameterType="com.yogurt.po.Student">
        INSERT INTO student (name,score,age,gender) VALUES (#{name},#{score},#{age},#{gender});
        <selectKey keyProperty="id" order="AFTER" resultType="int" >
            SELECT LAST_INSERT_ID();
        </selectKey>
    </insert>

如果使用的是mysql这样的支持自增主键的数据库,可以简单的使用第一种方式;

对于不支持自增主键的数据库,如oracle,则没有主键返回这一概念,而需要在插入之前先生成一个主键。此时可以用标签,设置其order属性为BEFORE,并在标签体内写上生成主键的SQL语句,这样在插入之前,会先处理,生成主键,再执行真正的插入操作。

标签其实就是一条SQL,这条SQL的执行,可以放在主SQL执行之前或之后,并且会将其执行得到的结果封装到入参的Java对象的指定属性上。注意子标签只能用在和标签中。上面的LAST_INSERT_ID()实际上是MySQL提供的一个函数,可以用来获取最近插入或更新的记录的主键id。

selectKey 元素的属性

属性描述
keyPropertyselectKey 语句结果应该被设置到的目标属性。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn返回结果集中生成列属性的列名。如果生成列不止一个,可以用逗号分隔多个属性名称。
resultType结果的类型。通常 MyBatis 可以推断出来,但是为了更加准确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果生成列不止一个,则可以使用包含期望属性的 Object 或 Map。
order可以设置为 BEFOREAFTER。如果设置为 BEFORE,那么它首先会生成主键,设置 keyProperty 再执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 中的语句 - 这和 Oracle 数据库的行为相似,在插入语句内部可能有嵌入索引调用。
statementType和前面一样,MyBatis 支持 STATEMENTPREPAREDCALLABLE 类型的映射语句,分别代表 Statement, PreparedStatementCallableStatement 类型。

2.6 占位的参数

为什么要用@Param - CSDN博客

先举个例子

<insert id="insertUser" parameterType="User">
  insert into users (id, username, password)
  values (#{id}, #{username}, #{password})
</insert>

如果 User 类型的参数对象传递到了语句中,会查找 id、username 和 password 属性,然后将它们的值传入预处理语句的参数中。

对传递语句参数来说,这种方式真是干脆利落。不过参数映射的功能远不止于此。

下面的比较少用,看看就行

首先,和 MyBatis 的其它部分一样,参数也可以指定一个特殊的数据类型。

# { property , javaType = int , jdbcType = NUMERIC }

注意JDBC 要求,如果一个列允许使用 null 值,并且会使用值为 null 的参数,就必须要指定 JDBC 类型(jdbcType)

要更进一步地自定义类型处理方式,可以指定一个特殊的类型处理器类(或别名),比如:

#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}

参数的配置好像越来越繁琐了,但实际上,很少需要如此繁琐的配置。

对于数值类型,还可以设置 numericScale 指定小数点后保留的位数。

#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}

最后,mode 属性允许你指定 INOUTINOUT 参数。如果参数的 modeOUTINOUT,将会修改参数对象的属性值,以便作为输出参数返回。 如果 modeOUT(或 INOUT),而且 jdbcTypeCURSOR(也就是 Oracle 的 REFCURSOR),你必须指定一个 resultMap 引用来将结果集 ResultMap 映射到参数的类型上。要注意这里的 javaType 属性是可选的,如果留空并且 jdbcType 是 CURSOR,它会被自动地被设为 ResultMap

#{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentResultMap}

MyBatis 也支持很多高级的数据类型,比如结构体(structs),但是当使用 out 参数时,你必须显式设置类型的名称。比如(再次提示,在实际中要像这样不能换行):

#{middleInitial, mode=OUT, jdbcType=STRUCT, jdbcTypeName=MY_TYPE, resultMap=departmentResultMap}

尽管上面这些选项很强大,但大多时候,你只须简单指定属性名,顶多要为可能为空的列指定 jdbcType,其他的事情交给 MyBatis 自己去推断就行了。

#{firstName}
#{middleInitial,jdbcType=VARCHAR}
#{lastName}

2.7 字符串替换

${columnName}

容易被SQL攻击,尽量就不用,我喜欢用concat( ‘%’, XXXX ,’%’ )这个函数解决

⭐结果映射

主要讲解resultMap的用法,针对一些比较复杂的结果返回,用普通的JavaBean可能无法接收,应对与多表联查的场景

1 配置说明

主标签:

  • resultMap:用于映射,类似于解决起别名的问题

  • association:对象名称

  • collection:集合名称

  • discriminator:鉴别器

子标签:

  • property:类属性名
  • column:数据库字段名
  • javaType:结果类型,写这个类的包路径,搭配association使用,或者搭配discriminator使用
  • ofType:集合中的泛型类,搭配collection使用
  • select:

2 普通映射(一对一)

java

@Data
@AllArgsConstructor
@NoArgsConstructor
public class TestDao {
    private String studentName;
    private String courseName;
}

xml

<select id="arrayTestDaoList" resultMap="TestDemo01">
        SELECT b.stu_name as studentname,
               a.cou_name as coursename
        FROM curricula x
                 JOIN course a ON a.cou_id = x.cou_id
                 JOIN student b ON b.stu_id = x.stu_id
</select>
    
<resultMap id="TestDemo01" type="JavaBean.TestDao">
        <result property="studentName" column="studentname"/>
        <result property="courseName" column="coursename"/>
</resultMap>

或者也可以在SQL语句中用起别名的方式,让mybatis自动为我们映射

3 association(一对一)

太简单了不想写,大概就是对象名的一个映射,跟上面的差不多

<?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="main.StudentMapper">
    <!-- 多表关联处理方式一 -->
    <!-- resultMap的名字随便起,后续是需要对这个ID进行配置的 -->
    <select id="getStu" resultMap="StudentUser">
        select *
        from Student;
    </select>

    <!--    property:JavaBean
              column:DataBase   -->
    <resultMap id="StudentUser" type="main.Student">
        <id property="" column="主键">
        <result property="studentId" column="StudentId"/>
        <result property="studentName" column="StudentName"/>
        <result property="teacherId" column="TeacherId"/>
        <!--    association:对象  column:数据库对应列名  javaType:结果类型
                 collection:集合    -->
        <association property="sUser" column="TeacherId" javaType="main.User" select="main.UserMapper.getId"/>
    </resultMap>

    <!-- 多表关联处理方式二:根据结果嵌套处理 -->
    <select id="getStuUserResult" resultMap="StudentUser2">
        select StudentName,
               TeacherId,
               ID,
               USERNAME
        from Student,
             USER
        where Student.TeacherId = USER.ID;
    </select>

    <resultMap id="StudentUser2" type="main.Student">
        <result property="studentName" column="StudentName" />
        <result property="teacherId" column="TeacherId" />
         <!--    association:对象  column:数据库对应列名  javaType:结果类型 property:属性名
                 collection:集合   ofType:集合里面的泛型类
-->
        <association property="sUser" column="main.User" javaType="main.User">
            <result property="id" column="ID" />
            <result property="userName" column="USERNAME" />
        </association>
    </resultMap>
</mapper>

4 collection(多对多)

首先谈谈我实验后的结果逻辑

他是一个一对多的场景,例如,一个学生选择了很多门课程,如下


学生编号 学生姓名 课程编号 课程名称
101 张三 11 大数据
101 张三 12 人工智能
102 李四 12 人工智能
102 李四 15 物联网
102 李四 16 厨师
103 王五 13 云计算
103 王五 14 路由交换


张三选择了三门两门课程,李四是三门课程,所以他是需要用一个列表去接收数据,一个对象课程不太够

下面上代码看看

Student.java

public class Student {
    private Integer stuId;
    private String stuName;
    private List<Course> courseList;
    // 一个学生类,包含学生姓名、编号,以及他选择的课程列表
}

public class Course {
    private Integer couId;
    private String couName;
}

StudentMapper接口

public interface StudentMapper {
    List<Student> studentList();
}

xml文件

<select id="studentList" resultMap="TestDemo01">
        SELECT b.stu_id   as stuId,
               b.stu_name as stuName,
               a.cou_id   as couId,
               a.cou_name as couName
        FROM curricula x
                 JOIN course a ON a.cou_id = x.cou_id
                 JOIN student b ON b.stu_id = x.stu_id
        ORDER BY b.stu_id
    </select>

    <resultMap id="TestDemo01" type="JavaBean.Student">
        <id property="stuId" column="stuId"/>
        <result property="stuName" column="stuName"/>
        <collection property="courseList" ofType="JavaBean.Course">
            <id property="couId" column="couId"/>
            <result property="couName" column="couName"/>
        </collection>
    </resultMap>

5 分步查询

6 自动映射

再说吧。。。。。我觉得跟hibernate是一个类型的意思

7 discriminator(鉴别器)

(2条消息) MyBatis级联探讨第二篇——鉴别器(discriminator)_ykzhen2015的专栏-CSDN博客

感觉挺鸡肋的,是多对多查询的一个补充,根据查询的结果再去嵌套判断,晚点再做补充

⭐动态SQL

主要用于字符串的拼接,这里我引用官网的片段

包括下面几个参数

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

1 基本玩法

if

使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:

<select id="findActiveBlogWithTitleLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
</select>

这条语句提供了可选的查找文本功能。如果不传入 “title”,那么所有处于 “ACTIVE” 状态的 BLOG 都会返回;如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果(细心的读者可能会发现,“title” 的参数值需要包含查找掩码或通配符字符)。

如果希望通过 “title” 和 “author” 两个参数进行可选搜索该怎么办呢?首先,我想先将语句名称修改成更名副其实的名称;接下来,只需要加入另一个条件即可。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

choose、when、otherwise

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

还是上面的例子,但是策略变为:传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG(这可能是管理员认为,与其返回大量的无意义随机 Blog,还不如返回一些由管理员精选的 Blog)。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

where

前面几个例子已经方便地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if” 示例,这次我们将 “state = ‘ACTIVE’” 设置成动态条件,看看会发生什么。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE
  <if test="state != null">
    state = #{state}
  </if>
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:

SELECT * FROM BLOG
WHERE

这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:

SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’

这个查询也会失败。这个问题不能简单地用条件元素来解决。这个问题是如此的难以解决,以至于解决过的人不会再想碰到这种问题。

MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

trim

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。

set

用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

来看看与 set 元素等价的自定义 trim 元素吧:

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

注意,我们覆盖了后缀值设置,并且自定义了前缀值。

foreach

关于 mybatis 中 in 写法, 参数详解 - 雪化山河 - 博客园 (cnblogs.com)

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符。

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

item 集合中每一个元素进行迭代时的别名,
index 表示在迭代过程中,每次迭代到的位置,
open 该语句以什么开始,

separator 在每次进行迭代之间以什么符号作为分隔 符,
close 以什么结束,

在使用foreach的时候最关键的也是最容易出错的就是collection属性,
该属性是必须指定的,但是在不同情况 下,该属性的值是不一样的,
主要有一下3种情况:

  1. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
    
  2. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
    
  3. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了
    

2 其他

sql

使用sql标签抽取重复出现的SQL片段

  <!-- 使用sql标签抽取重复出现的SQL片段 -->
<sql id="empSelectColumns">
     emp_id empId,emp_name  empName ,emp_salary empSalary
</sql>

使用include标签引用声明的SQL片段

   <!-- 使用include标签引用声明的SQL片段 -->
<select id="findEmp" resultType="employee">
    select <include refid="empSelectColumns"></include>  from t_emp   where 1=1
    <if test="empName!=null and empName !=''">
       and emp_name like concat("%",#{empName},"%")
    </if>
    <if test="minSalary>0">
        and emp_salary>=#{minSalary}
    </if>
</select>

script(注解)

要在带注解的映射器接口类中使用动态 SQL,可以使用 script 元素。比如

    @Update({"<script>",
      "update Author",
      "  <set>",
      "    <if test='username != null'>username=#{username},</if>",
      "    <if test='password != null'>password=#{password},</if>",
      "    <if test='email != null'>email=#{email},</if>",
      "    <if test='bio != null'>bio=#{bio}</if>",
      "  </set>",
      "where id=#{id}",
      "</script>"})
    void updateAuthorValues(Author author);

bind(OGNL)

bind 元素允许你在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文。比如

<select id="selectBlogsLike" resultType="Blog">
  <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
  SELECT * FROM BLOG
  WHERE title LIKE #{pattern}
</select>

多数据库支持

如果配置了 databaseIdProvider,你就可以在动态代码中使用名为 “_databaseId” 的变量来为不同的数据库构建特定的语句。比如下面的例子

<insert id="insert">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    <if test="_databaseId == 'oracle'">
      select seq_users.nextval from dual
    </if>
    <if test="_databaseId == 'db2'">
      select nextval for seq_users from sysibm.sysdummy1"
    </if>
  </selectKey>
  insert into users values (#{id}, #{name})
</insert>

动态 SQL 中的插入脚本语言

MyBatis 从 3.2 版本开始支持插入脚本语言,这允许你插入一种语言驱动,并基于这种语言来编写动态 SQL 查询语句。

可以通过实现以下接口来插入一种语言

public interface LanguageDriver {
  ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
  SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);
  SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);
}

实现自定义语言驱动后,你就可以在 mybatis-config.xml 文件中将它设置为默认语言:

<typeAliases>
  <typeAlias type="org.sample.MyLanguageDriver" alias="myLanguage"/>
</typeAliases>
<settings>
  <setting name="defaultScriptingLanguage" value="myLanguage"/>
</settings>

或者,你也可以使用 lang 属性为特定的语句指定语言:

<select id="selectBlog" lang="myLanguage">
  SELECT * FROM BLOG
</select>

或者,在你的 mapper 接口上添加 @Lang 注解:

public interface Mapper {
  @Lang(MyLanguageDriver.class)
  @Select("SELECT * FROM BLOG")
  List<Blog> selectBlog();
}

提示 可以使用 Apache Velocity 作为动态语言,更多细节请参考 MyBatis-Velocity 项目。

你前面看到的所有 xml 标签都由默认 MyBatis 语言提供,而它由语言驱动 org.apache.ibatis.scripting.xmltags.XmlLanguageDriver(别名为 xml)所提供。

⭐缓存

1 一级缓存

基本说明

mybatis的缓存只有两种

  • 一级
  • 二级

一级缓存下,如果涉及到修改数据,就会默认刷掉

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

一级缓存是不能关闭的,因为mybatis的一些关键特性都是基于一级缓存来做的,其结果映射重度依赖一级缓存,所以不让关,但是可以修改配置

MyBatis提供了一个配置参数localCacheScope,用于控制一级缓存的级别

该参数的取值为SESSION、STATEMENT,当指定localCacheScope参数值为SESSION时,缓存对整个SqlSession有效,只有执行DML语句(更新语句)时,缓存才会被清除。当localCacheScope值为STATEMENT时,缓存仅对当前执行的语句有效,当语句执行完毕后,缓存就会被清空。

所以主要讲解二级缓存

查看缓存效果

加入了log日志后,如果直接进行两次查询,会发现日志中只有一次查询语句,因为第二次调用了缓存

[19:14:03.417] [DEBUG] [main] [mapper.EmpMapper.selectAll] [==>  Preparing: select empno, ename, job, mgr, hiredate, sal, comm, deptno from emp]
[19:14:03.443] [DEBUG] [main] [mapper.EmpMapper.selectAll] [==> Parameters: ]
[19:14:03.480] [DEBUG] [main] [mapper.EmpMapper.selectAll] [<==      Total: 14]

当我们在sql文件中给这条sql语句加上强制刷新参数flushCache="true"时,就会产生这样的效果

[19:10:47.424] [DEBUG] [main] [mapper.EmpMapper.selectAll] [==>  Preparing: select empno, ename, job, mgr, hiredate, sal, comm, deptno from emp]
[19:10:47.450] [DEBUG] [main] [mapper.EmpMapper.selectAll] [==> Parameters: ]
[19:10:47.477] [DEBUG] [main] [mapper.EmpMapper.selectAll] [<==      Total: 14]
[19:10:47.479] [DEBUG] [main] [mapper.EmpMapper.selectAll] [==>  Preparing: select empno, ename, job, mgr, hiredate, sal, comm, deptno from emp]
[19:10:47.479] [DEBUG] [main] [mapper.EmpMapper.selectAll] [==> Parameters: ]
[19:10:47.490] [DEBUG] [main] [mapper.EmpMapper.selectAll] [<==      Total: 14]

总结

  • 在同一个 SqlSession 中, Mybatis 会把执行的方法和参数通过算法生成缓存的键值, 将键值和结果存放在一个 Map 中, 如果后续的键值一样, 则直接从 Map 中获取数据
  • 不同的 SqlSession 之间的缓存是相互隔离的
  • 用一个 SqlSession, 可以通过配置使得在查询前清空缓存
  • 任何的 UPDATE, INSERT, DELETE 语句都会清空缓存

2 开启mybatis内置二级缓存

基本说明

mybatis-config.xml中properties参数后开启全局二级缓存

<settings>
        <!--全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 -->
        <setting name="cacheEnabled" value="true"/>
</settings>

开启二级缓存方法(要让实体类继承序列化接口

<!-- 在mapper.xml命名空间下加入cache标签启用二级缓存功能 -->
<cache/>

还可以配置缓存策略、缓存刷新频率、缓存的容量等属性

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

可用的清除策略有:

  • LRU – 最近最少使用:移除最长时间不被使用的对象。
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
  • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。

默认的清除策略是 LRU。

flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

提示 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新

示例代码

mybatis.xml

<configuration>
    <!-- environments表示配置Mybatis的开发环境,可以配置多个环境,
    在众多具体环境中,使用default属性指定实际运行时使用的环境 -->
    <properties resource="dbconfig/db.properties">
    </properties>
    <settings>
        <!--全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    <environments default="mysql">

mapper.xml

<mapper namespace="mapper.EmpMapper">
    <!-- 二级缓存分开关 -->
    <cache/>

开启后,效果如下(命中率0)

[19:33:55.425] [DEBUG] [main] [mapper.EmpMapper] [Cache Hit Ratio [mapper.EmpMapper]: 0.0]

6.2 MyBatis使用Redis缓存

其实Mybatis也很聪明,知道大多数用户都会用第三方缓存,所以对原生的二级缓存官方很少讲解

  • 加入mybatis-redis的JAR包
  • 在Mapper的XML配置文件添加缓存配置

(1条消息) mybatis与redis整合_蜗牛学习笔记-CSDN博客_mybatis redis

6.3 Mybatis使用EHCache

(1条消息) Mybatis整合第三方缓存ehcache_林海静的博客-CSDN博客

1、加入依赖包

2、提供配置文件

3、cache type添加二级缓存为EHCache

4、添加日志桥梁

5、运行测试

⭐日志

加入后可以查看到执行的SQL语句以及效率相关等情况

1 简述

2 log4j

操作流程

  • 导包
  • 编写配置文件log4j
  • 记得把log4j放在资源根目录下

示例代码

pom.xml
<?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>org.example</groupId>
    <artifactId>03_mybatis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.4.0</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.2.3</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>
    </dependencies>
</project>
log4j.properties
#############
# 输出到控制台
#############
# log4j.rootLogger日志输出类别和级别:只输出不低于该级别的日志信息DEBUG < INFO < WARN < ERROR < FATAL
# WARN:日志级别     CONSOLE:输出位置自己定义的一个名字       logfile:输出位置自己定义的一个名字
log4j.rootLogger=DEBUG,CONSOLE,logfile
# 配置CONSOLE输出到控制台
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
# 配置CONSOLE设置为自定义布局模式
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
# 配置CONSOLE日志的输出格式  [frame] 2019-08-22 22:52:12,000  %r耗费毫秒数 %p日志的优先级 %t线程名 %C所属类名通常为全类名 %L代码中的行号 %x线程相关联的NDC %m日志 %n换行
log4j.appender.CONSOLE.layout.ConversionPattern=[frame] %d{yyyy-MM-dd HH:mm:ss,SSS} - %-4r %-5p [%t] %C:%L %x - %m%n
################
# 输出到日志文件中
################
# 配置logfile输出到文件中 文件大小到达指定尺寸的时候产生新的日志文件
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
# 保存编码格式
log4j.appender.logfile.Encoding=UTF-8
# 输出文件位置此为项目根目录下的logs文件夹中
log4j.appender.logfile.File=logs/root.log
# 后缀可以是KB,MB,GB达到该大小后创建新的日志文件
log4j.appender.logfile.MaxFileSize=10MB
# 设置滚定文件的最大值3 指可以产生root.log.1、root.log.2、root.log.3和root.log四个日志文件
log4j.appender.logfile.MaxBackupIndex=3
# 配置logfile为自定义布局模式
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n
##########################
# 对不同的类输出不同的日志文件
##########################
# club.bagedate包下的日志单独输出
#log4j.logger.club.bagedate=DEBUG,bagedate
# 设置为false该日志信息就不会加入到rootLogger中了
#log4j.additivity.club.bagedate=false
# 下面就和上面配置一样了
log4j.appender.bagedate=org.apache.log4j.RollingFileAppender
log4j.appender.bagedate.Encoding=UTF-8
log4j.appender.bagedate.File=logs/bagedate.log
log4j.appender.bagedate.MaxFileSize=10MB
log4j.appender.bagedate.MaxBackupIndex=3
log4j.appender.bagedate.layout=org.apache.log4j.PatternLayout
log4j.appender.bagedate.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n

3 logback

示例

加入jar包

<dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

配置logback.xml

切记文件放在资源根下

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 指定日志输出的位置 -->
    <appender name="STDOUT"
              class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- 日志输出的格式 -->
            <!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
            <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
        </encoder>
    </appender>

    <!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
    <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
    <root level="DEBUG">
        <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
        <appender-ref ref="STDOUT"/>
    </root>
    <!-- 根据特殊需求指定局部日志级别 -->
    <logger name="mapper.EmpMapper" level="DEBUG"/>
</configuration>

⭐逆向工程

非常实用的一个东西,在新建项目的时候,数据库里面存在非常多的表需要我们在Java去建类

如果一个个去建,就很累,所以衍生了逆向工程这个宝贝

1 操作流程

非常简单,两种方法,三个步骤,关键在于XML的配置

方法一:基于类执行

  • 导入JAR包

  • 配置XML文件

  • 执行测试类

方法二:基于Maven插件

  • 导入JAR包
  • 配置Maven插件
  • 配置XML文件
  • 执行Maven插件

还能用Mybatis-Plus插件去做,我打算在另外一篇md里面说明

2 代码示例

我只想演示方法一

导入JAR包
<!-- 逆向工程 -->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.4.0</version>
        </dependency>
XML配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!--
            targetRuntime: 执行生成的逆向工程的版本
                    MyBatis3Simple: 生成基本的CRUD(清新简洁版)
                    MyBatis3: 生成带条件的CRUD(奢华尊享版)
     -->
    <context id="DB_Ailyn" targetRuntime="MyBatis3">
        <!-- 数据库的连接信息 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://47.106.207.254:3306/mybatis"
                        userId="root"
                        password="szsti@123">
        </jdbcConnection>

        <!-- javaBean的生成策略-->
        <javaModelGenerator targetPackage="JavaBean" targetProject=".\src\main\java">
            <!-- 生成子包 -->
            <property name="enableSubPackages" value="true"/>
            <!-- 去掉前后空格 -->
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>

        <!-- SQL映射文件的生成策略 -->
        <sqlMapGenerator targetPackage="sql" targetProject=".\src\main\resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>

        <!-- Mapper接口的生成策略 -->
        <javaClientGenerator type="Mapper" targetPackage="mapper" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <!-- 逆向分析的表 -->
        <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
        <!-- domainObjectName属性指定生成出来的实体类的类名 -->
        <table tableName="course" domainObjectName="CourseDao"/>
        <table tableName="student" domainObjectName="StudentDao"/>
        <table tableName="curricula" domainObjectName="CurriculaDao"/>
    </context>
</generatorConfiguration>
类执行
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class Run {
    public static void main(String[] args) throws XMLParserException, IOException, InvalidConfigurationException, SQLException, InterruptedException {

        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("src/main/resources/config/generatorConfiguration.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
                callback, warnings);
        myBatisGenerator.generate(null);
    }
}

over

标签:语句,缓存,映射,基础,MyBatis,SQL,Mybatis,属性
来源: https://blog.csdn.net/weixin_48518621/article/details/120329832

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有