Mybatis笔记

在这里插入图片描述

Mybatis笔记-------基于Maven、Idea编辑器

1.Mybatis简介:

1.Mybatis介绍

1.1MyBatis 是支持定制化 SQL、存储过程以及高级 映射的优秀的持久层框架。

1.2 MyBatis 避免了几乎所有的 JDBC 代码和手动设 置参数以及获取结果集。

1.3 MyBatis可以使用简单的XML或注解用于配置和原 始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录

2.Mybatis历史

2.1 原是Apache的一个开源项目iBatis, 2010年6月这 个项目由Apache Software Foundation 迁移到了 Google Code,随着开发团队转投Google Code 旗下, iBatis3.x正式更名为MyBatis ,代码于 2013年11月迁移到Github(下载地址见后)

2.2 iBatis一词来源于“internet”和“abatis”的组合,是 一个基于Java的持久层框架。 iBatis提供的持久 层框架包括SQL Maps和Data Access Objects (DAO)

3.为什么要使用Mybatis

3.1 MyBatis是一个半自动化的持久化层框架。

3.2 JDBC – SQL夹在Java代码块里,耦合度高导致硬编码内伤 – 维护不易且实际开发需求中sql是有变化,频繁修改的情况多见 • Hibernate和JPA – 长难复杂SQL,对于Hibernate而言处理也不容易 – 内部自动生产的SQL,不容易做特殊优化。

3.3 –基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难。 导致数据库性能下降。 •对开发人员而言,核心sql还是需要自己优化 • sql和java编码分开,功能边界清晰,一个专注业务、 一个专注数据。

2.Mybatis下载地址:

1.下载地址:https://github.com/mybatis/mybatis-3/
2.如何在Github上面下载Mybatis:

2.1:

在这里插入图片描述

2.2:

在这里插入图片描述

3.Maven项目添加Mybatis依赖:

<!--单元测试依赖-->
    <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>3.4.6</version>
    </dependency>
<!--MySql8版本依赖-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.20</version>
    </dependency>

在这里插入图片描述

4.Mybatis版HelloWord:

1.创建一张测试表
CREATE TABLE admin(
	admin_Id INT PRIMARY KEY auto_increment,
	admin_Name VARCHAR(20) NOT NULL,
	admin_Password VARCHAR(10) NOT NULL,
	admin_Email VARCHAR(20) NOT NULL
);
2.创建对应的javaBean
public class User {
    private Integer adminId;
    private String adminName;
    private String adminPassword;
    private String adminEmail;
}
无参和有参构造器
GeterSeter
toString()方法;
3.创建mybatis配置文件,

在这里插入图片描述

4.Mybatis配置文件内容:

在这里插入图片描述

注意事项:Idea创建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">
也有可能:http://mybatis.org/dtd/mybatis-3-config.dtd这段会爆红快捷键让它自动下载DTD约束文件
5.创建SQL映射文件:

在这里插入图片描述

注意事项:Idea创建映射文件可能刚创建什么内容也没有添加表头:

<?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">
也有可能:http://mybatis.org/dtd/mybatis-3-mapper.dtd这段会爆红快捷键让它自动下载DTD约束文件
6.映射文件内容:

在这里插入图片描述

7.添加Log4j.Xml配置文件

<!--注入Log4j依赖-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

8.Log4j.Xml配置文件内容

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM
        "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
<log4j:configuration debug="false">
    <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>
9.测试

9.1 根据全局配置文件,利用 SqlSessionFactoryBuilder创建SqlSessionFactory

private void getSqlSessionFactory(){
      String resource = "Mybatis-Config.xml";
      InputStream resourceAsStream = Resources.getResourceAsStream(resource);
      SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
}

9.2 使用SqlSessionFactory获取sqlSession对象。一个 SqlSession对象代表和数据库的一次会话。

SqlSession openSession = factory.openSession();

9.3 使用SqlSession根据方法id进行操作

    SqlSession openSession = factory.openSession();
    Object o = openSession.selectOne("com.Java.dao.UserDaoMapper.getByUserId", 1);
    System.out.println(o);
    openSession.close();
注意:保持良好的代码规范:应该使用Try-Catch捕获

9.4:最终代码

 @Test
 public void getSqlSessionFactoryTest() throws IOException {
    String resource = "Mybatis-Config.xml";
    InputStream resourceAsStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession openSession = factory.openSession();
    Object o = openSession.selectOne("com.Java.dao.UserDaoMapper.getByUserId", 1);
    System.out.println(o);
    openSession.close();
}
注意:保持良好的代码规范:应该使用Try-Catch捕获
DEBUG 05-02 14:00:06,371 ==>  Preparing: select admin_Id as adminId, admin_Name as adminName, admin_Password as adminPassword, admin_Email as adminEmail from `admin` where admin_Id=?;(BaseJdbcLogger.java:159) 
DEBUG 05-02 14:00:06,414 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:159) 
DEBUG 05-02 14:00:06,442 <== Total: 1  (BaseJdbcLogger.java:159) 
User{adminId=1,adminName='user',adminPassword='123456789',adminEmail='user@qq.com'}

5.HelloWord接口式编程:

注意事项:

1、接口式编程

  • 原生: Dao ====> DaoImpl

  • mybatis: Mapper ====> xxMapper.xml

  • 2、SqlSession代表和数据库的一次会话;用完必须关闭;

  • 3、SqlSession和connection一样她都是非线程安全。每次使用都应该去获取新的对象。

  • 4、mapper接口没有实现类,但是mybatis会为这个接口生成一个代理对象。

  • (将接口和xml进行绑定)

  • UserDaoMapper userDaoMapper = sqlSession.getMapper(UserDaoMapper.class);

  • 5、两个重要的配置文件:

  • mybatis的全局配置文件:包含数据库连接池信息,事务管理器信息等…系统运行环境信息

  • sql映射文件:保存了每一个sql语句的映射信息:

  • 将sql抽取出来。

    Mybatis版本 :Mapper ------>xxxMapper.xml实例:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.使用SqlSession获取映射器进行操作
 @Test
 public void getSqlSessionFactoryTest() throws IOException {
    String resource = "Mybatis-Config.xml";
    InputStream resourceAsStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
   	SqlSession openSession = factory.openSession();
    UserDaoMapper mapper = openSession.getMapper(UserDaoMapper.class);
    User byUserId = mapper.getByUserId(2);
    System.out.println(byUserId);
    openSession.close();
}
DEBUG 05-02 14:17:40,347 ==> Parameters: 2(Integer)  (BaseJdbcLogger.java:159) 
DEBUG 05-02 14:17:40,387 <== Total: 1  (BaseJdbcLogger.java:159) 
User{adminId=2,adminName='abc',adminPassword='admin',adminEmail='admin@qq.com'}
4 SqlSession

4.1 SqlSession 的实例不是线程安全的,因此是不能 被共享的。

4.2 SqlSession每次使用完成后需要正确关闭,这个关闭操作是必须的

4.3 SqlSession可以直接调用方法的id进行数据库操作,但是我们一般还是推荐使用SqlSession获取到Dao接口的代理类,执行代理对象的方法,可 以更安全的进行类型检查操作

5 总结注意事项很多Mybatis新手在写映射文件的时候数据库的字段和JavaBean实体不匹配会这样:

DEBUG 05-02 16:22:35,634 ==>  Preparing: select * from `admin` where admin_Id=?; (BaseJdbcLogger.java:159) 
DEBUG 05-02 16:22:35,700 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:159) 
DEBUG 05-02 16:22:35,740 <==      Total: 1  (BaseJdbcLogger.java:159) 
null <!-- 查询有结果但返回打印null -->

原因:

Mysql数据库字段

在这里插入图片描述

JavaBean对应字段:

在这里插入图片描述

总结:在映射文件当中起别名:

 select admin_Id as adminId,
        admin_Name as adminName,
        admin_Password as adminPassword,
        admin_Email as adminEmail from `admin`
        where admin_Id=#{adminId};
  或者:在全局配置文件当中加上开启驼峰命名法规则:
  	<settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

6.MyBatis全局配置文件

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

在这里插入图片描述

在这里插入图片描述

<?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>
    <properties resource="jdbc.properties"/>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driverClassName}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/UserDaoMapper.xml"/>
    </mappers>
</configuration>
 注意事项:value=“要与jdbc.properties名字相同”   

在这里插入图片描述

不相同报以下异常:

org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: java.sql.SQLException: Error setting driver on UnpooledDataSource. Cause: java.lang.ClassNotFoundException: Cannot find class: ${driverClassNam}
### The error may exist in mapper/UserDaoMapper.xml
### The error may involve com.Java.dao.UserDaoMapper.getByUserId
### The error occurred while executing a query
### Cause: java.sql.SQLException: Error setting driver on UnpooledDataSource. Cause: java.lang.ClassNotFoundException: Cannot find class: ${driverClassNam}

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

在 properties 元素体内指定的属性首先被读取。

然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根 据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。

最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。

6.2 Settings设置:

1.这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

在这里插入图片描述

<!-- 
    是否开启自动驼峰命名规则(camel case) 映射 value=truefalse“ 默认值:false
    true:开启
    false:关闭    
 -->
<settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
6.3 typeAliases别名处理器:

1.类型别名是为 Java 类型设置一个短的名字,可以方便我们引用某个类。

<?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>
    <properties resource="jdbc.properties"/>

    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    
    <typeAliases>
        <typeAlias type="com.Java.pojo.User" alias="user"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driverClassName}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    
    <mappers>
        <mapper resource="mapper/UserDaoMapper.xml"/>
    </mappers>
</configuration>
 注意事项:<configuration>里面配置一定遵循Mybatis顺序</configuration>   

2.类很多的情况下,可以批量设置别名这个包下的每一个类 创建一个默认的别名,就是简单类名小写。

<!-- com.Java.pojo:这样写就包含pojo这个包下的所有实体类默认小写 例如:User user -->
<typeAliases>
     <package name="com.Java.pojo"/>
</typeAliases>

3.也可以使用@Alias注解为其指定一个别名

@Alias("user")
public class User {
    private Integer adminId;
    private String adminName;
    private String adminPassword;
    private String adminEmail;
}   

4.值得注意的是,MyBatis已经为许多常见的 Java 类型内建 了相应的类型别名。它们都是大小写不敏感的,我们在起 别名的时候千万不要占用已有的别名。

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

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator
6.4 typeHandlers类型处理器:

1.MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。

