Mybatis

一、MyBatis简介

1、什么是mybatis?

mybatis 是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。
mybatis 通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中
sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。
采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。

  • MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。
  • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
  • MyBatis可以用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录.

总之就是两个字

2、MyBatis历史 ���

  • 原是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation 迁移到了Google Code,随着开发团队转投Google Code旗下, iBatis3.x正式更名为MyBatis ,代码于2013年11月迁移到Github(下载地址见后)。
  • iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。 iBatis提供的持久层框架包括SQL Maps和Data Access Objects(DAO)

3、为什么要使用MyBatis?❔

  • MyBatis是一个半自动化的持久化层框架。
  • JDBC
  1. 夹在Java代码块里,耦合度高导致硬编码内伤
  2. 维护不易且实际开发需求中sql是有变化,频繁修改的情况多见
  • Hibernate和JPA
  1. 长难复杂SQL,对于Hibernate而言处理也不容易
  2. 内部自动生产的SQL,不容易做特殊优化。
  3. 基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难。导致数据库性能下降。
  • sql和java编码分开,功能边界清晰,一个专注业务、一个专注数据。

4、去哪里找MyBatis?

GitHub - mybatis/mybatis-3: MyBatis SQL mapper framework for Java

二、MyBatis_(XML式编程)

1.创建Maven工程

2.在pom.xml引入相关坐标

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <mybatis.version>3.5.3</mybatis.version>
        <mysql.version>5.1.38</mysql.version>
        <lombok.version>1.18.6</lombok.version>
        <slf4j.version>1.6.4</slf4j.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- Mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>${mybatis.version}</version>
        </dependency>
        <!-- MySql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

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

        <!--java链接Oracle驱动-->
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies>

3.创建学生表和班级表

CREATE TABLE `class` (
  `cid` int(11) NOT NULL AUTO_INCREMENT,
  `cname` varchar(64) DEFAULT NULL,
  PRIMARY KEY (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

CREATE TABLE `student` (
  `sid` int(11) NOT NULL AUTO_INCREMENT,
  `s_name` varchar(32) DEFAULT NULL,
  `sgender` varchar(16) DEFAULT NULL,
  `age` int(16) DEFAULT NULL,
  `classid` int(16) DEFAULT NULL,
  PRIMARY KEY (`sid`),
  KEY `fk_s_c` (`classid`),
  CONSTRAINT `fk_s_c` FOREIGN KEY (`classid`) REFERENCES `class` (`cid`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8

4.定义与表对应的实体类

public class Class {
    private Integer cid;
    private String cname;

    public Integer getCid() {
        return cid;
    }

    public void setCid(Integer cid) {
        this.cid = cid;
    }

    public String getCname() {
        return cname;
    }

    public void setCname(String cname) {
        this.cname = cname;
    }

    @Override
    public String toString() {
        return "Class{" +
                "cid=" + cid +
                ", cname='" + cname + '\'' +
                '}';
    }
}
public class Student {
    private Integer sid; //列名sid
    private String sName; //列名s_name
    private String sGender; //列名sgender
    private Integer sage; //列名age
    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", sName='" + sName + '\'' +
                ", sGender='" + sGender + '\'' +
                ", sage=" + sage +
                '}';
    }

    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public String getsName() {
        return sName;
    }

    public void setsName(String sName) {
        this.sName = sName;
    }

    public String getsGender() {
        return sGender;
    }

    public void setsGender(String sGender) {
        this.sGender = sGender;
    }

    public Integer getSage() {
        return sage;
    }

    public void setSage(Integer sage) {
        this.sage = sage;
    }

    public Student() {}
    public Student(Integer sid, String sName, String sGender, Integer sage) {
        this.sid = sid;
        this.sName = sName;
        this.sGender = sGender;
        this.sage = sage;
    }
}

5.创建SQL映射文件

这是我们使用MyBatis时编写的最多的文件,学生类对应StudentMapper,CRUD的SQL语句就定义在这里

<?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.etc.mapper.StudentMapper">
    <!--id是这个select语句的唯一标识-->
    <!--resultType表示把查询到的结果封装给哪个实体类-->
    <!--parameterType表示参数的类型 如果参数只有一个可以省略该属性-->
    <!--#{sid}可以接受到执行该SQL时传递进来的参数-->
    <!--<select id="selectOne" resultType="com.etc.domain.Student" parameterType="integer">-->
<select id="selectOne" resultType="com.etc.domain.Student">        
        select * from student where sid = #{sid}
    </select>
</mapper>

6.创建MyBatis全局配置文件

MyBatis 的全局配置文件包含了影响 MyBatis 行为的设置(settings)和属性(properties)信息、如数据库连接池信息等。指导着MyBatis进行工作。我们可以先参照官方文件的配置示例。

<?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="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--驱动-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <!--URL-->
                <property name="url" value="jdbc:mysql://localhost:3306/tx?serverTimezone=GMT"/>
                <property name="username" value="root"/>
                <property name="password" value="123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--        
将写好的SQL映射文件注册到全局配置文件中
在类路径下 直接写文件名-->
        <mapper resource="StudentMapper"></mapper>
    </mappers>
</configuration>

7.创建打印日志文件

为了方便调试,查看SQL的执行过程,可以引入log4j.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration debug="true">
    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Encoding" value="UTF-8"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m  (%F:%L) \n"/>
        </layout>
    </appender>
    <logger name="java.sql">
        <level value="debug"/>
    </logger>
    <logger name="org.apache.ibatis">
        <level value="info"/>
    </logger>
    <root>
        <level value="debug"/>
        <appender-ref ref="STDOUT"/>
    </root>
</log4j:configuration>

8.编写测试代码

从 XML 中构建 SqlSessionFactory
  1.  resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
从 SqlSessionFactory 中获取 SqlSession
  1. session = sqlSessionFactory.openSession();
通过 SqlSession 实例来直接执行已映射的 SQL 语句
  • 执行com.etc.mapper.StudentMapper这个namespace下的id叫作selectOne的语句,参数是1
    Object o = session.selectOne("com.etc.mapper.StudentMapper.selectOne", 1);
    System.out.println(o);
    session.close();

打印结果

name和age属性没有值,是因为实体类中的姓名和年龄属性跟表中的字段名不匹配
  1. select id="selectOne" resultType="com.etc.domain.Student" parameterType="integer">
      <!-- 查询具体的字段并起别名 别名和类的属性名保持一致 后面会有其他处理方案-->   
     select sid,s_name sName,sgender,age sage from student where sid = #{sid}
    </select>
再次打印结果

9.总结

  • 根据全局配置文件创建一个sqlSessionFactory对象
  • 全局配置文件中有一些运行环境信息
  • 定义SQL映射文件 配置了SQL语句 以及SQL的封装规则
  • 将SQL映射注册在全局配置文件中
  • 使用sqlSessionFactory创建session对象
  • 使用session对象执行增删改查操作,使用映射文件中SQL的唯一标识告诉mybatis要 执行谁
  • 如果属性无法赋值,就修改SQL语句,查询每一个字段 并给无法映射的列起别名,别名要和类的属性名一致

三、MyBatis_(接口式编程)

1:创建Maven工程

2:在pom.xml引入相关坐标

3:创建学生表

4:定义与表对应的实体类

5:定义操作学生表的接口

public interface StudentMapper {
    /**
     * @param sid 要查询的学生id
     * @return 返回学生实体对象
     * selectOne是方法名 将来作为mapper文件中SQLid
     */
    public Student selectOne(Integer sid);
}

6:修改SQL映射文件

<?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映射文件要和接口动态绑定 所以namespace必须是接口的全限定名-->
<mapper namespace="com.etc.mappers.StudentMapper">
    <!--
        id          是接口中定义的方法名
        resultType  返回值结果的全限定名
    -->
    <select id="selectOne" resultType="com.etc.domain.Student">
        select sid,s_name sName,sgender,age sage from student where sid = #{sid}
    </select>
</mapper>

7:编写测试代码

//从 XML 中构建 SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//从 SqlSessionFactory 中获取 SqlSession
SqlSession session = sqlSessionFactory.openSession();
//获取接口的实现类对象(底层是基于JDK动态代理技术)
StudentMapper m = session.getMapper(StudentMapper.class);
//调用方法 传递参数 接收结果
Student o = m.selectOne(1);
System.out.println(o);
//释放资源
session.close();

8:总结

  • 根据全局配置文件创建一个sqlSessionFactory对象
  • 全局配置文件中有一些运行环境信息
  • 定义操作学生的接口,接口中定义抽象的CRUD方法
  • 定义SQL映射文件 配置SQL语句 ,namespace是接口的全限定名 id是接口中方法的名称
  • 将SQL映射注册在全局配置文件中
  • 使用sqlSessionFactory创建session对象
  • 使用session对象获取接口的代理实现类对象
  • 调用实现类对象的方法 传参数 接收结果

- 有明确的返回值 有更强的类型检查,把DAO层的规范和实现分离了出来有利于扩展和维护
- 原生:Dao ---> Dao的实现类
- mybatis:xxxMapper ---> xxxMapper.XML文件
- SqlSession代表和数据库的一次会话 用完必须关闭
- SqlSession和connection一样都是线程不安全的 不能声明为成员变量,每次使用时都应该获取新的对象
- Mapper接口没有实现类 但是mybatis会将xml文件和接口绑定,为这个接口生成一个代理对象
- 全局配置文件mybatis-config.xml和SQL映射文件xxxMapper.xml
- SqlSession可以直接调用方法的id进行数据库操作,但是我们一般还是推荐使用SqlSession获取到Dao接口的代理类,执行代理对象的方法,可以更安全的进行类型检查操作

四、MyBatis-全局配置文件

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下

  • configuration(配置) 根标签
  • properties(属性)
  • settings(设置)
  • typeAliases(类型别名)
  • typeHandlers(类型处理器)(暂)
  • objectFactory(对象工厂)(暂)
  • plugins(插件)
  • environments(环境配置)
  • databaseIdProvider(数据库厂商标识)(暂)
  • mappers(映射器)
  • 需要定义标签的时候必须按照这个顺序来编写 不能打乱位置
1.properties(属性)

<!--可以使用字标签定义数据源相关属性 在下面的DataSource中使用el表达式获取定义的值 -->
<properties>
    <property name="driver" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT"/>
    <property name="username" value="root"/>
    <property name="password" value="123"/>
</properties>
<environments default="mysql">
    <environment id="mysql">
        <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>

<!--也可以用来引入外部properties配置文件的内容 db.properties放在类路径下或者resources目录下-->
<properties resource="db.properties"/>
<environments default="mysql">
    <environment id="mysql">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${jdbc.driverClassName}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </dataSource>
    </environment>
</environments>
<!--后期会把数据源的信息交个spring管理-->

#db.properties配置文件的内容 包括MySQL的和Oracle的配置信息
#数据库驱动
jdbc.driverClassName=com.mysql.jdbc.Driver
#数据库
jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&allowMultiQueries=true
#jdbc.url=jdbc:mysql://localhost:3306/tx?serverTimezone=GMT
#用户名
jdbc.username=root
#密码
jdbc.password=123
#别名方式,扩展插件,监控统计用的filter:stat,日志用的filter:log4j,防御sql注入的filter:wall
jdbc.filters=stat
#最大连接数
jdbc.maxActive=300
#初始化连接数
jdbc.initialSize=2
#获取连接最大等待时间
jdbc.maxWait=60000
#最小连接数
jdbc.minIdle=1
#检测连接有效性的时间间隔
jdbc.timeBetweenEvictionRunsMillis=60000
#连接保持空闲而不被驱逐的最长时间
jdbc.minEvictableIdleTimeMillis=300000
#连接有效性,检测sql
jdbc.validationQuery=SELECT 'x'
#定时检测空闲连接有效性
jdbc.testWhileIdle=true
#检测获取的连接的有效性
jdbc.testOnBorrow=false
#检测要归还的连接的有效性
jdbc.testOnReturn=false
#是否缓存preparedStatement,即PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
jdbc.poolPreparedStatements=false
jdbc.maxPoolPreparedStatementPerConnectionSize=50

orcl.driver=oracle.jdbc.driver.OracleDriver
orcl.url=jdbc:oracle:thin:@localhost:1521:orcl
orcl.username=scott
orcl.password=tiger

2.settings(设置)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。加粗斜体配置是常用配置

设置名

描述

有效值

默认值

cacheEnabled

全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。

true | false

true

lazyLoadingEnabled

延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。

true | false

false

aggressiveLazyLoading

开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。

true | false

false (在 3.4.1 及之前的版本中默认为 true)

multipleResultSetsEnabled

是否允许单个语句返回多结果集(需要数据库驱动支持)。

true | false

true

useColumnLabel

使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。

true | false

true

useGeneratedKeys

允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。

true | false

False

autoMappingBehavior

指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。

NONE, PARTIAL, FULL

PARTIAL

autoMappingUnknownColumnBehavior

指定发现自动映射目标未知列(或未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN)FAILING: 映射失败 (抛出 SqlSessionException)

NONE, WARNING, FAILING

NONE

defaultExecutorType

配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。

SIMPLE REUSE BATCH

SIMPLE

defaultStatementTimeout

设置超时时间,它决定数据库驱动等待数据库响应的秒数。

任意正整数

未设置 (null)

defaultFetchSize

为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。

任意正整数

未设置 (null)

defaultResultSetType

指定语句默认的滚动策略。(新增于 3.5.2)

FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置)

未设置 (null)

safeRowBoundsEnabled

是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。

true | false

False

safeResultHandlerEnabled

是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。

true | false

True

mapUnderscoreToCamelCase

N 映射到经典 Java 属性名 aColumn。

true | false

False

localCacheScope

MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。

SESSION | STATEMENT

SESSION

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 | false

false

returnInstanceForEmptyRow

当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2)

true | false

false

logPrefix

指定 MyBatis 增加到日志名称的前缀。

任何字符串

未设置

logImpl

指定 MyBatis 所用日志的具体实现,未指定时将自动查找。

SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING

未设置

proxyFactory

指定 Mybatis 创建可延迟加载对象所用到的代理工具。

CGLIB | JAVASSIST

JAVASSIST (MyBatis 3.3 以上)

vfsImpl

指定 VFS 的实现

自定义 VFS 的实现的类全限定名,以逗号分隔。

未设置

useActualParamName

允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1)