类型处理器Java 类型JDBC 类型
BooleanTypeHandlerjava.lang.Boolean, boolean数据库兼容的 BOOLEAN
ByteTypeHandlerjava.lang.Byte, byte数据库兼容的 NUMERICBYTE
ShortTypeHandlerjava.lang.Short, short数据库兼容的 NUMERICSMALLINT
IntegerTypeHandlerjava.lang.Integer, int数据库兼容的 NUMERICINTEGER
LongTypeHandlerjava.lang.Long, long数据库兼容的 NUMERICBIGINT
FloatTypeHandlerjava.lang.Float, float数据库兼容的 NUMERICFLOAT
DoubleTypeHandlerjava.lang.Double, double数据库兼容的 NUMERICDOUBLE
BigDecimalTypeHandlerjava.math.BigDecimal数据库兼容的 NUMERICDECIMAL
StringTypeHandlerjava.lang.StringCHAR, VARCHAR
ClobReaderTypeHandlerjava.io.Reader-
ClobTypeHandlerjava.lang.StringCLOB, LONGVARCHAR
NStringTypeHandlerjava.lang.StringNVARCHAR, NCHAR
NClobTypeHandlerjava.lang.StringNCLOB
BlobInputStreamTypeHandlerjava.io.InputStream-
ByteArrayTypeHandlerbyte[]数据库兼容的字节流类型
BlobTypeHandlerbyte[]BLOB, LONGVARBINARY
DateTypeHandlerjava.util.DateTIMESTAMP
DateOnlyTypeHandlerjava.util.DateDATE
TimeOnlyTypeHandlerjava.util.DateTIME
SqlTimestampTypeHandlerjava.sql.TimestampTIMESTAMP
SqlDateTypeHandlerjava.sql.DateDATE
SqlTimeTypeHandlerjava.sql.TimeTIME
ObjectTypeHandlerAnyOTHER 或未指定类型
EnumTypeHandlerEnumeration TypeVARCHAR 或任何兼容的字符串类型,用来存储枚举的名称(而不是索引序数值)
EnumOrdinalTypeHandlerEnumeration Type任何兼容的 NUMERICDOUBLE 类型,用来存储枚举的序数值(而不是名称)。
SqlxmlTypeHandlerjava.lang.StringSQLXML
InstantTypeHandlerjava.time.InstantTIMESTAMP
LocalDateTimeTypeHandlerjava.time.LocalDateTimeTIMESTAMP
LocalDateTypeHandlerjava.time.LocalDateDATE
LocalTimeTypeHandlerjava.time.LocalTimeTIME
OffsetDateTimeTypeHandlerjava.time.OffsetDateTimeTIMESTAMP
OffsetTimeTypeHandlerjava.time.OffsetTimeTIME
ZonedDateTimeTypeHandlerjava.time.ZonedDateTimeTIMESTAMP
YearTypeHandlerjava.time.YearINTEGER
MonthTypeHandlerjava.time.MonthINTEGER
YearMonthTypeHandlerjava.time.YearMonthVARCHARLONGVARCHAR
JapaneseDateTypeHandlerjava.time.chrono.JapaneseDateDATE
6.5 日期类型的处理

1.日期和时间的处理,JDK1.8以前一直是个头疼的 问题。

2.我们通常使用JSR310规范领导者Stephen Colebourne创建的Joda-Time来操作。1.8已经实 现全部的JSR310规范了

3.日期时间处理上,我们可以使用MyBatis基于 JSR310(Date and Time API)编写的各种日期 时间类型处理器

4.MyBatis3.4 以前的版本需要我们手动注册这些处 理器,以后的版本都是自动注册

在这里插入图片描述

5.自定义类型处理器《也可以借鉴后半部分14.3、自定义TypeHandler处理枚举

我们可以重写类型处理器或创建自己的类型处理 器来处理不支持的或非标准的类型。

步骤:

1、实现org.apache.ibatis.type.TypeHandler接口或 者继承org.apache.ibatis.type.BaseTypeHandler

2、指定其映射某个JDBC类型(可选操作)

3、在mybatis全局配置文件中注册

public class HandlerTest extends BaseTypeHandler {
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Object o, JdbcType jdbcType) throws SQLException {
        
    }

    @Override
    public Object getNullableResult(ResultSet resultSet, String s) throws SQLException {
        return null;
    }

    @Override
    public Object getNullableResult(ResultSet resultSet, int i) throws SQLException {
        return null;
    }

    @Override
    public Object getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        return null;
    }
}
<!-- 在全局配置文件中配置Handler -->
<typeHandlers>
     <typeHandler handler="com.Java.handler.HandlerTest"/>
</typeHandlers>
6.6 plugins插件<泼辣跟思>

1. 插件是MyBatis提供的一个非常强大的机制,我们 可以通过插件来修改MyBatis的一些核心行为。插 件通过动态代理机制,可以介入四大对象的任何 一个方法的执行。见下半部分

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

• ParameterHandler (getParameterObject, setParameters)

• ResultSetHandler (handleResultSets, handleOutputParameters)

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

6.7 environments环境

1.MyBatis可以配置多种环境,比如开发、测试和生 产环境需要有不同的配置。

• 每种环境使用一个environment标签进行配置并指 定唯一标识符

• 可以通过environments标签中的default属性指定 一个环境的标识符来快速的切换环境在这里插入图片描述

<!--environments翻译:环境们  -->
<environments default="dev_mysql">
        <environment id="dev_mysql">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driverClassName}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
</environments>

2.在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):

  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
  • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如:
<transactionManager type="MANAGED">
  <property name="closeConnection" value="false"/>
</transactionManager>

3.dataSource

3.1、type: UNPOOLED | POOLED | JNDI | 自定义 –

3.2、UNPOOLED:不使用连接池, UnpooledDataSourceFactory – POOLED:使用连接池,

3.3、PooledDataSourceFactory – JNDI: 在EJB 或应用服务器这类容器中查找指定的数据源

3.4、自定义:实现DataSourceFactory接口,定义数据源的 获取方式。

• 实际开发中我们使用Spring管理数据源,并进行 事务控制的配置来覆盖上述配

4、databaseIdProvider环境

4.1、MyBatis 可以根据不同的数据库厂商执行不同的语句。

<databaseIdProvider type="DB_VENDOR">
    <!-- 为不同的数据库厂商起别名 -->
    <property name="MySQL" value="mysql"/>
    <property name="Oracle" value="oracle"/>
    <property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>

4.2、Type:DB_VENDOR – 使用MyBatis提供的VendorDatabaseIdProvider解析数据库 厂商标识。

也可以实现DatabaseIdProvider接口来自定义。

• Property-name:数据库厂商标识

• Property-value:为标识起一个别名,方便SQL语句使用 databaseId属性引用

<select id="getByUserId" resultType="com.Java.pojo.User"
         parameterType="Integer"
         databaseId="mysql">
            select admin_Id as adminId,
            admin_Name as adminName,
            admin_Password as adminPassword,
            admin_Email as adminEmail from `admin`
            where admin_Id=#{adminId};
</select>

4.3、DB_VENDOR – 会通过 DatabaseMetaData#getDatabaseProductName() 返回的字符 串进行设置。由于通常情况下这个字符串都非常长而且相同产品的不 同版本会返回不同的值,所以最好通过设置属性别名来使其变短

4.4、Batis匹配规则如下:

– 1、如果没有配置databaseIdProvider标签,那么databaseId=null

– 2、如果配置了databaseIdProvider标签,使用标签配置的name去匹 配数据库信息,匹配上设置databaseId=配置指定的值,否则依旧为 null

– 3、如果databaseId不为null,他只会找到配置databaseId的sql语句

– 4、MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。

6.8 mapper映射

1.mapper逐个注册SQL映射文件

<mappers>
     <mapper url="C:\Users\25346\Desktop\Mybatis_Review\src\main\resources\mapper\UserDaoMapper.xml"/>
     <mapper resource="mapper/UserDaoMapper.xml"/>
     <mapper class="com.Java.dao.UserDaoMapperAnnotation"/>
</mappers>

2.mapper:注册一个sql映射
注册配置文件
resource:引用类路径下的sql映射文件
mybatis/mapper/EmployeeMapper.xml
url:引用网路路径或者磁盘路径下的sql映射文件
file:///var/mappers/AuthorMapper.xml

​ 注册接口
​ class:引用(注册)接口,
​ 1、有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
​ 2、没有sql映射文件,所有的sql都是利用注解写在接口上;
​ 推荐:
​ 比较重要的,复杂的Dao接口我们来写sql映射文件
​ 不重要,简单的Dao接口为了开发快速可以使用注解;

3.注解版实现

public interface UserDaoMapperAnnotation {
    @Select("select * from admin where admin_Id=#{adminId}")
    User getUserById(Integer adminId);
}
@Test
public void getSqlSessionFactoryTest() throws IOException {
        String resource = "Mybatis-Config.xml";
        InputStream resourceAsStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession openSession = factory.openSession();
        UserDaoMapperAnnotation mapper = openSession.getMapper(UserDaoMapperAnnotation.class);
        User userById = mapper.getUserById(1);
        System.out.println(userById);
        openSession.close();

}
注意:保持良好的代码规范:应该使用Try-Catch捕获
DEBUG 05-02 16:00:55,851 ==> Preparing: select * from admin where admin_Id=?   (BaseJdbcLogger.java:159) 
DEBUG 05-02 16:00:55,888 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:159) 
DEBUG 05-02 16:00:55,919 <== Total: 1  (BaseJdbcLogger.java:159) 
User{adminId=1,adminName='user',adminPassword='123456789',adminEmail='user@qq.com'}

4.使用批量注册:

这种方式要求SQL映射文件名必须和接口名相同并且在同一目录下

<package name="com.Java.dao"/>

注意事项:

在这里插入图片描述

不在同一个目录下报异常:

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.Java.dao.UserDaoMapper.getByUserId

7.Mybatis映射文件

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

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

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

下一部分将从语句本身开始来描述每个元素的细节。


7.2、insert、update、delete元素属性

在这里插入图片描述

7.3、主键生成方式:insert – 映射插入语句。

1、若数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),则可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上。

代码如下:

UserDaoMapper.class

void saveUser(User user);

UserDaoMapper.xml

<!--void saveUser(User user);-->
<insert id="saveUser"
            databaseId="mysql"
            useGeneratedKeys="true"
            keyProperty="adminId"
            parameterType="com.Java.pojo.User">
        insert into `admin`(admin_Id,admin_Name,admin_Password,admin_Email)
        values (#{adminId},#{adminName},#{adminPassword},#{adminEmail});
</insert>

TestMybatis.class(测试类代码)

 @Test
public void getSqlSessionFactoryTest() throws IOException {
        String resource = "Mybatis-Config.xml";
        InputStream resourceAsStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory factory = new 			             		SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession openSession = factory.openSession();
        UserDaoMapper mapper = openSession.getMapper(UserDaoMapper.class);
        User user = new User(null, "admin", "admin", "admin@qq.com");
        mapper.saveUser(user);
        System.out.println(user.getAdminId());
        openSession.commit();
        openSession.close();
}
注意:保持良好的代码规范:应该使用Try-Catch捕获

打印结果:

DEBUG 05-04 10:04:28,595 ==>  Preparing: insert into `admin`(admin_Id,admin_Name,admin_Password,admin_Email) values (?,?,?,?);   (BaseJdbcLogger.java:159) 
DEBUG 05-04 10:04:28,638 ==> Parameters: null, admin(String), admin(String), admin@qq.com(String)  (BaseJdbcLogger.java:159) 
DEBUG 05-04 10:04:28,640 <== Updates: 1  (BaseJdbcLogger.java:159) 
1

注意事项:

1、我们需要手动提交数据

  • 	sqlSessionFactory.openSession();===》手动提交
    
  • 	sqlSessionFactory.openSession(true);===》自动提交
    
  • 	如果没有提交情况如下,显示这样
    

在这里插入图片描述

在这里插入图片描述

总结:或者设置会话自动提交,或者openSession.commit();手动提交

**7.4、**selectKey属性在这里插入图片描述

代码如下:《Mybatis官网文档实例》

对于不支持自动生成主键列的数据库和可能不支持自动生成主键的 JDBC 驱动,MyBatis 有另外一种方法来生成主键。
这里有一个简单(也很傻)的示例,它可以生成一个随机 ID(不建议实际使用,这里只是为了展示 MyBatis 处理问题的灵活性和宽容度):
<insert id="insertAuthor">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
  </selectKey>
  insert into Author
    (id, username, password, email,bio, favourite_section)
	values
    (#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR})
</insert>
7.5、update – 映射更新语句。

代码如下:

UserDaoMapper.class

void upDateUser(User user);

UserDaoMapper.xml

<!--void upDateUser(User user);-->
<update id="upDateUser" 
            databaseId="mysql" 
            parameterType="com.Java.pojo.User">
        update `admin`
		set admin_Name=#{adminName},admin_Password=#				     				{adminPassword},admin_Email=#{adminEmail}
		where admin_Id=#{adminId};
</update>

TestMybatis.class(测试代码)

 @Test
public void getSqlSessionFactoryTest() throws IOException {
        String resource = "Mybatis-Config.xml";
        InputStream resourceAsStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession openSession = factory.openSession();
        UserDaoMapper mapper = openSession.getMapper(UserDaoMapper.class);
        mapper.upDateUser(new User(1, "root", "root", "root@qq.com"));
        openSession.commit();
        openSession.close();
}
7.6、delete – 映射删除语句。

代码如下:

UserDaoMapper.class

void deleteUser(Integer adminId);U

UserDaoMapper.xml

<!--void deleteUser(Integer adminId);-->
    <delete id="deleteUser" databaseId="mysql" 								   				parameterType="com.Java.pojo.User" >
        	delete from `admin` where admin_Id=#{adminId};
    </delete>

TestMybatis.xml

@Test
public void getSqlSessionFactoryTest() throws IOException {
    String resource = "Mybatis-Config.xml";
    InputStream resourceAsStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession openSession = factory.openSession();
    UserDaoMapper mapper = openSession.getMapper(UserDaoMapper.class);
    mapper.deleteUser(1);
    openSession.commit();
    openSession.close();
}
7.7、参数(Parameters)传递

• 单个参数 – 可以接受基本类型,对象类型,集合类型的值。

这种情况 MyBatis可直接使用这个参数,不需要经过任何处理。

• 多个参数 – 任意多个参数,都会被MyBatis重新包装成一个Map传入。

Map的key是param1,param2,0,1…,值就是参数的值。

• 命名参数 – 为参数使用@Param起一个名字,

MyBatis就会将这些参数封 装进map中,key就是我们自己指定的名字

• POJO – 当这些参数属于我们业务POJO时,我们直接传递POJO

• Map – 我们也可以封装多个参数为map,直接传递

代码如下:返回值为Map集合

7.8、参数处理

参数也可以指定一个特殊的数据类型:

在这里插入图片描述

– javaType 通常可以从参数对象中来去确定 – 如果 null 被当作值来传递,对于所有可能为空的列, jdbcType 需要被设置

– 对于数值类型,还可以设置小数点后保留的位数:

– mode 属性允许指定 IN,OUT 或 INOUT 参数。如果参数 为 OUT 或 INOUT,参数对象属性的真实值将会被改变, 就像在获取输出参数时所期望的那样。

参数位置支持的属性

– javaType、jdbcType、mode、numericScale、 resultMap、typeHandler、jdbcTypeName、expression

实际上通常被设置的是:

可能为空的列名指定 jdbcType

• #{key}:获取参数的值,预编译到SQL中。安全。

• ${key}:获取参数的值,拼接到SQL中。有SQL注入问题。ORDER BY ${name}

UserDaoMapper.class

//多条记录封装一个map:Map<Integer,User>:键是这条记录的主键,值是记录封装后的javaBean
//@MapKey:告诉mybatis封装这个map的时候使用哪个属性作为map的key
@MapKey("adminName")
Map<Integer, User> getUserByLastNameLikeReturnMap(String adminName);
//返回一条记录的map;key就是列名,值就是对应的值
Map<String, Object> getUserByIdReturnMap(Integer adminId);
//返回多条记录List集合
List<User> getUsersByLastNameLike(String adminName);
//传入Map集合参数查询返回对象
User getUserByMap(Map<String, Object> map);
//传入多个参数返回对象
User getUserByIdAndLastName(@Param("adminId") Integer adminId, @Param("adminName") String adminName);

UserDaoMapper.xml

<!--Map<Integer, User> getUserByLastNameLikeReturnMap(String adminName);-->
<select id="getUserByLastNameLikeReturnMap" resultType="com.Java.pojo.User">
    select * from `admin` where admin_Name like #{adminName};
</select>
<!--Map<String, Object> getUserByIdReturnMap(Integer adminId);-->
<select id="getUserByIdReturnMap" resultType="map">
    select * from `admin` where admin_Id=#{adminId};
</select>
<!--List<User> getUsersByLastNameLike(String adminName);-->
<select id="getUsersByLastNameLike" resultType="com.Java.pojo.User">
    select * from `admin` where admin_Name like #{adminName}
</select>
<!--User getUserByMap(Map<String, Object> map);-->
<select id="getUserByMap" resultType="com.Java.pojo.User">
    select * from ${tableName} where admin_Id=${adminId} and admin_Name=#{adminName}
</select>
<!--User getUserByIdAndLastName(Integer adminId, String adminName);-->
<select id="getUserByIdAndLastName" resultType="com.Java.pojo.User">
    select * from `admin` where admin_Id = #{adminId} and admin_Name=#{adminName};
</select>

TestMyabtis.class

@Test
public void getSqlSessionFactoryTest() throws IOException {
    String resource = "Mybatis-Config.xml";
    InputStream resourceAsStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession openSession = factory.openSession();
    UserDaoMapper mapper = openSession.getMapper(UserDaoMapper.class);
    //Map<Integer, User> userByLastNameLikeReturnMap = mapper.getUserByLastNameLikeReturnMap("%u%");
    //for (Map.Entry<Integer, User> entry : userByLastNameLikeReturnMap.entrySet()) {
    //    System.out.println(entry.getKey() + "----->" + entry.getValue());
    //}
    //Map<String, Object> userByIdReturnMap = mapper.getUserByIdReturnMap(1);
    //for (Map.Entry<String, Object> entry : userByIdReturnMap.entrySet()) {
    //    System.out.println(entry.getKey() + "----->" + entry.getValue());
    //}
    //List<User> usersByLastNameLike = mapper.getUsersByLastNameLike("%u%");
    //usersByLastNameLike.forEach(System.out::println);
    //Map<String, Object> map = new HashMap<String, Object>();
    //map.put("tableName", "admin");
    //map.put("adminId", 1);
    //map.put("adminName", "user");
    //User userByMap = mapper.getUserByMap(map);
    //System.out.println(userByMap);
    User user = mapper.getUserByIdAndLastName(1, "user");
    System.out.println(user);
    openSession.close();
}
7.9、select元素

Select元素来定义查询操作。

• Id:唯一标识符。 – 用来引用这条语句,需要和接口的方法名一致

• parameterType:参数类型。 – 可以不传,MyBatis会根据TypeHandler自动推断

• resultType:返回值类型。 – 别名或者全类名,如果返回的是集合,定义集合中元 素的类型。不能和resultMap同时使用

在这里插入图片描述

Select元素里面的属性:

在这里插入图片描述

7.10、自动映射

1、全局setting设置

– autoMappingBehavior默认是PARTIAL,开启自动映射 的功能。唯一的要求是列名和javaBean属性名一致

– 如果autoMappingBehavior设置为null则会取消自动映射

– 数据库字段命名规范,POJO属性符合驼峰命名法,如 A_COLUMNaColumn,我们可以开启自动驼峰命名规 则映射功能,mapUnderscoreToCamelCase=true。

2、自定义resultMap,实现高级结果集映射。

constructor - 类在实例化时, 用来注入结果到构造方法中

idArg - ID 参数; 标记结果作为 ID 可以帮助提高整体效能

arg - 注入到构造方法的一个普通结果

id – 一个 ID 结果; 标记结果作为 ID 可以帮助提高整体效能

result – 注入到字段或 JavaBean 属性的普通结果

association – 一个复杂的类型关联;许多结果将包成这种类型

– 嵌入结果映射 – 结果映射自身的关联,或者参考一个

collection – 复杂类型的集

– 嵌入结果映射 – 结果映射自身的集,或者参考一个

discriminator – 使用结果值来决定使用哪个结果映射

– case – 基于某些值的结果映射 • 嵌入结果映射 – 这种情形结果也映射它本身,因此可以包含很多相同的元 素,或者它可以参照一个外部的结果映射。

id & result:id 和 result 映射一个单独列的值到简单数据类型 (字符串,整型,双精度浮点数,日期等)的属性或字段。

在这里插入图片描述

代码如下:

UserDaoMapperPlus.class

User getUserById(Integer adminId);

UserDaoMapperPlus.xml

<!--自定义某个javaBean的封装规则
        type:自定义规则的Java类型
        id:唯一id方便引用
-->
<resultMap id="MyUserMap" type="com.Java.pojo.User">
      <!--指定主键列的封装规则
            id定义主键会底层有优化;
            column:指定哪一列
            property:指定对应的javaBean属性
      -->
      <id column="admin_Id" property="adminId"/>
      <result column="admin_Name" property="adminName"/>
      <!-- 其他不指定的列会自动封装:我们只要写resultMap就把全部的映射规则都写上.-->
      <result column="admin_Password" property="adminPassword"/>
      <result column="admin_Email" property="adminEmail"/>
</resultMap>

<!-- resultMap:自定义结果集映射规则;  -->
<!-- User getUserById(Integer adminId); -->
<select id="getUserById" resultMap="MyUserMap">
        select * from `admin` where admin_Id=#{adminId};
</select>

TestMybatis.class(单元测试)

//为了方便以后测试调用就封装成一个方法
private SqlSessionFactory getSqlSessionFactory() throws IOException {
    String resource = "Mybatis-Config.xml";
    InputStream resourceAsStream = Resources.getResourceAsStream(resource);
    return new SqlSessionFactoryBuilder().build(resourceAsStream);
}
@Test
public void getSqlSessionFactoryTest() {
    SqlSession openSession = null;
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        openSession = sqlSessionFactory.openSession();
        UserDaoMapperPlus mapper = openSession.getMapper(UserDaoMapperPlus.class);
        User userById = mapper.getUserById(1);
        System.out.println(userById);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (openSession != null) {
            openSession.close();
        }
    }
}

3、联合查询:级联属性封装结果集

代码如下:

创建adminProperty表:

#创建用户属性表 1:为超级管理员 2:普通管理员
CREATE TABLE adminProperty(
	a_propertyId INT PRIMARY KEY auto_increment,
	a_propertyName VARCHAR(20) NOT NULL
);
#向adminProperty表中添加数据
INSERT INTO adminProperty VALUES(NULL,'超级管理员'),(NULL,'普通管理员');

向admin表中添加列,并设置外键:

#添加列
alter table admin add column admin_propertyId INT NOT NULL;
#设置外键

在这里插入图片描述

创建对应的 adminProperty JavaBean:

UserProperty.class(JavaBean)

public class UserProperty {
    private Integer propertyId;
    private String propertyName;
    //有参和无参
    //Geter和Seter
    //toString()
}    

User.class

public class User {
    private Integer adminId;
    private String adminName;
    private String adminPassword;
    private String adminEmail;
    private UserProperty userProperty;
     //有参和无参
    //Geter和Seter
    //toString()
}    

UserDaoMapperPlus.class

User getUserAndProperty(Integer adminId);

UserDaoMapperPlus.xml

<!--
    联合查询:级联属性封装结果集
 -->
  <resultMap id="MyUserAndPropertyMap" type="com.Java.pojo.User">
      <id column="admin_Id" property="adminId"/>
      <result column="admin_Name" property="adminName"/>
      <result column="admin_Password" property="adminPassword"/>
      <result column="admin_Email" property="adminEmail"/>
      <result column="a_PropertyId" property="userProperty.propertyId"/>
      <result column="a_PropertyName" property="userProperty.propertyName"/>
  </resultMap>
<!--User getUserAndProperty(Integer adminId);-->
    <select id="getUserAndProperty" resultMap="MyUserAndPropertyMap">
        SELECT
           *
        FROM
            admin a,
            adminproperty ap
        WHERE
            a.admin_Id = ap.a_propertyId
            AND a.admin_Id = #{adminId};
</select>    

TestMybatis.Class(单元测试)

//为了方便以后测试调用就封装成一个方法
private SqlSessionFactory getSqlSessionFactory() throws IOException {
    String resource = "Mybatis-Config.xml";
    InputStream resourceAsStream = Resources.getResourceAsStream(resource);
    return new SqlSessionFactoryBuilder().build(resourceAsStream);
}

@Test
public void getSqlSessionFactoryTest() {
    SqlSession openSession = null;
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        openSession = sqlSessionFactory.openSession();
        UserDaoMapperPlus mapper = openSession.getMapper(UserDaoMapperPlus.class);
        User userAndProperty = mapper.getUserAndProperty(1);
        System.out.println(userAndProperty);
        System.out.println(userAndProperty.getUserProperty());
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (openSession != null) {
            openSession.close();
        }
    }
}