true | false

true

configurationFactory

指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3)

一个类型别名或完全限定类名。

未设置

shrinkWhitespacesInSql

Removes extra whitespace characters from the SQL. Note that this also affects literal strings in SQL. (Since 3.5.5)

true | false

false

3.typeAliases(类型别名)

<!--别名处理器 类型别名可为 Java 类型设置一个缩写名字。 目的在于简化全限定名的书写-->
<typeAliases>
    <!--
            type 要起别名的类的全限定名
            alias 是自定义别名
    -->
    <!--
<typeAlias type="com.etc.domain.Student" alias="student"/>
-->
    <!--
            package 批量起别名
            name 给哪个包下的类起别名 默认就是类名小写

            如果不想使用默认别名 想自定义 可以在类的声明上使用@Alias注解
   如果同一包下有两个类名相同的类,也可以使用@Alias注解加以区分
    
@Alias("stu")
public class Student {

}
别名不区分大小写
     -->
    <package name="com.etc.domain"/>
</typeAliases>

下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。

4.typeHandlers(类型处理器)了解

MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。从 3.4.5 开始,MyBatis 默认支持 JSR-310(日期和时间 API) 。负责对java的类型和MySQL的类型进行转换.比如把java的String 转换映射成MySQL数据库的char/varchar类型,我们无需配置.mybatis会自动配置

5.plugins(插件)

MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心。

6.environments(环境配置)

<!--
environments    可以配置多种数据库环境
        default 指定使用默认环境 可以在开发/测试/mysql/Oracle之间切换
    environment 配置具体的数据库环境信息
    id  代表当前环境的唯一标识
        transactionManager  事务管理器
            type    事务管理器的类 当前环境使用何种事务管理方式
                    JDBC      JdbcTransactionFactory
                        直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
                    MANAGED   ManagedTransactionFactory
                        这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期, 默认情况下它会关闭连接
        dataSource  元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
            type   指定数据源类型,有三种内建的数据源类型
                    UNPOOLED
                        这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,
                        是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形
                    POOLED
                        这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间
                    JNDI
                        这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。

实际开发中我们使用Spring管理数据源,并进行事务控制的配置来覆盖上述配置
-->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driverClassName}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>

        <environment id="oracle">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driverClassName}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

7.databaseIdProvider(数据库厂商标识)(了解)

<!--
    MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。
    MyBatis 会加载带有匹配当前数据库 databaseId 属性和所有不带 databaseId 属性的语句。

    DB_VENDOR   VendorDatabaseIdProvider
        得到数据库厂商的标识 mybatis根据数据库厂商的标识来执行不同的SQL