4、使用association(爱搜赛爱神)定义关联的单个对象的封装规则又称级联查询;

UserDaoMapperPlus.xml:

<!--
  使用association定义关联的单个对象的封装规则;
-->
<resultMap id="MyUserAndPropertyMap2" type="com.Java.pojo.User">
      <id column="admin_Id" property="adminId"/>
      <result column="admin_Name" property="adminName"/>
      <result column="admin_Password" property="adminPassword"/>
      <result column="admin_Email" property="adminEmail"/>
      <!--  association可以指定联合的javaBean对象
          property="dept":指定哪个属性是联合的对象
          javaType:指定这个属性对象的类型[不能省略]
	  -->
      <association property="userProperty" javaType="com.Java.pojo.UserProperty">
          <result column="a_PropertyId" property="propertyId"/>
          <result column="a_PropertyName" property="propertyName"/>
      </association>
</resultMap>
--------------------------------------------------------------------------------              
<!--User getUserAndProperty(Integer adminId);-->
<select id="getUserAndProperty" resultMap="MyUserAndPropertyMap2">
   SELECT
        *
   FROM
        admin a,
        adminproperty ap
   WHERE
        a.admin_Id = ap.a_propertyId
        AND a.admin_Id = #{adminId};
</select>              
<!--测试方法一样,省略..........-->   

5、使用association进行分段查询:

UserDaoMapper.class:

User getUserByIdStep(Integer adminId);

UserDaoPropertyMapper.class

UserProperty getUserPropertyById(Integer propertyId);

UserDaoMapper.xml:

<!--
    使用association定义关联的单个对象的封装规则;
   -->
<resultMap id="MyUserAndPropertyMap3" type="com.Java.pojo.User">
    <id column="admin_Id" property="adminId"/>
    <result column="admin_Name" property="adminName"/>
    <result column="admin_Password" property="adminPassword"/>
    <result column="admin_Email" property="adminEmail"/>
    <!-- association定义关联对象的封装规则
     select:表明当前属性是调用select指定的方法查出的结果
     column:指定将哪一列的值传给这个方法
     流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性
     -->
    <association property="userProperty"
                 select="com.Java.dao.UserDaoPropertyMapper.getUserPropertyById"
                 column="admin_PropertyId">
    </association>
</resultMap>

UserDaoPropertyMapper.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">
<mapper namespace="com.Java.dao.UserDaoPropertyMapper">
    <!-- 自定义封装规则 -->
    <resultMap id="MyUserPropertyMap" type="com.Java.pojo.UserProperty">
        <id column="a_propertyId" property="propertyId"/>
        <result column="a_propertyName" property="propertyName"/>
    </resultMap>

    <!--UserProperty getUserPropertyById(Integer propertyId);-->
    <select id="getUserPropertyById" resultMap="MyUserPropertyMap">
        select * from `adminproperty` where a_propertyId=#{propertyId}
    </select>

</mapper>

TestMybatis.Class(单元测试)

@Test
public void getSqlSessionFactoryTest() {
    SqlSession openSession = null;
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        openSession = sqlSessionFactory.openSession();
        UserDaoMapperPlus mapper = openSession.getMapper(UserDaoMapperPlus.class);
        User userByIdStep = mapper.getUserByIdStep(1);
        System.out.println(userByIdStep);
        System.out.println(userByIdStep.getUserProperty());
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (openSession != null) {
            openSession.close();
        }
    }
}

6、使用collection集合进行分段查询和延迟加载:

Mybatis-Config.xml:

<!-- 全局配置文件开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>

需求:查询属性的时候将属性对应的所有用户信息也查询出来:注释在UserDaoPropertyMapper.xml中

UserDaoMapperPlus.Class:(用户)

List<User> getUserByUserPropertyId(Integer propId);

UserDaoPropertyMapper.Class:(用户属性

UserProperty getUserPropertyByIdStep(Integer propertyId);

UserDaoMapperPlus.Xml:

<!--List<User> getUserByUserPropertyId(Integer propId);-->
<select id="getUserByUserPropertyId" resultType="com.Java.pojo.User">
    select * from `admin` where admin_PropertyId=#{propId};
</select>

UserDaoPropertyMapper.Xml:

<!-- collection:分段查询 -->
<resultMap type="com.Java.pojo.UserProperty" id="MyUserPropertyStep">
    <id column="a_propertyId" property="propertyId"/>
    <result column="a_propertyName" property="propertyName"/>
    <collection property="users"
                select="com.Java.dao.UserDaoMapperPlus.getUserByUserPropertyId"
                column="{propId=a_propertyId}" fetchType="lazy"/>
    <!--fetchType="lazy": fetchType="lazy":表示使用延迟加载;
						- lazy:延迟
						- eager:立即-->
</resultMap>

<!--UserProperty getUserPropertyByIdStep(Integer propertyId);-->
<select id="getUserPropertyByIdStep" resultMap="MyUserPropertyStep">
	select * from adminproperty where a_propertyId=#{propertyId}
</select>

7.11、discriminator鉴别器-------->案例

<!-- =======================鉴别器============================ -->
<!-- <discriminator javaType=""></discriminator>
		鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
		封装Employee:
			如果查出的是女生:就把部门信息查询出来,否则不查询;
			如果是男生,把last_name这一列的值赋值给email;
 -->
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpDis">
    <id column="id" property="id"/>
    <result column="last_name" property="lastName"/>
    <result column="email" property="email"/>
    <result column="gender" property="gender"/>
    <!--
        column:指定判定的列名
        javaType:列值对应的java类型  
     -->
    <discriminator javaType="string" column="gender">
        <!--女生  resultType:指定封装的结果类型;不能缺少。/resultMap-->
        <case value="0" resultType="com.atguigu.mybatis.bean.Employee">
        <association property="dept" 
        select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
        column="d_id">
        </association>
        </case>
        <!--男生 ;如果是男生,把last_name这一列的值赋值给email; -->
        <case value="1" resultType="com.atguigu.mybatis.bean.Employee">
        <id column="id" property="id"/>
        <result column="last_name" property="lastName"/>
        <result column="last_name" property="email"/>
        <result column="gender" property="gender"/>
        </case>
    </discriminator>
</resultMap>

8、Mybatis动态SQL

英文翻译:(dynamic动态)

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

SQL的操作。

• 动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处

理器相似。

• MyBatis 采用功能强大的基于 OGNL 的表达式来简化操作。

if

choose (when, otherwise)

trim (where, set)

foreach

8.1、if

admin(表):

#添加admin_Gender列 0为男 1为女
alter table admin add column admin_Gender INT NOT NULL;

User.Class(JavaBean)

public class User {
    private Integer adminId;
    private String adminName;
    private String adminPassword;
    private String adminEmail;
    private Integer adminGender;
}   

UserDaoMapperDynamicSQL.Class

List<User> getUserByConditionIf(User users);

UserDaoMapperDynamicSQL.Xml

<!--if:判断
    • choose (when, otherwise):分支选择;带了breakswitch-case
        如果带了id就用id查,如果带了lastName就用lastName查;只会进入其中一个
    • trim 字符串截取(where(封装查询条件), set(封装修改条件))
    • foreach 遍历集合
-->
<!-- List<User> getUserByConditionIf(User users); -->
<select id="getUserByConditionIf" resultType="com.Java.pojo.User">
    select * from `admin`
    <!-- where -->
    <where>
        <!-- test:判断表达式(OGNL)
        OGNL参照PPT或者官方文档。
               c:if  test
        从参数中取值进行判断
        遇见特殊符号应该去写转义字符:
        &&-->
        <if test="adminId!=null">
            admin_Id=#{adminId}
        </if>
        <if test="adminName!=null &amp;&amp; adminName!=&quot;&quot;">
            and admin_Name like #{adminName}
        </if>
        <if test="adminPassword!=null and adminPassword.trim()!=&quot;&quot;">
            and admin_Email=#{adminEmail}
        </if>
        <!-- ognl会进行字符串与数字的转换判断  "0"==0 -->
        <if test="adminGender==0 or adminGender==1">
            and admin_Gender=#{adminGender}
        </if>
    </where>
</select>

TestMybatis.Class(测试)

@Test
public void getSqlSessionFactoryTest() {
    SqlSession openSession = null;
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        openSession = sqlSessionFactory.openSession();
        UserDaoMapperDynamicSQL mapper = openSession.getMapper(UserDaoMapperDynamicSQL.class);
        List<User> userByConditionIf = mapper.getUserByConditionIf(new User(1, null, null, null, null, 0));
        userByConditionIf.forEach(System.out::println);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (openSession != null) {
            openSession.close();
        }
    }
}
8.2、trim

UserDaoMapperDynamicSQL.Class

List<User> getUserByConditionTrim(User users);

UserDaoMapperDynamicSQL.Xml

<!--List<User> getUserByConditionTrim(User users);-->
<select id="getUserByConditionTrim" resultType="com.Java.pojo.User">
    select * from `admin`
    <!-- 后面多出的and或者or where标签不能解决
    prefix="":前缀:trim标签体中是整个字符串拼串 后的结果。
            prefix给拼串后的整个字符串加一个前缀
    prefixOverrides="":
            前缀覆盖: 去掉整个字符串前面多余的字符
    suffix="":后缀
            suffix给拼串后的整个字符串加一个后缀
    suffixOverrides=""
            后缀覆盖:去掉整个字符串后面多余的字符
    -->
    <!-- 自定义字符串的截取规则 -->
    <trim prefix="where" suffixOverrides="and">
        <if test="adminId!=null">
            admin_Id=#{adminId} and
        </if>
        <if test="adminName!=null &amp;&amp; adminName!=&quot;&quot;">
            admin_Name like #{adminName} and
        </if>
        <if test="adminEmail!=null and adminEmail.trim()!=&quot;&quot;">
            admin_Email=#{adminEmail} and
        </if>
        <!-- ognl会进行字符串与数字的转换判断  "0"==0 -->
        <if test="adminGender==0 or adminGender==1">
            admin_Gender=#{adminGender}
        </if>
    </trim>
</select>

TestMybatis.Class(测试)

@Test
public void getSqlSessionFactoryTest() {
    SqlSession openSession = null;
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        openSession = sqlSessionFactory.openSession();
        UserDaoMapperDynamicSQL mapper = openSession.getMapper(UserDaoMapperDynamicSQL.class);
        List<User> user = mapper.getUserByConditionTrim(new User(null, "user", null, null, null, 1));
        user.forEach(System.out::println);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (openSession != null) {
            openSession.close();
        }
    }
}
8.3、choose (when, otherwise):

UserDaoMapperDynamicSQL.Class

List<User> getUsersByConditionChoose(User users);

UserDaoMapperDynamicSQL.Xml

<!--List<User> getUsersByConditionChoose(User users);-->
<select id="getUsersByConditionChoose" resultType="com.Java.pojo.User">
    select * from `admin`
    <where>
        <!-- 如果带了id就用id查,如果带了lastName就用lastName查;只会进入其中一个 -->
        <choose>
            <when test="adminId!=null">
                admin_Id=#{adminId}
            </when>
            <when test="adminName!=null">
                admin_Name like #{adminName}
            </when>
            <when test="adminEmail!=null">
                admin_Email = #{adminEmail}
            </when>
            <otherwise>
                admin_Gender = 0
            </otherwise>
        </choose>
    </where>
</select>

TestMybatis.Class(测试)

@Test
public void getSqlSessionFactoryTest() {
    SqlSession openSession = null;
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        openSession = sqlSessionFactory.openSession();
        UserDaoMapperDynamicSQL mapper = openSession.getMapper(UserDaoMapperDynamicSQL.class);
        List<User> admin = mapper.getUsersByConditionChoose(new User(null, "user", null, null, null, null));
        admin.forEach(System.out::println);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (openSession != null) {
            openSession.close();
        }
    }
}
8.4、Set标签的使用

UserDaoMapperDynamicSQL.Class

void updateUser(User user);

UserDaoMapperDynamicSQL.Xml

<!--void updateUser(User user);-->
<select id="updateUser" resultType="com.Java.pojo.User">
    update `admin`
    <set>
        <if test="adminName!=null">
            admin_Name=#{adminName},
        </if>
        <if test="adminEmail!=null">
            admin_Email=#{adminEmail},
        </if>
        <if test="adminGender!=null">
            admin_Gender=#{adminGender}
        </if>
    </set>
    where admin_Id=#{adminId};
</select>

TestMybatis.Class(单元测试)

@Test
public void getSqlSessionFactoryTest() {
    SqlSession openSession = null;
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        openSession = sqlSessionFactory.openSession();
        UserDaoMapperDynamicSQL mapper = openSession.getMapper(UserDaoMapperDynamicSQL.class);
        mapper.updateUser(new User(1, "admin", null, "admin@qq.com", null, 1));
        openSession.commit();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (openSession != null) {
            openSession.close();
        }
    }
}
8.5、foreach

UserDaoMapperDynamicSQL.Class

List<User> getUserByConditionForeach(@Param("adminIds") List<Integer> adminIds);

UserDaoMapperDynamicSQL.Xml

<!--List<User> getUserByConditionForeach(List<Integer> adminIds);-->
<select id="getUserByConditionForeach" resultType="com.Java.pojo.User">
    select * from `admin`
    <!--
        collection:指定要遍历的集合:
            list类型的参数会特殊处理封装在map中,map的key就叫list
        item:将当前遍历出的元素赋值给指定的变量
        separator:每个元素之间的分隔符
        open:遍历出所有结果拼接一个开始的字符
        close:遍历出所有结果拼接一个结束的字符
        index:索引。遍历list的时候是index就是索引,item就是当前值
                      遍历map的时候index表示的就是map的key,item就是map的值

        #{变量名}就能取出变量的值也就是当前遍历出的元素
      -->
    <foreach collection="adminIds" item="items" separator=","
             open="where admin_Id in(" close=")">
        #{items}
    </foreach>
</select>

TestMybatis.Class(单元测试)

@Test
public void getSqlSessionFactoryTest() {
    SqlSession openSession = null;
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        openSession = sqlSessionFactory.openSession();
        UserDaoMapperDynamicSQL mapper = openSession.getMapper(UserDaoMapperDynamicSQL.class);
        List<Integer> adminIds = new ArrayList<Integer>();
        adminIds.add(1);
        adminIds.add(2);
        adminIds.add(3);
        List<User> userByConditionForeach = mapper.getUserByConditionForeach(adminIds);
        userByConditionForeach.forEach(System.out::println);
        openSession.commit();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (openSession != null) {
            openSession.close();
        }
    }
}
8.6、foreach批量保存

UserDaoMapperDynamicSQL.Class

void addUsers(@Param("users") List<User> users);

UserDaoMapperDynamicSQL.Xml

<!--void addUsers(@Param("users") List<User> users);-->
<insert id="addUsers">
    insert into `admin`(
    <include refid="insertColumn"/>
    )
    values
    <foreach collection="users" item="user" separator=",">
        (#{user.adminName},#{user.adminPassword},#{user.adminEmail},#{user.adminPropertyId},#{user.adminGender})
    </foreach>
</insert>

<sql id="insertColumn" databaseId="mysql">
    admin_Name,admin_Password,admin_Email,admin_PropertyId,admin_Gender
</sql>

TestMyabtis.Class

@Test
public void getSqlSessionFactoryTest() {
    SqlSession openSession = null;
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        openSession = sqlSessionFactory.openSession();
        UserDaoMapperDynamicSQL mapper = openSession.getMapper(UserDaoMapperDynamicSQL.class);
        List<User> users = new ArrayList<User>();
        users.add(new User(null, "123", "123", "123@qq.com", 1, 0));
        users.add(new User(null, "456", "456", "456@qq.com", 2, 1));
        users.add(new User(null, "789", "789", "789@qq.com", 1, 1));
        mapper.addUsers(users);
        openSession.commit();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (openSession != null) {
            openSession.close();
        }
    }
}
8.7、include
<!-- 
    抽取可重用的sql片段。方便后面引用 
    1、sql抽取:经常将要查询的列名,或者插入用的列名抽取出来方便引用
    2、include来引用已经抽取的sql:
    3、include还可以自定义一些property,sql标签内部就能使用自定义的属性
    include-property:取值的正确方式${prop},
    #{不能使用这种方式}
-->
<sql id="insertColumn">
    <if test="_databaseId=='oracle'">
    	employee_id,last_name,email
    </if>
    <if test="_databaseId=='mysql'">
    	 admin_Name,admin_Password,admin_Email,admin_PropertyId,admin_Gender
    </if>
</sql>

9、MyBatis-缓存机制

MyBatis 包含一个非常强大的查询缓存特性,它可以非

常方便地配置和定制。缓存可以极大的提升查询效率。

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

一级缓存和二级缓存

– 1、默认情况下,只有一级缓存(SqlSession级别的缓存,

也称为本地缓存)开启。

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

别的缓存。

– 3、为了提高扩展性。MyBatis定义了缓存接口Cache。我们

可以通过实现Cache接口来自定义二级缓存

一级缓存演示&失效情况:

Mybatis-Config.xml

<!--显式的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题  -->
<setting name="cacheEnabled" value="true"/>

UserDaoMapper.Xml

<!--  
     eviction:缓存的回收策略:
      • LRU – 最近最少使用的:移除最长时间不被使用的对象。
      • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
      • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
      • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
      • 默认的是 LRU。
     flushInterval:缓存刷新间隔
      缓存多长时间清空一次,默认不清空,设置一个毫秒值
     readOnly:是否只读:
      true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。
         mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快
      false:非只读:mybatis觉得获取的数据可能会被修改。
        mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢
     size:缓存存放多少元素;
     type="":指定自定义缓存的全类名;
       实现Cache接口即可;
-->
<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"/>

TestMybatis.Class

package com.atguigu.mybatis.test;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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 org.junit.Test;

import com.atguigu.mybatis.bean.Department;
import com.atguigu.mybatis.bean.Employee;
import com.atguigu.mybatis.dao.DepartmentMapper;
import com.atguigu.mybatis.dao.EmployeeMapper;
import com.atguigu.mybatis.dao.EmployeeMapperAnnotation;
import com.atguigu.mybatis.dao.EmployeeMapperDynamicSQL;
import com.atguigu.mybatis.dao.EmployeeMapperPlus;