databased是用于在多种数据库分同一条SQL对应的数据库。
可以这样认为,在Mybatis中SqL的id和 databased组合才是一条SQL的唯一标识
    -->
    <databaseIdProvider type="DB_VENDOR">
        <!--给不同的数据库起别名-->
        <property name="MySQL" value="mysql" />
        <property name="SQL Server" value="sqlserver"/>
        <property name="DB2" value="db2"/>
        <property name="Oracle" value="oracle" />
    </databaseIdProvider>

<!--通过指定databaseId告诉mybatis这条SQL由什么数据库执行-->
    <select id="selectOne" resultType="stu" databaseId="mysql">
       select sid,s_name sName,sgender,age sage from student where sid = #{sid}
    </select>

    <select id="selectOne" resultType="stu" databaseId="oracle">
       select * from student where sid = #{sid}
    </select>

测试代码如下:

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
Connection connection = session.getConnection();
String dbName = connection.getMetaData().getDatabaseProductName();
String dbVersion = connection.getMetaData().getDatabaseProductVersion();
System.out.println("数据库名称是:" + dbName + ";版本是:" + dbVersion);
StudentMapper m = session.getMapper(StudentMapper.class);
Student o = m.selectOne(1);
System.out.println(o);
session.close();

注意:在测试时要执行两条一模一样的SQL语句,这样 在切换数据库环境时才能感受到效果

当时

8.mappers(映射器)

<!--    将SQL映射文件注册到全局配置中-->
    <mappers>
    <!-- resource
    在类路径下 直接写文件名
    如果在多级目录下 要带上目录名称
    -->
    <!--        <mapper resource="mappers/StudentMapper.xml"></mapper>-->
    <!--        <mapper resource="StudentMapper.xml"></mapper>-->
    <!--
DEMO:
        也可以使用class属性 定义接口的全限定名
        接口名和配置文件名必须相同并且
        需要让接口和mapper文件路径相同

这是在由mapper.xml文件和mapper接口同时存在情况下使用
如果只有mapper接口 没有xml文件 SQL语句全部以注解形式定义在接口方法上,那么直接定义接口的全限定名即可
也可以使用全局注册  <package name="com.etc.mappers"/> 如果存在xml和接口绑定的情况 那么xml文件依然遵循DEMO规则
    -->
    <package name="com.etc.mappers"/>
    <mapper class="com.etc.mappers.StudentMapper"></mapper>
</mappers>

<mappers>
    <!-- 使用相对于类路径的资源引用 -->
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
    <!-- 使用完全限定资源定位符(URL) -->
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
    <!-- 使用映射器接口实现类的完全限定类名 -->
  <mapper class="org.mybatis.builder.AuthorMapper"/>
    <!-- 将包内的映射器接口实现全部注册为映射器 -->
  <package name="org.mybatis.builder"/>
</mappers>

9.objectFactory(对象工厂)了解

每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。

五、MyBatis-SQL映射文件

MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。

SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):

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

1.基本的CRUD

a.XML和接口绑定

接口

    public Student selectOne(Integer sid);

    public List<Student> selectAll();

    public void insertOne(Student s);

    public void updateOne(Student s);
    
//    1.mybatis允许我们在做增删改时直接定义返回值类型为Integer/Long/Boolean 基本或者包装类型都可以
//    public boolean deleteOne(Integer sid);
    public Integer deleteOne(Integer sid);

SQL映射文件

<!--
    <select id="selectOne" resultType="student" parameterType="integer">
        select sid,s_name,sgender,age sage from student
    </select>-->
    <select id="selectOne" resultType="student">
        select sid,s_name,sgender,age sage from student where sid = #{sid}
    </select>
    <select id="selectAll" resultType="student">
        select sid,s_name,sgender,age sage  from student
    </select>