public class MyBatisTest {
	

	public SqlSessionFactory getSqlSessionFactory() throws IOException {
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		return new SqlSessionFactoryBuilder().build(inputStream);
	}
	
	/**
	 * 两级缓存:
	 * 一级缓存:(本地缓存):sqlSession级别的缓存。一级缓存是一直开启的;SqlSession级别的一个Map
	 * 		与数据库同一次会话期间查询到的数据会放在本地缓存中。
	 * 		以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库;
	 * 
	 * 		一级缓存失效情况(没有使用到当前一级缓存的情况,效果就是,还需要再向数据库发出查询):
	 * 		1、sqlSession不同。
	 * 		2、sqlSession相同,查询条件不同.(当前一级缓存中还没有这个数据)
	 * 		3、sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响)
	 * 		4、sqlSession相同,手动清除了一级缓存(缓存清空)
	 * 
	 * 二级缓存:(全局缓存):基于namespace级别的缓存:一个namespace对应一个二级缓存:
	 * 		工作机制:
	 * 		1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;
	 * 		2、如果会话关闭;一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中的内容;
	 * 		3、sqlSession===EmployeeMapper==>Employee
	 * 						DepartmentMapper===>Department
	 * 			不同namespace查出的数据会放在自己对应的缓存中(map)
	 * 			效果:数据会从二级缓存中获取
	 * 				查出的数据都会被默认先放在一级缓存中。
	 * 				只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中
	 * 		使用:
	 * 			1)、开启全局二级缓存配置:<setting name="cacheEnabled" value="true"/>
	 * 			2)、去mapper.xml中配置使用二级缓存:
	 * 				<cache></cache>
	 * 			3)、我们的POJO需要实现序列化接口
	 * 	
	 * 和缓存有关的设置/属性:
	 * 			1)、cacheEnabled=true:false:关闭缓存(二级缓存关闭)(一级缓存一直可用的)
	 * 			2)、每个select标签都有useCache="true":
	 * 					false:不使用缓存(一级缓存依然使用,二级缓存不使用)
	 * 			3)、【每个增删改标签的:flushCache="true":(一级二级都会清除)】
	 * 					增删改执行完成后就会清楚缓存;
	 * 					测试:flushCache="true":一级缓存就清空了;二级也会被清除;
	 * 					查询标签:flushCache="false":
	 * 						如果flushCache=true;每次查询之后都会清空缓存;缓存是没有被使用的;
	 * 			4)、sqlSession.clearCache();只是清楚当前session的一级缓存;
	 * 			5)、localCacheScope:本地缓存作用域:(一级缓存SESSION);当前会话的所有数据保存在会话缓存中;
	 * 								STATEMENT:可以禁用一级缓存;		
	 * 				
	 *第三方缓存整合:
	 *		1)、导入第三方缓存包即可;
	 *		2)、导入与第三方缓存整合的适配包;官方有;
	 *		3)、mapper.xml中使用自定义缓存
	 *		<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
	 *
	 * @throws IOException 
	 * 
	 */
	@Test
	public void testSecondLevelCache02() throws IOException{
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		SqlSession openSession = sqlSessionFactory.openSession();
		SqlSession openSession2 = sqlSessionFactory.openSession();
		try{
			//1、
			DepartmentMapper mapper = openSession.getMapper(DepartmentMapper.class);
			DepartmentMapper mapper2 = openSession2.getMapper(DepartmentMapper.class);
			
			Department deptById = mapper.getDeptById(1);
			System.out.println(deptById);
			openSession.close();
			Department deptById2 = mapper2.getDeptById(1);
			System.out.println(deptById2);
			openSession2.close();
			//第二次查询是从二级缓存中拿到的数据,并没有发送新的sql
		}finally{
			
		}
	}
	@Test
	public void testSecondLevelCache() throws IOException{
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		SqlSession openSession = sqlSessionFactory.openSession();
		SqlSession openSession2 = sqlSessionFactory.openSession();
		try{
			//1、
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
			
			Employee emp01 = mapper.getEmpById(1);
			System.out.println(emp01);
			openSession.close();
			
			//第二次查询是从二级缓存中拿到的数据,并没有发送新的sql
			//mapper2.addEmp(new Employee(null, "aaa", "nnn", "0"));
			Employee emp02 = mapper2.getEmpById(1);
			System.out.println(emp02);
			openSession2.close();
			
		}finally{
			
		}
	}
	
	@Test
	public void testFirstLevelCache() throws IOException{
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		SqlSession openSession = sqlSessionFactory.openSession();
		try{
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			Employee emp01 = mapper.getEmpById(1);
			System.out.println(emp01);
			
			//xxxxx
			//1、sqlSession不同。
			//SqlSession openSession2 = sqlSessionFactory.openSession();
			//EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
			
			//2、sqlSession相同,查询条件不同
			
			//3、sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响)
			//mapper.addEmp(new Employee(null, "testCache", "cache", "1"));
			//System.out.println("数据添加成功");
			
			//4、sqlSession相同,手动清除了一级缓存(缓存清空)
			//openSession.clearCache();
			
			Employee emp02 = mapper.getEmpById(1);
			//Employee emp03 = mapper.getEmpById(3);
			System.out.println(emp02);
			//System.out.println(emp03);
			System.out.println(emp01==emp02);
			
			//openSession2.close();
		}finally{
			openSession.close();
		}
	}
}
二级缓存

• 二级缓存(second level cache),全局作用域缓存

• 二级缓存默认不开启,需要手动配置

• MyBatis提供二级缓存的接口以及实现,缓存实现要求

POJO实现Serializable接口

• 二级缓存在 SqlSession** 关闭或提交****之后才会生效

• 使用步骤

– 1、全局配置文件中开启二级缓存

• <setting name=“cacheEnabled” value=“true”/>

– 2、需要使用二级缓存的映射文件处使用cache配置缓存

– 3、注意:POJO需要实现Serializable接口

缓存相关属性

• **eviction=“FIFO”:**缓存回收策略:

• LRU – 最近最少使用的:移除最长时间不被使用的对象。

• FIFO – 先进先出:按对象进入缓存的顺序来移除它们。

• SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。

• WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

• 默认的是 LRU。

• flushInterval:刷新间隔,单位毫秒

• 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新

• size:引用数目,正整数

• 代表缓存最多可以存储多少个对象,太大容易导致内存溢出

• readOnly:只读,true/false

• true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象

不能被修改。这提供了很重要的性能优势。

• false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,

但是安全,因此默认是 false。

缓存有关设置

• 1、全局setting的cacheEnable:

– 配置二级缓存的开关。一级缓存一直是打开的。

• 2、select标签的useCache属性:

– 配置这个select是否使用二级缓存。一级缓存一直是使用的

• 3、sql标签的flushCache属性:

– 增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。

查询默认flushCache=false。

• 4、sqlSession.clearCache():

– 只是用来清除一级缓存。

• 5、当在某一个作用域 (一级缓存Session/二级缓存

Namespaces) 进行了 C/U/D 操作后,默认该作用域下所

有 select 中的缓存将被clear。

第三方缓存整合

• EhCache 是一个纯Java的进程内缓存框架,具有快速、精

干等特点,是Hibernate中默认的CacheProvider。

• MyBatis定义了Cache接口方便我们进行自定义扩展。

• 步骤:

– 1、导入ehcache包,以及整合包,日志包

ehcache-core-2.6.8.jar、mybatis-ehcache-1.0.3.jar

slf4j-api-1.6.1.jar、slf4j-log4j12-1.6.2.jar

– 2、编写ehcache.xml配置文件

– 3、配置cache标签

– <cache type=“org.mybatis.caches.ehcache.EhcacheCache”>

• 参照缓存:若想在命名空间中共享相同的缓存配置和实例。

可以使用 cache-ref 元素来引用另外一个缓存。

在这里插入图片描述

当执行一条查询SQL时,流程为从二级缓存中进行查询,进入一级缓存中查询,执行 JDBC 查询。

在这里插入图片描述

10、Mybatis-SSM整合(已经整合,见ssm整合)

11、Mybatis-逆向工程

MyBatis Generator:

• 简称MBG,是一个专门为MyBatis框架使用者定制的代码生成器,可以快速的根据表生成对应的映射文件,接口,以及bean类。支持基本的增、删、改、查,以及QBC风格的条件查询。但是表连接、存储过程等这些复杂sql的定义需要我们手工编写

• 官方文档地址

http://www.mybatis.org/generator/

• 官方工程地址

https://github.com/mybatis/generator/releases

1、手动导入jar包方式
1.1、使用步骤

– 1)编写MBG的配置文件(重要几处配置)

​ 1)jdbcConnection配置数据库连接信息

​ 2)javaModelGenerator配置javaBean的生成策略

​ 3)sqlMapGenerator 配置sql映射文件生成策略

​ 4)javaClientGenerator配置Mapper接口的生成策略

​ 5)table 配置要逆向解析的数据表

​ tableName:表名

​ domainObjectName:对应的javaBean名

– 2)运行代码生成器生成代码

• 注意:

Context标签

targetRuntime=“MyBatis3“ 可以生成带条件的增删改查

targetRuntime=“MyBatis3Simple“ 可以生成基本的增删改查

如果再次生成,建议将之前生成的数据删除,避免xml向后追加内容出现的问题。

1.2、创建配置文件mbg.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:豪华版
	
	 -->
  <context id="DB2Tables" targetRuntime="MyBatis3">
  	<!-- jdbcConnection:指定如何连接到目标数据库 -->
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
        connectionURL="jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true"
        userId="root"
        password="123456">
    </jdbcConnection>
    
	<!--  -->
    <javaTypeResolver >
      <property name="forceBigDecimals" value="false" />
    </javaTypeResolver>

	<!-- javaModelGenerator:指定javaBean的生成策略 
	targetPackage="test.model":目标包名
	targetProject="\MBGTestProject\src":目标工程
	-->
    <javaModelGenerator targetPackage="com.atguigu.mybatis.bean" 
    		targetProject=".\src">
      <property name="enableSubPackages" value="true" />
      <property name="trimStrings" value="true" />
    </javaModelGenerator>

	<!-- sqlMapGenerator:sql映射生成策略: -->
    <sqlMapGenerator targetPackage="com.atguigu.mybatis.dao"  
    	targetProject=".\conf">
      <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>

	<!-- javaClientGenerator:指定mapper接口所在的位置 -->
    <javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.mybatis.dao"  
    	targetProject=".\src">
      <property name="enableSubPackages" value="true" />
    </javaClientGenerator>

	<!-- 指定要逆向分析哪些表:根据表要创建javaBean -->
    <table tableName="tbl_dept" domainObjectName="Department"></table>
    <table tableName="tbl_employee" domainObjectName="Employee"></table>
  </context>
</generatorConfiguration>
1.3、生成器生成代码
public static void main(String[] args) throws Exception {
    List<String> warnings = new ArrayList<String>();
    boolean overwrite = true;
    File configFile = new File("mbg.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);
}

1.4、测试查询

测试查询:
QBC风格的带条件查询
@Test
public void test01(){
    SqlSession openSession = build.openSession();
    DeptMapper mapper = openSession.getMapper(DeptMapper.class);
    DeptExample example = new DeptExample();
    //所有的条件都在example中封装
    Criteria criteria = example.createCriteria();
    //select id, deptName, locAdd from tbl_dept WHERE 
    //( deptName like ? and id > ? ) 
    criteria.andDeptnameLike("%部%");
    criteria.andIdGreaterThan(2);
    List<Dept> list = mapper.selectByExample(example);
    for (Dept dept : list) {	
         System.out.println(dept);
	} 
}
2、基于Maven插件创建Mybatis逆向工程
2.1、注入所需依赖
<?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>com,Java.mybatis</groupId>
    <artifactId>Mybatis_Review</artifactId>
    <version>1.0-SNAPSHOT</version>

    <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>3.4.6</version>
        </dependency>
        <!--MySql8版本依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.0</version>
                <!-- 插件的依赖 -->
                <dependencies>
                    <!-- 逆向工程的核心依赖 -->
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.2</version>
                    </dependency>
                    <!-- 数据库连接池 -->
                    <dependency>
                        <groupId>com.mchange</groupId>
                        <artifactId>c3p0</artifactId>
                        <version>0.9.2</version>
                    </dependency>
                    <!-- MySQL 驱动 -->
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>8.0.20</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
</project>
2.2、配置逆向工程配置文件generatorConfig.xml 在resource->generatorConfig.xml
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">//dtd约束爆红快捷键提示自动下载约束
<generatorConfiguration>
    <!--MBG的配置文件-->
    <!-- targetRuntime=“MyBatis3“  生成带条件的增删改查(动态sql)
         targetRuntime=“MyBatis3Simple“  生成基本的增删改查
    -->
    <context id="DBTables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自动生成的注释 true:是;false:否 -->
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <jdbcConnection
                driverClass="com.mysql.cj.jdbc.Driver"
                connectionURL="jdbc:mysql://localhost:3306/user?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;serverTimezone=GMT"
                userId="root"
                password="130428">
        </jdbcConnection>

        <!-- 默认 false,把 JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true 时把
        JDBC DECIMAL和 NUMERIC 类型解析为 java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>

        <!--javaBean的生成策略
                targetProject:生成实体类的路径
                targetPackage:生成的实体类的所在包名
         -->
        <javaModelGenerator targetProject=".\src\main\java"
                            targetPackage="com.Java.pojo">
            <!-- enableSubPackages:是否让 schema 作为包的后缀 -->
            <property name="enableSubPackages" value="false"/>
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>

        <!--映射文件的生成策略
                targetProject:XxxMapper.xml 映射文件生成的路径 -->
        <sqlMapGenerator targetProject=".\src\main\resources"
                         targetPackage="mapper">
            <!-- enableSubPackages:是否让 schema 作为包的后缀 -->
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>

        <!-- dao接口(映射器)的生成策略
                    targetPackage:Mapper 接口生成的位置 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetProject=".\src\main\java"
                             targetPackage="com.Java.dao">
            <!-- enableSubPackages:是否让 schema 作为包的后缀 -->
            <property name="enableSubPackages" value="false"/>
        </javaClientGenerator>

        <!-- 数据表与javaBean的映射
                    数据库表名字和实体类对应的映射指定 -->
        <table tableName="admin" domainObjectName="User"/>
        <table tableName="adminproperty" domainObjectName="UserProperty"/>
    </context>
</generatorConfiguration>
2.3、启动逆向工程

在这里插入图片描述

2.4、成功创建

在这里插入图片描述

2.5、测试逆向工程
@Test
public void getSqlSessionFactoryTest() {
    SqlSession sqlSession = null;
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.insert(new User(null, "逆向工程", "123456", "123@qq.com", 1, 0));
        sqlSession.commit();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}

12、Mybatis-运行原理

在这里插入图片描述

13、MyBatis-插件开发

1、MyBatis在四大对象的创建过程中,都会有插件进行介入。插件可以利用动态代理机制一层层的包装目标对象,而实现在目标对象执行目标方法之前进行拦截的效果。

2、MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用。

3、默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

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

• ParameterHandler (getParameterObject, setParameters)

• ResultSetHandler (handleResultSets, handleOutputParameters)

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

1、插件开发

• 插件开发步骤

1.1、编写插件实现Interceptor接口,并使用

@Intercepts注解完成插件签名

@Intercepts({
        @Signature(type = StatementHandler.class, method = "parameterize", args = java.sql.Statement.class)
})

1.2、在全局配置文件中注册插件

<plugins>
    <plugin interceptor="com.Java.dao.MyFirstPlugin">
        <property name="userName" value="password"/>
    </plugin>
</plugins>

1.3、插件原理

1)、按照插件注解声明,按照插件配置顺序调用插件plugin方法,生成被拦截对象的动态代理

2)、多个插件依次生成目标对象的代理对象,层层包裹,先声明的先包裹;形成代理链

3)、目标方法执行时依次从外到内执行插件的intercept方法。

在这里插入图片描述

4)、多个插件情况下,我们往往需要在某个插件中分离出目标对象。可以借助MyBatis提供的SystemMetaObject类来进行获取最后一层的h以及target属性的值

2、Interceptor接口

2.1、Intercept:拦截目标方法执行

2.2、plugin:生成动态代理对象,可以使用MyBatis提

供的Plugin类的wrap方法

2.3、setProperties:注入插件配置时设置的属性

在这里插入图片描述

3、代码演示
@Intercepts({
        @Signature(type = StatementHandler.class, method = "parameterize", args = java.sql.Statement.class)
})
public class MyFirstPlugin implements Interceptor {
    /**
     * intercept:拦截:
     * 拦截目标对象的目标方法的执行;
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("MyFirstPlugin.....intercept:" + invocation.getMethod());
        Object target = invocation.getTarget();
        System.out.println("当前拦截到的对象:" + target);
        MetaObject metaObject = SystemMetaObject.forObject(target);
        Object value = metaObject.getValue("parameterHandler.parameterObject");
        System.out.println("当前sql语句执行的参数是:" + value);
        metaObject.setValue("parameterHandler.parameterObject", 3);
        return invocation.proceed();
    }

    /**
     * plugin:包装目标对象的:包装:为目标对象创建一个代理对象
     */
    @Override
    public Object plugin(Object target) {
        System.out.println("MyFirstPlugin.....plugin将要包装的对象:" + target);
        return Plugin.wrap(target, this);
    }

    /**
     * setProperties:将插件注册时的property属性设置进来
     */
    @Override
    public void setProperties(Properties properties) {
        System.out.println("插件配置的信息:" + properties);
    }
}
4、代码测试
@Test
public void getSqlSessionFactoryTest() {
    SqlSession sqlSession = null;
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.selectByPrimaryKey(1);
        System.out.println(user);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}
5、测试结果
插件配置的信息:{userName=password}
MyFirstPlugin.....plugin将要包装的对象:org.apache.ibatis.executor.CachingExecutor@57c758ac
MyFirstPlugin.....plugin将要包装的对象:org.apache.ibatis.scripting.defaults.DefaultParameterHandler@35ef1869
MyFirstPlugin.....plugin将要包装的对象:org.apache.ibatis.executor.resultset.DefaultResultSetHandler@68999068
MyFirstPlugin.....plugin将要包装的对象:org.apache.ibatis.executor.statement.RoutingStatementHandler@2ef3eef9
DEBUG 05-07 19:41:19,649 ==>  Preparing: select admin_Id, admin_Name, admin_Password, admin_Email, admin_PropertyId, admin_Gender from admin where admin_Id = ?   (BaseJdbcLogger.java:159) 
MyFirstPlugin.....intercept:public abstract void org.apache.ibatis.executor.statement.StatementHandler.parameterize(java.sql.Statement) throws java.sql.SQLException
当前拦截到的对象:org.apache.ibatis.executor.statement.RoutingStatementHandler@2ef3eef9
当前sql语句执行的参数是:1
DEBUG 05-07 19:41:19,692 ==> Parameters: 3(Integer)  (BaseJdbcLogger.java:159) 
DEBUG 05-07 19:41:19,734 <==      Total: 1  (BaseJdbcLogger.java:159) 
User{adminId=3, adminName='abcd', adminPassword='abcd', adminEmail='abcd@qq.xom', adminPropertyid=1, adminGender=0}

14、Mybatis-扩展

14.1、PageHelper插件进行分页

1、PageHelper是MyBatis中非常方便的第三方分页插件。

2、PageHelper官网文档

https://github.com/pagehelper/Mybatis

PageHelper/blob/master/README_zh.md

3、PageHelper使用步骤:

3.1、手动导入jar包

在这里插入图片描述

在这里插入图片描述

3.2、基于Maven注入依赖

<!-- pageHelper分页插件 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.11</version>
</dependency>

3.3、在MyBatis全局配置文件中配置分页插件。

<!-- Mybatis-Config -->
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <property name="param1" value="param2"/>
    </plugin>
</plugins>

3.4、使用PageHelper提供的方法进行分页

代码演示:

UserDaoMapper.Xml

<!--List<User> queryAllUser();-->
<select id="queryAllUser" resultType="com.Java.pojo.User">
   select * from `admin` <!--此处就不要加引号了-->
</select>

TestMybatis.class