<!--
    <insert id="insertOne">
        insert into student values(null,#{sName},#{sGender},#{sage})
    </insert>
-->
    <insert id="insertOne" parameterType="student">
        insert into student values(null,#{sName},#{sGender},#{sage})
    </insert>
<!--
    <update id="updateOne">
        update student set s_name=#{sName},sgender=#{sGender},age=#{sage} where sid=#{sid}
    </update>
-->
    <update id="updateOne" parameterType="student">
        update student set s_name=#{sName},sgender=#{sGender},age=#{sage} where sid=#{sid}
    </update>
<!--
    <delete id="deleteOne">
        delete from student where sid = #{sid}
    </delete>-->
    <delete id="deleteOne" parameterType="integer">
        delete from student where sid = #{sid}
    </delete>


b.注解Mapper接口

    @Select("select sid,s_name sName,sgender,age sage from student where sid = #{sid}")
    @ResultType(Student.class)//@ResultType 对应xml中ResultType的属性
    public Student selectOne(Integer sid);

    @Select("select sid,s_name,sgender,age sage  from student")
    @ResultType(Student.class)
    public List<Student> selectAll();

    @Insert("insert into student values(null,#{sName},#{sGender},#{sage})")
    public void insertOne(Student s);

    @Update("update student set s_name=#{sName},sgender=#{sGender},age=#{sage} where sid=#{sid}")
    public void updateOne(Student s);

    @Delete("delete from student where sid = #{sid}")
    public Integer deleteOne(Integer sid);

注意:

 1.DML方法允许定义布尔类型或者数值类型返回值

 2.openSession() 获取的session执行完毕后需要手动调用commit()方法提交事务

 3.openSession(true) 获取的session可以自动提交事务

2.Insert插入获取自增主键

a.MySQL

    <!--mysql 支持主键自增 statement中有getGeneratedKeys() 可以获取到插入记录后生成的id
        useGeneratedKeys= true 使用获取自增主键
        keyProperty 获取到主键值后把值封装给javabean的哪个属性
    -->
    <insert id="insertOne" parameterType="student" useGeneratedKeys="true" keyProperty="sid">
        insert into student values(null,#{sName},#{sGender},#{sage})
    </insert>

    <!--
select last_insert_id();获取最后一次插入记录的id 与表无关
order="AFTER" 表示在插入语句后执行
keyProperty 表示把查到的id封装给javabean的哪个属性
resultType 表示查到结果的类型
  -->
    <insert id="insertOne" parameterType="student">
        <selectKey order="AFTER" keyProperty="sid" resultType="integer">
            select last_insert_id()
        </selectKey>
        insert into student values(null,#{sName},#{sGender},#{sage})
    </insert>

b.Oracle

    <!--
        如果是Oracle需要自己去创建序列
        create sequence s_seq increment by 1 start with 1 nominvalue nomaxvalue nocycle nocache;
        删除序列
        drop sequence s_seq;
        查询
        select s_seq.currval from dual;
        select s_seq.nextval from dual;
        比方说我现在查出来值是10,那么我要把当前值改成8,那么可以这么改:
            alter sequence 序列名 increment by -2;
            如果我需要把当前值改成15,那么可以这么改:
            alter sequence 序列名 increment by 5;
            上述是通过修改当前序列增量长度间隔值,用于修改当前序列值,增加1或-1或n或-n,
   修改了之后,要执行一下nextval
            当修改好当前值之后,记得一定要把序列增量改回来,改为1:
            alter sequence 序列名 increment by 1;
    -->

    <insert id="insertOne" parameterType="student" databaseId="oracle">
        <selectKey order="BEFORE" keyProperty="sid" resultType="integer">
            <!--
先执行 select s_seq.nextval from dual 得到序列值
把序列值封装对象id属性
再执行插入学生记录的SQL语句 通过#{sid}把id从javabean中取出来
  -->
            select s_seq.nextval from dual
        </selectKey>
        insert into student values(#{sid},#{sName},#{sGender},#{sage})
    </insert>
     <!--
先执行插入语句 s_seq.nextval 获取到序列值
再执行 select s_seq.currval from dual 封装给javabean对象的id属性
如果插入多条 select s_seq.currval就是最后一个id的值 了解 不建议使用
-->
    <insert id="insertOne" parameterType="student" databaseId="oracle">
        <selectKey order="AFTER" keyProperty="sid" resultType="integer">
            select s_seq.currval from dual
        </selectKey>
        insert into student values(s_seq.nextval,#{sName},#{sGender},#{sage})
    </insert>

3.params参数处理

1.单个参数

 mybatis不会特殊处理 名字也可以随便起

2.多个参数
  • mybatis会把多个参数封装成一个map
  • select * from student where name=#{param1} and age=#{param2}
  • select(string name,int age)

  1. value:方法传递过来的参数
    select sid,s_name,sgender,age sage from student where s_name = #{param1} and sgender=#{param2}
    select sid,s_name,sgender,age sage from student where s_name = #{arg0} and sgender=#{arg1}

    这两种方式都不推荐 可以使用注解给参数命名
    多个参数还是封装到map集合中
    key:@Param指定的名称
    value:是方法传递的参数值
    public Student select(@Param("sname") String sname, @Param("sgender") String sgender);
    select sid,s_name,sgender,age sage from student where s_name = #{sname} and sgender=#{sgender}
  • 可以把参数封装到map中, #{key} 就是获取map中的value,调用方法时传递Map集合
  1. Student select(HashMap<String, Object> map);
    select sid,s_name,sgender,age sage from student where s_name = #{sname} and sgender=#{sgender}
  • 如果多个参数正好是业务逻辑中的数据模型 可以直接传入POJO
  • 属性名} 取出pojo对应的属性值
  • 如果参数是数组或集合
  • list 取值语法:list[index]
    select sid,s_name,sgender,age sage from student where sid=#{list[1]}
    如果方法的参数是数组 取值语法:#{array[index]}
    select sid,s_name,sgender,age sage from student where sid=#{array[0]}
  • 获取参数的语法总结
  1. sid,s_name,sgender,age sage from student where s_name = #{sname} and sgender=#{sgender}
    sid,s_name,sgender,age sage from student where s_name = #{sname} and sgender=#{stu.sGender}
    sid,s_name,sgender,age sage from student where s_name = #{param1} and sgender=#{param2.sGender}
    sid,s_name,sgender,age sage from student where s_name = #{0} and sgender=#{1.sGender} ×
    sid,s_name,sgender,age sage from student where sid=#{collection[1]} set集合不行 ×
3.#{} 和 ${} 区别

都可以获取map或者pojo中的属性值
区别在于 :
    #{} 是以预编译的形式 将参数设置到SQL语句中 可以防止SQL注入
    select sid,s_name,sgender,age sage from student where s_name = ? and sgender = ? 
    ${} 是把取出来的值拼接到SQL上 注意取出来的值是字符串要用单引号引起来 大部分使用#{} 
    select sid,s_name,sgender,age sage from student where s_name = '${sname}' and sgender='${sgender}'

jdbcType
对于插入null值 mysql默认是支持的 如果表列没有指定非空约束,那么空值可以插入到表中
对于Oracle来说 在执行SQL时接收到的参数是null,那么插入时会报错,因此可以配置jdbcType让Oracle也支持插入null值

局部配置
insert into STUDENT values (stu_id.nextval,#{sName},#{sage},#{sGender,jdbcType=NULL})
全局配置
        <settings>
            <setting name="mapUnderscoreToCamelCase" value="true"/>
            <setting name="jdbcTypeForNull" value="NULL"/>
        </settings>

4.select 返回结果

https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#select

1.返回一个或多个对象

public Student selectOne(Integer sid);
<!-- resultType是被封装的类型名称 -->
<select id="selectOne" resultType="student">
select sid,s_name,sgender,age sage from student where sid=#{sid}
</select>

public List<Student> selectAll();
<select id="selectAll" resultType="student">
    <!--mybatis会自动把多条记录封装到多个对象 并放入集合中-->
select sid,s_name,sgender,age sage from student
</select>

2.返回一个Map集合

public Map<String,Object> selectMap(Integer sid);
<!-- key是属性名/列名 value是表记录值 resultType是map类型 -->
<select id="selectMap" resultType="map">
<!--        select sid,s_name,sgender,age sage from student where sid=#{sid} -->
    select * from student where sid=#{sid}
</select>
    
    <!--
        结果是Map集合,key是指定列名,value是bean对象
@MapKey 指定返回的map集合中用哪一列做key 注意定义Map的key类型要和列的类型一致
    -->
@MapKey("sid")
public Map<Integer,Student> selectMapKey(Integer sid);
<select id="selectMapKey" resultType="student">
     select sid,s_name,sgender,age sage from student
</select>

3.自定义封装(单表)

    <!--
        resultMap 自定义封装规则
            id 唯一标识方便引用
            type 结果封装成何种类型

            id主键列 property属性名 column列名
            result普通列 property 属性名 column列名
    -->
    <resultMap id="mystudent" type="student">
        <id property="sid" column="sid"/>
        <result property="sName" column="s_name"/>
        <result property="sGender" column="sgender"/>
        <result property="sage" column="age"/>
    </resultMap>

<!--resultMap引用定好的resultMap的id -->
    <select id="selectOne" resultMap="mystudent">
        select * from student where sid=#{sid}
    </select>

4.自定义封装(多表一对一)

学生表和班级表是多对一的关系
一个学生 -> 对应一个班级
一个班级 -> 对应多个学生

a. 级联属性封装结果集

需求:查询学生,并将其班级信息一并查出
    <resultMap id="mystudent2" type="student">
        <id property="sid" column="sid"/>
        <result property="sName" column="s_name"/>
        <result property="sGender" column="sgender"/>
        <result property="sage" column="age"/>
        <!--
            property 级联属性的属性
            column 联合查询出来的字段
        -->
        <result property="c.cid" column="cid"/>
        <result property="c.cname" column="cname"/>
    </resultMap>

    <select id="getStudentAndClass" resultMap="mystudent2">
        select * from class c,student s where c.cid = s.classid and sid=#{sid} order by cid;
    </select>

b. association 定义关联的单个对象的封装规则

需求:查询学生,并将其班级信息一并查出   
<!-- 嵌套结果集-->
<resultMap id="mystudent3" type="student">
        <id property="sid" column="sid"/>
        <result property="sName" column="s_name"/>
        <result property="sGender" column="sgender"/>
        <result property="sage" column="age"/>
        <!--
            association 指定联合的javabean对象
            property 指定哪个属性是联合的对象
            javaType 指定这个属性对象的类型
        -->
        <association property="c" javaType="com.etc.domain.Class">
            <id column="cid" property="cid"></id>
            <result column="cname" property="cname"></result>
        </association>
    </resultMap>

    <select id="getStudentAndClass" resultMap="mystudent3">
        select * from class c,student s where c.cid = s.classid and sid=#{sid} order by cid;
    </select>

c. 使用association 分步查询

需求:查询学生,并将其班级信息一并查出
1 定义ClassMapper
public interface ClassMapper {
    public Class getClassByid(Integer cid);
}
2 定义ClassMapper配置文件
<mapper namespace="com.etc.mappers.ClassMapper">
    <select id="getClassByid" resultType="class">
        select * from class where cid=#{cid}
    </select>
</mapper>
3  <!--
        association分步查询
            1 按照学生的id查询学生信息
            2 根据学生的外键id查询对应的班级信息
            3 把班级信息设置到学生的班级属性中
            select 表示当前属性是调用select指定的方法查出的结果
            column 指定将哪列的值传递select这个方法
    -->
    <resultMap id="mystudent4" type="student">
        <id property="sid" column="sid"/>
        <result property="sName" column="s_name"/>
        <result property="sGender" column="sgender"/>
        <result property="sage" column="age"/>
        <association property="c" select="com.etc.mappers.ClassMapper.getClassByid" column="classid"></association>
    </resultMap>

    <select id="getStudentAndClassByStep" resultMap="mystudent4">
        select * from student where sid=#{sid}
    </select>

d. 使用association 分步查询并延迟加载

现在每次查询学生对象时都把对应的班级信息给查了出来.也可以在使用到班级信息的时候(比如输出)再去执行查询
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>

<resultMap id="mystudent4" type="student">
        <id property="sid" column="sid"/>
        <result property="sName" column="s_name"/>
        <result property="sGender" column="sgender"/>
        <result property="sage" column="age"/>
        <association property="c" select="com.etc.mappers.ClassMapper.getClassByid" 
        column="classid" fetchType="lazy"></association>
    </resultMap>

<!-- fetchType 可以对每个SQL语句进行精准控制 lazy 就是延迟加载 eager 就是立即加载-->
    <select id="getStudentAndClassByStep" resultMap="mystudent4">
        select * from student where sid=#{sid}
    </select>

5.自定义封装(多表一对多)

a.使用collection定义多个结果的封装规则

需求:查询班级,并将其学生信息一并查出

定义接口方法
public Class getClassAndStudentByid(Integer cid);
<resultMap id="myclass1" type="class">
        <id column="cid" property="cid"></id>
        <result column="cname" property="cname"></result>
        <!--
            collection 表示封装的结果是一个集合
                property 指定给哪个属性封装多个结果
                ofType 集合中的类型
        -->
        <collection property="students" ofType="com.etc.domain.Student">
            <id property="sid" column="sid"></id>
            <result property="sName" column="s_name"></result>
            <result property="sGender" column="sgender"></result>
            <result property="sage" column="age"></result>
        </collection>
    </resultMap>
    <select id="getClassAndStudentByid" resultMap="myclass1">
        select * from class c cross join student s on c.cid = s.classid and c.cid = #{cid}
    </select>

b.使用collection分步查询

需求:查询班级,并将其学生信息一并查出

根据班级id查出该班级的信息
public Class getClassAndStudentByidAndStep(Integer cid);

根据班级id查询出该班中所有的学生
public List<Student> selectAllByID(@Param("classid") Integer id);
<mapper namespace="com.etc.mappers.StudentMapper">
    <select id="selectAllByID" resultType="student">
        select sid,s_name sName,sgender,age sage from student where classid=#{classid}
    </select>
</mapper>    

<resultMap id="myclass2" type="class">
    <id property="cid" column="cid"></id>
    <result property="cname" column="cname"></result>
    <!--
select 根据那个SQL映射方法执行查询
column 把查询出来的哪一列作为参数传给select
fetchType 控制是否延迟加载
-->
    <collection property="students" select="com.etc.mappers.StudentMapper.selectAllByID" 
           column="cid" fetchType="eager"></collection>
</resultMap>

<select id="getClassAndStudentByidAndStep" resultMap="myclass2">
select * from class where cid=#{cid}
</select>

6.鉴别器discriminator

<resultMap id="mystudent4" type="student">
    <id property="sid" column="sid"/>
    <result property="sName" column="s_name"/>
    <result property="sGender" column="sgender"/>
    <result property="sage" column="age"/>
    <!--
        discriminator 鉴别器 类似于switch case的思想
            javaType 鉴别的列类型
            column 根据哪一列鉴别
                case 匹配
                    value 值
                    resultType 匹配之后封装的结果类型
    -->
    <discriminator javaType="string" column="sgender">
        <case value="男" resultType="student">
            <association property="c" select="com.etc.mappers.ClassMapper.getClassByid" column="classid" fetchType="eager"></association>
        </case>
        <case value="女" resultType="student">
            <id property="sid" column="sid"/>
            <result property="sName" column="sgender"/>
            <result property="sGender" column="sgender"/>
            <result property="sage" column="age"/>
        </case>
    </discriminator>
</resultMap>

<select id="getStudentAndClassByStep" resultMap="mystudent4">
    select * from student where sid=#{sid}
</select>

7.内置参数

mybatis有两个默认的内置参数: _parameter 和 _databaseId

<!--
_databaseId 判断不同的dbid,通过切换数据库运行环境,动态执行不同的SQL
_parameter 通常用来判断传进来的参数是否为null
-->
    <select id="findBydbId" resultMap="mystu1">
        <if test="_databaseId=='mysql'">
            select * from student where
            <if test="_parameter!=null">
                s_name like #{_parameter.sName}
            </if>
        </if>
        <if test="_databaseId=='oracle'">
            select * from student
        </if>
    </select>

8.模糊查询的几种写法

方式1:
    <select id="findBlurry" resultMap="mystu1">
        select * from student where s_name like #{sName}
    </select>
    
    StudentMapper m = session.getMapper(StudentMapper.class);
    Student st = new Student();
    st.setsName("%李%");
    List<Student> list = m.findBlurry(st);

方式2: 不安全 不推荐
    <select id="findBlurry" resultMap="mystu1">
        select * from student where s_name like '%${sName}%'
    </select>
    
    StudentMapper m = session.getMapper(StudentMapper.class);
    Student st = new Student();
    st.setsName("李");
    List<Student> list = m.findBlurry(st);

5.定义SQL片段(暂)

    <sql id="st">
        select * from student
    </sql>    
<!--SQL表示定义一个片段 id方便引用-->

<select id="findBydbId" resultMap="mystu1">
        <if test="_databaseId=='mysql'">
            <include refid="st"/> where
            <if test="_parameter!=null">
                s_name like #{_parameter.sName}
            </if>
        </if>
        <if test="_databaseId=='oracle'">
            <include refid="st"/> student
        </if>
    </select>

<!--在需要引入SQL片段的地方使用
include标签
  refid 使用要引入的SQL片段的id
-->
    <select id="findBlurry" resultMap="mystu1">
        <bind name="name" value="'%'+sName+'%'"/>
        <include refid="st"/> where s_name like #{name}
    </select>

六、MyBatis-动态SQL

1.if判断

<mapper namespace="com.etc.mappers.StudentMapper">
    <resultMap id="mystu1" type="student">
        <id property="sid" column="sid"></id>
        <result property="sName" column="s_name"></result>
        <result property="sGender" column="sgender"></result>
        <result property="sage" column="age"></result>
    </resultMap>
    <!--
1 select * from tb where 1=1 后面所有的条件前都加and
2 使用where标签 它可以自动去除条件前面的and
3 if可以判断传进来的条件是否不为空 长度是否不为零
4 如果把and 或者or 放在条件后面 where就失效了因此可以使用trim标签
select * from student where sid=#{sid} and s_name like #{sName} and sgender=#{sGender} and age=#{sage}
-->
    <select id="selectBycondition" resultMap="mystu1">
        select * from student
        <where>
            <if test="sid != null">
                and sid=#{sid}
            </if>
            <if test="sName != null and sName != ''">
                and s_name like #{sName}
            </if>
            <if test="sGender != null and sGender != ''">
                and sgender=#{sGender}
            </if>
            <if test="sage != null">
                and age=#{sage}
            </if>
        </where>
    </select>
</mapper>

2.trim(where set)

原始的where

<!--and 也可以放在条件的后面,这种写法在全条件下没有问题 但是如果是有中间的条件那么会多and-->
<select id="selectBycondition" resultMap="mystu1">
    select * from student
    <where>
     <if test="sid != null">
     sid=#{sid} and
        </if>
        <if test="sName != null and sName != ''">
            s_name like #{sName} and
        </if>
        <if test="sGender != null and sGender != ''">
            sgender=#{sGender} and
        </if>
        <if test="sage != null">
            age=#{sage}
        </if>
    </where>
</select>

使用trim标签拼接where和去除and

<!--使用trim标签-->
<select id="selectBycondition" resultMap="mystu1">
    select * from student
    <trim prefix="where" suffixOverrides="and">
        <!--
            prefix  给拼好的SQL语句前加前缀
            suffix  加后缀
            prefixOverrides 去除前缀
            suffixOverrides 去除后缀
        -->
        <if test="sid != null">
            sid=#{sid} and
        </if>
        <if test="sName != null and sName != ''">
            s_name like #{sName} and
        </if>
        <if test="sGender != null and sGender != ''">
            sgender=#{sGender} and
        </if>
        <if test="sage != null">
            age=#{sage}
        </if>
    </trim>
</select>

原始的set

<update id="update">
    <!--update student set s_name=#{sName},sgender=#{sGender},age=#{sage} where sid=#{sid} -->
    <!--
            如果少条件 最终SQL会多逗号
            因此需要使用set标签
        -->
    update student
    <set>
        <if test="sName != null and sName != ''">
            s_name= #{sName} ,
        </if>
        <if test="sGender != null and sGender != ''">
            sgender=#{sGender} ,
        </if>
        <if test="sage != null">
            age=#{sage}
        </if>
    </set>
    where sid=#{sid}
</update>

使用trim标签拼接set和去除逗号

<update id="update">
    update student
    <!--拼接set前缀 去除逗号后缀-->
    <trim prefix="set" suffixOverrides=",">
        <if test="sName != null and sName != ''">
            s_name= #{sName} ,
        </if>
        <if test="sGender != null and sGender != ''">
            sgender=#{sGender} ,
        </if>
        <if test="sage != null">
            age=#{sage}
        </if>
    </trim>
    where sid=#{sid}
</update>

3.choose (when otherwise)

分支选择:带了id 只拼id 带了name 只拼name 只会进入其中一个
<select id="selectByconditionAndChoose" resultMap="mystu1">
    select * from student
    <where>
        <choose>
            <when test="sage != null">
                age=#{sage}
            </when>
            <when test="sName != null and sName != ''">
                s_name like #{sName}
            </when>
            <when test="sGender != null">
                sgender=#{sGender}
            </when>
            <when test="sid != null">
                sid=#{sid}
            </when>
            <otherwise>
            </otherwise>
        </choose>
    </where>
</select>

4.foreach

a:遍历列表

<select id="selectByids" resultMap="mystu1">
    <!--select * from student where sid in(1,2,3);-->
    select * from student where sid in
    <!--<foreach collection="list" item="id" separator="," open="(" close=")">-->
    <foreach collection="list" item="id" separator="," open=" where sid in(" close=")">
        #{id}
    </foreach>
    <!--
            collection 指定要遍历的集合 list类型会特殊处理放在map中,key就叫list 此处无需#{list}
            item 将遍历出来的元素赋值给指定变量
            separator 每个元素之间的分隔符 如果是最后一个则不加
            open 遍历出所有结果拼接一个开始的字符串
            close 遍历出所有结果后拼接结束的字符串
            index 遍历list时index就是当前元素索引
         -->
</select>

b:批量插入_MySQL

<insert id="insert">
    <!--
  insert into student values(null,'aaa','男',12),(null,'bbb','女',22);
  这是执行一次SQL
 -->
    insert into student values
    <foreach collection="list" item="stu" separator=",">
        (null,#{stu.sName},#{stu.sGender},#{stu.sage},null)
    </foreach>
</insert>
<!--
        这是执行多次SQL 需要在url上开启允许多次query
        jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&allowMultiQueries=true
  可以用于批量删除 批量修改
 -->
<insert id="insert">
    <foreach collection="list" item="stu" separator=";">
        insert into student values(null,#{stu.sName},#{stu.sGender},#{stu.sage},null)
    </foreach>
</insert>

c:批量插入_Oracle

insert into STUDENT values (s_seq.nextval,'菅直人','男',55),(s_seq.nextval,'菅直人','男',55);这种语法Oracle是不支持的 ×

begin
    insert into STUDENT values (s_seq.nextval,'菅直人','男',55);
    insert into STUDENT values (s_seq.nextval,'小泉','男',44); √
end;

<!--end;后面分号不能少
此处不需要使用separator属性
-->
<insert id="insert" databaseId="oracle">
    <foreach collection="list" item="stu" open="begin" close="end;">
        insert into student values(s_seq.nextval,#{stu.sName},#{stu.sGender},#{stu.sage});
    </foreach>
</insert>

七、MyBatis-缓存机制(暂)

MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。

MyBatis系统中默认定义了两级缓存。一级缓存和二级缓存。

1、默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。

2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存。

3、为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

A:一级缓存:本地缓存

 与数据库同一次会话期间查询到的数据会放在本地缓存中 以后使用到相同的数据 直接从缓存中获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值