@Test
public void pageHelperTest() {
    SqlSession sqlSession = null;
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Page<Object> objects = PageHelper.offsetPage(0, 5);
        List<User> users = mapper.queryAllUser();
        //传入要连续显示的页数
        for (User user : users) {
            System.out.println(user);
        }
        System.out.println("当前页码:" + objects.getPageNum());
        System.out.println("总记录数:" + objects.getTotal());
        System.out.println("每页的记录数:" + objects.getPageSize());
        System.out.println("总页码:" + objects.getPages());
        System.out.println("----------------------");
        PageInfo<User> userPageInfo = new PageInfo<>(users, 5);
        System.out.println("当前页码:" + userPageInfo.getPageNum());
        System.out.println("总记录数:" + userPageInfo.getTotal());
        System.out.println("每页的记录数:" + userPageInfo.getPageSize());
        System.out.println("总页码:" + userPageInfo.getPages());
        System.out.println("是否第一页:" + userPageInfo.isIsFirstPage());
        System.out.println("连续显示的页码:");
        int[] navigateNums = userPageInfo.getNavigatepageNums();
        for (int navigateNum : navigateNums) {
            System.out.println(navigateNum);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}

3.5、 可以使用更强大的PageInfo封装返回结果

 		PageInfo<User> userPageInfo = new PageInfo<>(users, 5);
        System.out.println("当前页码:" + userPageInfo.getPageNum());
        System.out.println("总记录数:" + userPageInfo.getTotal());
        System.out.println("每页的记录数:" + userPageInfo.getPageSize());
        System.out.println("总页码:" + userPageInfo.getPages());
        System.out.println("是否第一页:" + userPageInfo.isIsFirstPage());
        System.out.println("连续显示的页码:");
        int[] navigateNums = userPageInfo.getNavigatepageNums();
        for (int navigateNum : navigateNums) {
            System.out.println(navigateNum);
}
14.2、批量操作

1、默认的 openSession() 方法没有参数,它会创建有如下特性的

– 会开启一个事务(也就是不自动提交)

– 连接对象会从由活动环境配置的数据源实例得到。

– 事务隔离级别将会使用驱动或数据源的默认设置。

– 预处理语句不会被复用,也不会批量处理更新。

2、openSession 方法的 ExecutorType类型的参数,枚举类型:

– ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情(这是默认装配

的)。它为每个语句的执行创建一个新的预处理语句。

– ExecutorType.REUSE: 这个执行器类型会复用预处理语句。

– ExecutorType.BATCH: 这个执行器会批量执行所有更新语句

在这里插入图片描述

2、代码演示

@Test
public void testBatch() {
    SqlSession sqlSession = null;
    long start = System.currentTimeMillis();
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        for (int i = 0; i <= 10000; i++) {
            mapper.insert(new User(null, UUID.randomUUID().toString().substring(0, 5), "123456", "pilaing@qq.com", 1, 0));
        }
        sqlSession.commit();
        long end = System.currentTimeMillis();
        System.out.println("耗时:" + (end - start));
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}
耗时:7006
14.3、自定义TypeHandler处理枚举

1、我们可以通过自定义TypeHandler的形式来在设置参数或者取出结果集的时候自定义参数封装策略。

• 步骤:

– 1、实现TypeHandler接口或者继承BaseTypeHandler

– 2、使用@MappedTypes定义处理的java类型使用@MappedJdbcTypes定义jdbcType类型

– 3、在自定义结果集标签或者参数处理的时候声明使用自定义

TypeHandler进行处理或者在全局配置TypeHandler要处理的javaTyp

2、代码演示

新建一个枚举类:

UserEnum.Class:

package com.Java.Enum;

/**
 * UserEnum:定义一个枚举类,来显示用户的登录状态
 */
public enum UserEnum {
    //枚举的索引
    //0                                 1                                   2
    LOGIN(100, "登录状态"), LOGOUT(200, "登录退出状态"), REMOVE(300, "用户删除状态");

    private Integer code;
    private String msg;

    UserEnum() {
    }

    UserEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

TestUserEnum.Class(单元测试枚举类)

@Test
public void testEnumUser() {
    UserEnum userEnum = UserEnum.LOGIN;
    System.out.println("枚举的索引:" + userEnum.ordinal());
    System.out.println("枚举的名字:" + userEnum.name());

    System.out.println("用户的状态码:" + userEnum.getCode());
    System.out.println("用户的状态码信息:" + userEnum.getMsg());

}

测试全局配置EnumOrdinalTypeHandler

<!--在Mybatis-Config.xml配置文件-->
<typeHandlers>
        <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.Java.Enum.UserEnum"/>
</typeHandlers>

Mybatis-Config.xml

<typeHandlers>
		<!--1、配置我们自定义的TypeHandler  -->
		<typeHandler handler="com.atguigu.mybatis.typehandler.MyEnumEmpStatusTypeHandler" javaType="com.atguigu.mybatis.bean.EmpStatus"/>
		<!--2、也可以在处理某个字段的时候告诉MyBatis用什么类型处理器
				保存:#{empStatus,typeHandler=xxxx}
				查询:
					<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmp">
				 		<id column="id" property="id"/>
				 		<result column="empStatus" property="empStatus" typeHandler=""/>
				 	</resultMap>
				注意:如果在参数位置修改TypeHandler,应该保证保存数据和查询数据用的TypeHandler是一样的。
		  -->
</typeHandlers>

MyEnumEmpStatusTypeHandler.Class(自定义创建MyEnumEmpStatusTypeHandler实现implementsTypeHandler)

package com.Java.handler;

import com.Java.Enum.UserEnum;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class MyEnumEmpStatusTypeHandler implements TypeHandler<UserEnum> {

    @Override
    public void setParameter(PreparedStatement preparedStatement, int i, UserEnum userEnum, JdbcType jdbcType) throws SQLException {
        System.out.println("要保存的状态码:" + userEnum.getCode());
        preparedStatement.setNString(i, userEnum.getCode().toString());
    }

    @Override
    public UserEnum getResult(ResultSet resultSet, String s) throws SQLException {
        int code = resultSet.getInt(s);
        return UserEnum.getUserByEnumCodeReturn(code);
    }

    @Override
    public UserEnum getResult(ResultSet resultSet, int i) throws SQLException {
        int code = resultSet.getInt(i);
        return UserEnum.getUserByEnumCodeReturn(code);
    }

    @Override
    public UserEnum getResult(CallableStatement callableStatement, int i) throws SQLException {
        int code = callableStatement.getInt(i);
        return UserEnum.getUserByEnumCodeReturn(code);
    }
}

向admin表中添加列表示用户状态

alter table admin add column admin_Starts INT;

UserDaoMapper.Class

//保存操作
Long addUser(User user);
//查询操作
List<User> queryAllUser(Integer adminId);

UserDaoMapper.Xml

<!--Long addUser(User user);-->
<insert id="addUser" useGeneratedKeys="true" keyProperty="adminId">
    insert into admin(admin_Name,admin_Password,admin_Starts)
    values (#{adminName},#{adminPassword},#{userStarts,typeHandler=com.Java.handler.MyEnumEmpStatusTypeHandler})
</insert>
<!--查询操作-->
<resultMap id="MyMap" type="com.Java.pojo.User">
        <id column="admin_Id" property="adminId"/>
        <result column="admin_Name" property="adminName"/>
        <result column="admin_Password" property="adminPassword"/>
        <result column="admin_Email" property="adminEmail"/>
        <result column="admin_PropertyId" property="adminPropertyid"/>
        <result column="admin_Gender" property="adminGender"/>
        <result column="admin_Starts" property="userStarts" typeHandler="com.Java.handler.MyEnumEmpStatusTypeHandler"/>
</resultMap>
 <!--  List<User> queryAllUser(Integer adminId);-->
<select id="queryAllUser" resultMap="MyMap">
        select `admin_Name`,`admin_Password`,`admin_Email`,`admin_Starts`
        from `admin` where admin_Id=#{adminId};
</select>

TestMybatis.Class(单元测试)测试保存

@Test
public void getSqlSessionFactoryTest() {
    SqlSession sqlSession = null;
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User(null, "admin123", "admin132", null, null, 0, UserStarts.LOGIN);
        mapper.addUser(user);
        System.out.println(user.getAdminId());
        sqlSession.commit();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}

TestMybatis.Class(单元测试)测试查询

@Test
public void getSqlSessionFactoryTest() {
    SqlSession sqlSession = null;
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.queryAllUser(20018);
        for (User user : users) {
            System.out.println(user);
            System.out.println(user.getUserStarts());
        }
        sqlSession.commit();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}

控制台打印

DEBUG 05-10 09:03:48,104 ==>  Preparing: select `admin_Name`,`admin_Password`,`admin_Email`,`admin_Starts` from `admin` where admin_Id=?;   (BaseJdbcLogger.java:159) 
DEBUG 05-10 09:03:48,136 ==> Parameters: 20018(Integer)  (BaseJdbcLogger.java:159) 
从数据库当中要获取的状态码:200
DEBUG 05-10 09:03:48,154 <==      Total: 1  (BaseJdbcLogger.java:159) 
User{adminId=null, adminName='admin123', adminPassword='admin132', adminEmail='null', adminPropertyid=null, adminGender=null, userStarts=LOGOUT}
LOGOUT

);
//查询操作
List queryAllUser(Integer adminId);


**UserDaoMapper.Xml**

```xml
<!--Long addUser(User user);-->
<insert id="addUser" useGeneratedKeys="true" keyProperty="adminId">
    insert into admin(admin_Name,admin_Password,admin_Starts)
    values (#{adminName},#{adminPassword},#{userStarts,typeHandler=com.Java.handler.MyEnumEmpStatusTypeHandler})
</insert>
<!--查询操作-->
<resultMap id="MyMap" type="com.Java.pojo.User">
        <id column="admin_Id" property="adminId"/>
        <result column="admin_Name" property="adminName"/>
        <result column="admin_Password" property="adminPassword"/>
        <result column="admin_Email" property="adminEmail"/>
        <result column="admin_PropertyId" property="adminPropertyid"/>
        <result column="admin_Gender" property="adminGender"/>
        <result column="admin_Starts" property="userStarts" typeHandler="com.Java.handler.MyEnumEmpStatusTypeHandler"/>
</resultMap>
 <!--  List<User> queryAllUser(Integer adminId);-->
<select id="queryAllUser" resultMap="MyMap">
        select `admin_Name`,`admin_Password`,`admin_Email`,`admin_Starts`
        from `admin` where admin_Id=#{adminId};
</select>

TestMybatis.Class(单元测试)测试保存

@Test
public void getSqlSessionFactoryTest() {
    SqlSession sqlSession = null;
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User(null, "admin123", "admin132", null, null, 0, UserStarts.LOGIN);
        mapper.addUser(user);
        System.out.println(user.getAdminId());
        sqlSession.commit();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}

TestMybatis.Class(单元测试)测试查询

@Test
public void getSqlSessionFactoryTest() {
    SqlSession sqlSession = null;
    try {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.queryAllUser(20018);
        for (User user : users) {
            System.out.println(user);
            System.out.println(user.getUserStarts());
        }
        sqlSession.commit();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}

控制台打印

DEBUG 05-10 09:03:48,104 ==>  Preparing: select `admin_Name`,`admin_Password`,`admin_Email`,`admin_Starts` from `admin` where admin_Id=?;   (BaseJdbcLogger.java:159) 
DEBUG 05-10 09:03:48,136 ==> Parameters: 20018(Integer)  (BaseJdbcLogger.java:159) 
从数据库当中要获取的状态码:200
DEBUG 05-10 09:03:48,154 <==      Total: 1  (BaseJdbcLogger.java:159) 
User{adminId=null, adminName='admin123', adminPassword='admin132', adminEmail='null', adminPropertyid=null, adminGender=null, userStarts=LOGOUT}
LOGOUT

版权声明:本文为weixin_46389877原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。