美文网首页JavaEE
JavaWeb了解之MyBatis框架

JavaWeb了解之MyBatis框架

作者: 平安喜乐698 | 来源:发表于2022-10-27 16:34 被阅读0次
    目录
      前言
        1. MyBatis
        2. 示例(使用MyBatis访问数据库)
        核心配置文件
        映射配置文件
      1. MyBatis注解
      2. 级联查询
      3. 动态SQL(减少代码工作量) 
      4. 分页
      5. 缓存
      6. 存储过程
      7. (根据数据库表)自动生成:POJO类、mapper映射文件、mapper 接口
      8. Maven+Spring+MyBatis
    

    一个轻量级访问数据库的JavaORM开源框架(对JDBC进一步封装),专注于SQL语句(使用xml文件或注解 将SQL和代码分离)。

    前言

    1. MyBatis

    官网(慢)github下载MyBatis框架

    MyBatis目录
    MyBatis目录
      1. lib目录
        MyBatis依赖的jar包
      2. mybatis-3.5.11.jar
        MyBatis核心jar包
      3. mybatis-3.5.11.pdf
        MyBatis使用说明英文文档
    
    序号 MyBatis依赖的jar包 描述
    1 asm-7.1.jar 操作Java字节码的类库
    2 cglib-3.3.0.jar 用来动态继承Java类或实现接口
    3 commons-logging-1.2.jar 用于通用日志处理
    4 javassist-3.29.2-GA.jar 分析、编码和创建Java类库
    5 log4j-api-2.19.0.jar 日志系统
    6 ognl-3.3.4.jar OGNL的类库
    7 reload4j-1.2.22.jar
    8 slf4j-api-2.0.1.jar 日志系统的封装,对外提供统一的API接口
    优点
      1. 简化JDBC开发(几乎所有的JDBC代码)。
      2. 最简单的持久化框架(灵活、易学)。
      3. SQL写在xml文件中,和代码分离,降低了耦合度,便于统一管理,提高了代码的可重用性。
      4. 支持编写动态SQL语句。
      5. 半自动ORM框架(只能自动将表映射到Java对象:读,不能自动将Java对象映射到表:存)
      6. 支持以存储过程的形式封装SQL。将业务逻辑保留在数据库之外,增强应用的可移植性,更易于部署和测试。
    缺点
      1. 编写SQL语句的工作量较大。
      2. SQL语句依赖于数据库(数据库移植性差:不能随意更换数据库)。
    
    MyBatis和Hibernate的区别
      1. Hibernate
        1. 全自动ORM框架。
        2. 使用HQL语句,独立于数据库(数据库移植性高)。不需要编写大量的SQL就可以完成映射,但会多消耗性能,且开发员不能自主的进行SQL性能优化。级联比MyBatis强大。Hibernate的DAO层开发比MyBatis简单,Mybatis使用注解方式时需要维护SQL和结果映射。
        3. 更好的二级缓存(如果出现脏数据,系统会报出错误并提示);可集成第三方缓存。在核心配置文件配置缓存提供者;每个映射配置文件中配置并发策略。
      2. MyBatis
        1. 半自动ORM框架
          使用xml或注解手动配置SQL映射(将Java类属性映射到SQL字符串中对应的表字段)。
          对于查询结果:会自动将字段映射到对应的Java类属性。
        2. 需手动编写SQL(灵活多变),依赖于数据库(数据库移植性差)。支持动态SQL处理列表、动态生成表名、支持存储过程。工作量相对较大。可以进行更为细致的SQL优化,可以减少不必要的SQL语句执行(提高性能)。
        3. 使用二级缓存时需特别小心(如果不能完全确定数据更新操作的波及范围,应避免Cache的盲目使用,否则脏数据的出现会给系统的正常运行带来很大的隐患)。在核心配置文件配置开启二级缓存;每个映射配置文件中配置多个的缓存机制,针对不同的表可使用不同的缓存机制。Mybatis可以在命名空间中共享相同的缓存配置和实例,通过Cache-ref来实现。
    综上所述
      1. Hibernate适合需求明确、业务固定的项目(如:OA项目、ERP项目、CRM项目等)。
      2. MyBatis适合需求多变、性能要求苛刻的互联网项目(如:电商项目、金融类型、旅游类、售票类项目等)。
    

    如果使用的是Maven项目时,只需在pom.xml文件中添加如下依赖内容(不需要手动导入jar包):

      <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.11</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>
      </dependencies>
    
    1. 示例(使用MyBatis访问数据库)
    1. 创建Web应用
    1. 导入依赖包
    
    将mysql-connector-java-xxx.jar、mybatis-xxx.jar、MyBatis的lib目录下的所有依赖jar复制到/WEB-INF/lib目录中。
    
    2. 创建log4j.properties日志文件(在src目录下),内容如下:
    
    # 全局的日志配置
    log4j.rootLogger=ERROR,stdout
    # MyBatis的日志配置(将com.sst.cx包下所有类的日志记录级别设置为DEBUG)
    log4j.logger.com.sst.cx=DEBUG
    # 控制台输出
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
    
    3. 创建表(在数据库中)
    
    create table EMPLOYEE (
       id INT NOT NULL auto_increment,
       first_name VARCHAR(20) default NULL,
       last_name  VARCHAR(20) default NULL,
       salary     INT  default NULL,
       PRIMARY KEY (id)
    );
    
    1. 创建持久化类
    ===》Employee.java(在com.sst.cx.domain包下创建)遵循POJO规范,属性名和表字段名通常需一致。    
      不一致则需进行额外配置
        方式1(在核心配置文件中配置)
          如果属性名为驼峰写法,数据库字段名为下划线写法时可使用该方式。
          <settings>
            <!-- 全局处理属性名和表字段名不一致(驼峰转下划线)  -->
            <setting name="mapUnderscoreToCamelCase" value="true" />
          </settings>
        方式2(在sql中定义别名)
          select first_name firstName from EMPLOYEE
        方式3(通过resultMap元素来映射字段名和类属性名)
          <select id="selectAllEmployee" resultMap="myResult">
            select * from EMPLOYEE
          </select>
          <resultMap type="com.sst.cx.domain.Employee" id="myResult">
            <id property="id" column="id"/>
            <result property="firstName" column="first_name"/>
          </resultMap>
    
    package com.sst.cx.domain;
    public class Employee {  
           private int id;
           private String firstName; 
           private String lastName;   
           private int salary;  
           public int getId() {
              return id;
           }
           public void setId( int id ) {
              this.id = id;
           }
           public String getFirstName() {
              return firstName;
           }
           public void setFirstName( String firstName ) {
              this.firstName = firstName;
           }
           public String getLastName() {
              return lastName;
           }
           public void setLastName( String lastName ) {
              this.lastName = lastName;
           }
           public int getSalary() {
              return salary;
           }
           public void setSalary( int salary ) {
              this.salary = salary;
           }
           // System.out.println(employeeTmp);会输出该方法的返回值。
           public String toString() {
               return "工号为"+id+"的员工:"+firstName+lastName+"的工资为$"+salary;
           }
    }
    
    1. 创建映射配置文件
    ===》EmployeeMapper.xml(在com.sst.cx.mappper包下创建,一般命名为类名+Mapper)
    
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.sst.cx.mapper.EmployeeMapper">
        <!-- 添加一个员工 -->
        <insert id="addEmployee" parameterType="com.sst.cx.domain.Employee">
            insert into EMPLOYEE(first_name,last_name,salary) values(#{firstName},#{lastName},#{salary})
        </insert>
        <!-- 查询所有员工信息 -->
        <select id="selectAllEmployee" resultType="com.sst.cx.domain.Employee">
            select * from EMPLOYEE
        </select>
        <!-- 删除指定员工 -->
        <delete id="deleteEmployee">
            delete from EMPLOYEE where id=#{id}
        </delete>   
        <!-- 更新指定员工 -->
        <update id="updateEmployee">
            update EMPLOYEE set salary=${salary} where id=#{id}
        </update>   
    </mapper>
    
    说明:
      1. mapper元素:映射配置文件的根元素
        namespace属性:用于指定唯一的命名空间(一般为:包名+映射文件名)。
      2. insert元素(对应一个插入语句)、delete元素(对应一个删除语句)、update元素(对应一个更新语句)、select元素(对应一个查询语句)。
        1. id属性:通过namespace属性+id属性就可以找到对应的SQL。
        2. parameterType属性:参数类型(给占位符赋值),可选(Mybatis会自动推断)。
        3. resultType属性:查询结果类型(类的完全限定名),会自动将数据库中查询到的表数据映射到Java对象中。
      3. #{} 表示一个占位符,相当于?。#{xxx}表示使用xxx属性值为占位符赋值。
    
    1. 创建核心配置文件
    ===》mybatis-config.xml(在src目录下创建,一般命名为mybatis-config)
      定义了:数据库连接相关参数(通常会将这些参数放置在单独的properties文件中,再使用properties元素引入。使用${参数名}来引用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>
        <settings>
            <!-- 全局处理属性名和表字段名不一致(驼峰转下划线)  -->
            <setting name="mapUnderscoreToCamelCase" value="true" />
            <setting name="logImpl" value="LOG4J" />
        </settings>
        <!-- 配置mybatis运行环境 -->
        <environments default="development">
            <environment id="development">
                <!-- 使用JDBC的事务管理 -->
                <transactionManager type="JDBC" />
                <dataSource type="POOLED">
                    <!-- MySQL数据库驱动 -->
                    <property name="driver" value="com.mysql.cj.jdbc.Driver" />
                    <!-- 连接数据库的URL -->
                    <property name="url"
                        value="jdbc:mysql://localhost:3306/Test?characterEncoding=utf8" />
                    <property name="username" value="root" />
                    <property name="password" value="12345678" />
                </dataSource>
            </environment>
        </environments>
        <!-- 添加映射文件,一个映射文件对应一个mapper元素 -->
        <mappers>
            <mapper resource="com/sst/cx/mapper/EmployeeMapper.xml" />
        </mappers>
    </configuration>
    
    1. 创建测试文件
    ===》Test.java(在com.sst.cx.test包下创建)
    
    package com.sst.cx.test;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.List;
    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 com.sst.cx.domain.Employee;
    public class Test {
        public static void main(String[] args) throws IOException {
    /*
    1. SqlSessionFactoryBuilder对象
      负责读取核心配置文件内容并保存到创建的SqlSessionFactory对象中(后就会被销毁,适合作为局部变量)。
      提供了多个build方法重载:
        build(InputStream);
        build(InputStream,String);
        build(InputStream,String,Properties);
        build(InputStream,Properties);
        build(Reader);
        build(Reader,String);
        build(Reader,String,Properties);
        build(Reader,Properties);
        build(Configuration);
      通过源码可知,以上build方法都在调用build(Reader reader, String environment, Properties properties)方法。
      可以发现配置信息可以通过3种方式传入:InputStream(字节流)、Reader(字符流)、Configuration(类)。字节流和字符流都属于读取xml配置文件的方式,又可分为2种方式:读取xml配置文件(常用)和编写代码。
    */
    /*
    2. SqlSessionFactory对象(对应一个数据库)
      由SqlSessionFactoryBuilder配置对象创建。每次访问数据库时,负责创建SqlSession对象。
      1. 程序运行期间只需创建一次(对应application作用域)。
      看一下SqlSessionFactory接口的定义:
        public interface SqlSessionFactory {
            SqlSession openSession();
            SqlSession openSession(boolean autoCommit);
            SqlSession openSession(Connection connection);
            SqlSession openSession(TransactionIsolationLevel level);
            SqlSession openSession(ExecutorType execType);
            SqlSession openSession(ExecutorType execType, boolean autoCommit);
            SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
            SqlSession openSession(ExecutorType execType, Connection connection);
            Configuration getConfiguration();
        }
    */
            // Reader configReader = Resources.getResourceAsReader(mybatis-config.xml"); 
            // SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(configReader);
            // 读取核心配置文件并创建SqlSessionFactory对象
            InputStream configInputStream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(configInputStream);
    /*
    3. SqlSession对象(对应一次数据库连接)
      用于执行映射文件中的SQL来持久化类(增删改查)。 类似于JDBC的Connection、Hibernate的Session。
      1. 非线程安全(应该避免多个线程共享同一个SqlSession实例,使用完后应及时关闭)。
      2. 每次访问数据库都需重新创建一个SqlSession对象(对应request作用域)。
      3. 执行SQL的2种方式:
        方式1. 创建映射器,让映射器通过命名空间和方法名来执行对应的SQL。
        方式2. 直接通过命名空间+id来执行对应的SQL。
    常用方法
      void clearCache();
      Configuration getConfiguration();
      void rollback(boolean force);
      void commit(boolean force);
      int delete(String statement, Object parameter);
    */
            // 创建SqlSession对象(对应一次数据库访问)
            SqlSession sqlSession = ssf.openSession();
            // 使用SqlSession对象执行映射文件中定义的SQL,并返回映射结果
            // 添加员工
            Employee employee = new Employee();
            employee.setFirstName("张");
            employee.setLastName("三");
            employee.setSalary(100000);
            sqlSession.insert("com.sst.cx.mapper.Employee.addEmployee", employee);
            // 查询所有员工
            List<Employee> employeeList = sqlSession.selectList("com.sst.cx.mapper.Employee.selectAllEmployee");
            for (Employee employeeTmp : employeeList) {
                System.out.println(employeeTmp);
                // 删:sqlSession.delete("com.sst.cx.mapper.EmployeeMapper.deleteEmployee", employeeTmp);
                // 改:sqlSession.update("com.sst.cx.mapper.EmployeeMapper.deleteEmployee", 修改后的employeeTmp);
            }
            // 提交事务
            sqlSession.commit();
            // 关闭SqlSession
            sqlSession.close();
        }
    }
    
    运行结果

    核心配置文件

    <?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 /><!-- 引入外部文件;设置name-value(可在其他地方引用) -->
        <settings /><!-- 配置MyBatis的运行时行为 -->
        <typeAliases /><!-- 取别名(替代类的完全限定名)可在映射文件中引用别名 -->
        <typeHandlers /><!-- 类型处理器(负责类型转换) -->
        <objectFactory /><!-- 对象工厂 -->
        <plugins /><!-- 插件 -->
        <environments><!-- 用于配置多套运行环境(开发、生产、测试) -->
            <environment><!-- 对应一套运行环境 -->
                <transactionManager /><!-- 指定事务管理器 -->
                <dataSource /><!-- 配置数据库连接参数 -->
            </environment>
        </environments>
        <databaseIdProvider /><!-- 数据库厂商标识 -->
        <mappers /><!-- 映射器(所有映射文件的路径) -->
    </configuration>
    
    1. properties元素(定义的内容可以在其他元素中使用${name}来引用)
      1. 引入外部properties文件(可以将数据库连接参数放置在database.properties文件中,然后引入该文件)。
        <properties resource="database.properties"/>
      2. 在property子元素中设置name-value。
        <properties>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </properties>
    
    2. settings元素
      用于配置MyBatis的运行时行为,大多数使用默认值即可。
      全配置的样例:
        <settings>
            <setting name="cacheEnabled" value="true"/>
            <setting name="lazyLoadingEnabled" value="true"/>
            <setting name="multipleResultSetsEnabled" value="true"/>
            <setting name="useColumnLabel" value="true"/>
            <setting name="useGeneratedKeys" value="false"/>
            <setting name="autoMappingBehavior" value="PARTIAL"/>
            <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
            <setting name="defaultExecutorType" value="SIMPLE"/>
            <setting name="defaultStatementTimeout" value="25"/>
            <setting name="defaultFetchSize" value="100"/>
            <setting name="safeRowBoundsEnabled" value="false"/>
            <setting name="mapUnderscoreToCamelCase" value="false"/>
            <setting name="localCacheScope" value="SESSION"/>
            <setting name="jdbcTypeForNull" value="OTHER"/>
            <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
        </settings>  
    
    3. typeAliases元素
      定义别名(在映射文件中不需再使用类的全限定名)
      方式1:
        <typeAliases>
          <typeAlias alias = "Employee" type = "com.sst.cx.domain.Employee"/>
        </typeAliases>
      方式2:(对包路径下的所有类定义别名:去除包路径后的类名):
        <typeAliases>
          <package name = "com.sst.cx.domain"/>
        </typeAliases>
    
    4. typeHandlers元素(有多个typeHandler子元素)
      对数据库类型和Java类型进行转换。
        1. jdbcType属性:指定数据库类型。
        2. javaType属性:指定对应的Java类型。
      对于自定义类型,需要实现TypeHandler接口或继承BaseTypeHandle类。
    
    5. environments元素
      用于配置多套运行环境(一套环境对应一个environment子元素)。开发-生产环境分别使用不同的数据库。
      default属性:默认使用哪一套环境,对应environment元素的运行环境id。
      有2个子元素:transactionManager元素、dataSource元素。
      例:
        <environments default="development">
          <environment id="development">
          </environment>
        </environments>
    
    transactionManager元素
      指定事务管理器(2种)
        1. JDBC
          应用服务器负责事务管理操作(如:提交、回滚)。
        2. MANAGED
          应用服务器负责管理连接生命周期。
      例:
         <!-- 使用JDBC的事务管理 -->
        <transactionManager type="JDBC" />
    
    dataSource元素
      用于配置数据库连接相关参数
      type属性:指定数据源的类型(3种)。
        1. UNPOOLED 
          没有数据库连接池(效率低)
        2. POOLED
          MyBatis维护一个数据库连接池。对于每个数据库的操作,MyBatis都会使用连接池中的连接,并在操作完成后将它们返回到池中。(减少了创建新连接所需的初始连接和身份验证时间)。
        3. JNDI
          MyBatis将从JNDI 数据源中获取连接。
      例:
        <dataSource type="POOLED">
          <!-- JDBC驱动程序类(数据库驱动) -->
          <property name="driver" value="com.mysql.cj.jdbc.Driver" />
          <!-- 要连接的数据库URL -->
          <property name="url" value="jdbc:mysql://localhost:3306/Test?characterEncoding=utf8" />
          <!-- 连接数据库使用的用户名 -->
          <property name="username" value="root" />
          <!-- 连接数据库使用的密码 -->
          <property name="password" value="12345678" />
        </dataSource>
    
    6. mappers元素(有多个mapper子元素)
      用于指定所有映射配置文件的路径。
      例:
        <!-- 添加映射文件,一个映射文件对应一个mapper元素 -->
        <mappers>
            <mapper resource="com/sst/cx/mapper/EmployeeMapper.xml" />
        </mappers>
    
    setting配置项 作用 配置选项 默认值
    cacheEnabled 是否开启二级缓存 true/false true
    lazyLoadingEnabled 是否开启延迟加载。开启后所有关联对象都会延迟加载。可在特定关联关系中设置fetchType属性进行覆盖 true/false false
    aggressiveLazyLoading 当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载 true/false 版本3.4.1 (不包含)之前的默认值为true,之后为false。
    multipleResultSetsEnabled 是否允许单一语句返回多结果集(需要兼容驱动) true/false true
    useColumnLabel 使用列标签代替列名。不同的驱动会有不同的表现 true/false true
    useGeneratedKeys 是否自动生成主键(需要驱动兼容。尽管一些驱动不能兼容但仍可正常工作,如:Derby) true/false false
    autoMappingBehavior 指定如何自动映射列字段到属性。NONE表示取消自动映射;PARTIAL表示自动映射(不包括结果集);FULL表示自动映射(包括结果集,无论是否嵌套) NONE、PARTIAL、FULL PARTIAL
    autoMappingUnkno wnColumnBehavior 指定自动映射当中未知列(或未知属性类型)时的行为。 默认为NONE:不处理;WARNING:只有当日志级别达到 WARN级别或者以下才会显示相关日志;FAILING:如果处理失败会抛出SqlSessionException异常 NONE、WARNING、FAILING NONE
    defaultExecutorType 配置默认的执行器。SIMPLE:普通的执行器;REUSE:会重用预处理语句;BATCH:执行器将重用语句并执行批量更新 SIMPLE、REUSE、BATCH SIMPLE
    defaultStatementTimeout 设置超时时间(决定驱动等待数据库响应的秒数) 正整数 Not Set (null)
    defaultFetchSize 设置数据库驱动程序默认返回的条数限制 正整数 Not Set (null)
    safeRowBoundsEnabled 允许在嵌套语句中使用分页(RowBounds),如果允许,设置false true/false false
    safeResultHandlerEnabled 允许在嵌套语句中使用分页(ResultHandler)。如果允许,设置false true/false true
    mapUnderscoreToCamelCase 是否开启自动驼峰命名规则映射 true/false false
    localCacheScope MyBatis利用本地缓存机制防止循环引用和加速联复嵌套査询。默认值为SESSION(缓存一个会话中执行的所有查询)。若设置值为STATEMENT,本地会话仅用在语句执行上,对相同SqlScssion的不同调用将不会共享数据 SESSION/STATEMENT SESSION
    jdbcTypeForNull 当没有为参数提供特定的JDBC类型时,为空值指定 JDBC 类型。某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER NULL、VARCHAR、OTHER OTHER
    lazyLoadTriggerMethods 指定哪个对象的方法触发一次延迟加载 equals、clone、hashCode、toString
    defaultScriptingLanguage 指定动态 SQL 生成的默认语言 org.apache.ibatis.script.ing.xmltags.XMLDynamicLanguageDriver
    callSettersOnNulls 指定当结果集中值为 null 时,是否调用映射对象的 setter(map 对象时为 put)方法,这对于 Map.kcySet() 依赖或 null 值初始化时是有用的。注意,基本类型(int、boolean 等)不能设置成 null true/false false
    logPrefix 指定 MyBatis 增加到日志名称的前缀 任何字符串 Not set
    loglmpl 指定 MyBatis 所用日志的具体实现,未指定时将自动査找 SLF4J/LOG4J/LOG4J2/JDK_LOGGING/COMMONS_LOGGING/ST DOUT_LOGGING/NO_LOGGING Not set
    proxyFactory 指定MyBatis创建具有延迟加栽能力的对象所用到的代理工具 CGLIB/JAVASSIST JAVASSIST(MyBatis3.3及以上)
    vfsImpl 指定VFS的实现类 提供VFS类的全限定名(多个时以逗号分隔) Not set
    useActualParamName 允许用方法参数中声明的实际名称引用参数。要使用此功能,项目必须被编译为 Java 8 参数的选择。(从版本 3.4.1 开始可以使用) true/false true

    映射配置文件

    映射器
      用于:配置缓存、定义SQL/动态SQL语句、定义参数类型、定义查询结果/映射。
      由以下2部分组成:
        1. Java接口(可省略)
          一个方法对应xml文件中的一个SQL语句。
        2. xml映射配置文件 或 注解
          文件中包含一组SQL语句(增删改查),这些语句称为映射语句或映射SQL语句。
          如果同时使用xml方式和注解方式,xml会覆盖注解。
          方式1. xml文件(常用)
            SQL语句比较复杂(表关联、多个查询条件、级联)或存在动态SQL时,此方式相比注解方式:可读性和可维护性高,避免了重复编写SQL语句。
            mapper元素的namespace属性用来定义命名空间,需要和接口的完全限定名一致。
          方式2. 注解
            在Java接口中添加注解(注入SQL)
    
    例1(xml文件方式):
    
    ===》1. Java接口类(可省略)
    package com.sst.cx.mapper;
    import java.util.List;
    import com.sst.cx.domain.Employee;
    public interface EmployeeMapper {
        // 添加一个员工
        public void addEmployee(Employee employee);
        // 查询所有员工信息
        public List<Employee> selectAllEmployee();
    }
    ===》2. 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.sst.cx.mapper.EmployeeMapper">
        <!-- 添加一个员工 -->
        <insert id="addEmployee" parameterType="com.sst.cx.domain.Employee">
            insert into EMPLOYEE(first_name,last_name,salary) values(#{firstName},#{lastName},#{salary})
        </insert>
        <!-- 查询所有员工信息 -->
        <select id="selectAllEmployee" resultType="com.sst.cx.domain.Employee">
            select * from EMPLOYEE
        </select>
    </mapper>
    ===》3. 在核心配置文件中添加该映射文件
    <mappers>
      <!-- MyBatis会读取该映射文件并 生成映射器 -->
      <mapper resource="com/sst/cx/mapper/EmployeeMapper.xml" />
    </mappers>
    ===》4. 测试
    执行SQL(2种方式)
    /*
    方式1(通过SqlSession对象执行SQL)
      1. selectOne
        只能查询1条数据,查询到多条数据时会导致运行时错误。必须指定查询条件。
        sqlSession.selectOne(String arg0, Object arg1)  
      2. selectList
        sqlSession.selectList(String arg0)
        sqlSession.selectList(String arg0, Object arg1)
      第一个参数:由 命名空间+SQL id 组成,唯一对应一个SQL语句。
      第二个参数:查询条件。
    sqlSession.insert("com.sst.cx.mapper.Employee.addEmployee", employee);
    List<Employee> employeeList = sqlSession.selectList("com.sst.cx.mapper.Employee.selectAllEmployee");
    */
    // 方式2(通过SqlSession对象获取Mapper接口并调用其方法执行SQL)
    // 推荐(可读性高;更符合面向对象思想,体现了业务逻辑;IDE会提供示错误提示和校验),这时Java接口不能省略(必须创建)。
    EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
    employeeMapper.addEmployee(employee);
    List<Employee> employeeList = employeeMapper.selectAllEmployee();
    for (Employee employeeTmp : employeeList) {
      System.out.println(employeeTmp);
    }
    
    例2(注解方式):
    
    ===》1. Java接口类
    package com.sst.cx.mapper;
    import java.util.List;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Insert;
    import org.apache.ibatis.annotations.Select;
    import com.sst.cx.domain.Employee;
    @Mapper
    public interface EmployeeMapper {
        // 添加一个员工
        @Insert(value = "insert into EMPLOYEE(first_name,last_name,salary) values(#{firstName},#{lastName},#{salary})")
        public void addEmployee(Employee employee);
        // 查询所有员工信息
        @Select(value = "select * from EMPLOYEE")
        public List<Employee> selectAllEmployee();
    }
    // 也可以在核心配置文件中添加<mapper class="com.sst.cx.mapper.EmployeeMapper"/>
    // 也可以在Configuration对象上注册该接口configuration.addMapper(EmployeeMapper.class);
    在核心配置文件的mappers元素中添加<package name="com.sst.cx.mapper"/>对包路径下所有Mapper进行统一配置。
    ===》2. 测试(测试代码同上)
    
    映射配置文件的元素 描述
    mapper元素 映射文件的根节点。有一个namescape属性(1. 定义命名空间,用于区分不同的mapper,全局唯一;2. 当绑定DAO接口时,可以不用创建该接口的实现类,MyBatis会通过接口的完整限定名查找到对应的mapper配置来执行SQL语句)。
    select元素 对应一个查询语句(可以自定义参数,返回结果集)
    insert元素 对应一个插入语句(执行后返回一个整数,代表插入的条数)
    update元素 对应一个更新语句(执行后返回一个整数,代表更新的条数)
    delete元素 对应一个删除语句(执行后返回一个整数,代表删除的条数)
    parameterMap元素 定义参数映射关系(即将被删除的元素,不再建议使用)
    sql元素 允许先定义一部分SQL(然后在多个SQL语句中使用)。
    resultMap元素 用来描述数据库结果集与对象的对应关系(最复杂、最强大的元素,提供映射规则)
    cache元素 配置指定命名空间的缓存
    cache-ref元素 引用其它命名空间缓存配置
    1. select元素(最常用、最强大)
      对应一个查询语句。
      1. id属性:通过mapper元素的namespace属性+select元素的id属性就可以找到唯一对应的SQL。
      2. resultType属性:指定结果集映射的Java类型。
      3. parameterType属性:指定查询参数(int、float、String、JavaBean、Map等)。
    例
      定义一个id为selectAllEmployee的查询语句(参数类型为string,返回结果类型为Employee)
        <select id="selectAllEmployee" resultType="com.sst.cx.domain.Employee" parameterType="string">
          select * from EMPLOYEE WHERE first_name=#{firstName}
        </select>
      MyBatis会自动将以上配置转为以下SQL代码
        String sql = "select * from EMPLOYEE WHERE first_name=?";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1,firstName);
    
    传递多个参数
      方式1. 使用Map传递参数(可读性差、不利于后期维护)不推荐
        1. 映射文件
          <select id="selectEmployeeByMap" resultType="com.sst.cx.domain.Employee" parameterType="map">
            select * from EMPLOYEE WHERE first_name=#{firstName} AND last_name=#{lastName}
          </select>
        2. Java接口
          public List<Employee> selectEmployeeByMap(Map<String, String> params);
        3. 测试
          Map<String,String> paramsMap = new HashMap<String,String>();
          paramsMap.put("firstName","张");
          paramsMap.put("lastName","三");
          employeeMapper.selectEmployeeByMap(paramsMap);
      方式2. 使用注解传递参数(当参数个数≤5时,更直观)
        1. 映射文件
          <select id="selectEmployeeByAn" resultType="com.sst.cx.domain.Employee">
            select * from EMPLOYEE WHERE first_name=#{firstName} AND last_name=#{lastName}
          </select>
        2. Java接口
          public List<Employee> selectEmployeeByAn(@Param("firstName") String name, @Param("lastName") String url);
      方式3. 使用JavaBean传递参数(当参数个数>5时,建议该方式)
        1. 映射文件
          <select id="selectEmployeeByBean" resultType="com.sst.cx.domain.Employee">
            select * from EMPLOYEE WHERE first_name=#{firstName} AND last_name=#{lastName}
          </select>
        2. Java接口
          public List<Employee> selectEmployeeByBean(Employee employee);
    
    select元素的常用属性 描述
    id 通过mapper元素的namespace属性+select元素的id属性就可以找到唯一对应的SQL(不唯一会抛异常)。
    parameterType 指定传入SQL语句的参数类型的完全限定名或别名。是一个可选属性,MyBatis能自动推断出参数类型。支持基本数据类型(int、float、String等)和等复杂数据类型(JavaBean、Map)。
    resultType SQL语句执行后返回的结果类型(全限定名或者别名)。如果是集合类型,则返回的是集合元素的类型,可以使用resultType或resultMap。
    resultMap 映射集的引用,与<resultMap>元素(MyBatis最复杂的元素,可以配置映射规则、级联、typeHandler 等)一起使用,可以使用 resultType或resultMap。
    flushCache SQL语句执行后是否清空之前查询的本地缓存和二级缓存,默认值为false。
    useCache 是否开启二级缓存,默认值为true(会将査询结果存入二级缓存中)。
    timeout 设置执行该操作的超时时间(以s单位),超时将抛出异常。
    fetchSize 获取记录的总条数设定,默认值是数据库厂商提供的JDBC驱动所设置的条数。
    statementType 执行SQL时使用的Statement类型,取值为STATEMENT(Statement)、 PREPARED(PreparedStatement,默认)、CALLABLE(CallableStatement) 。
    resultSetType 针对JDBC的ResultSet接口而言,取值为FORWARD_ONLY(只允许向前访问)、SCROLL_SENSITIVE(双向滚动,但不及时更新)、SCROLLJNSENSITIVE(双向滚动,及时更新)。
    2. insert元素
      对应一个插入语句。
    例
      定义一个id为addEmployee的插入语句(参数类型为Employee)
        <insert id="addEmployee" parameterType="com.sst.cx.domain.Employee">
            insert into EMPLOYEE(first_name,last_name,salary) values(#{firstName},#{lastName},#{salary})
        </insert>
    
    自定义主键(若数据库不支持主键自动递增,如Oracle。或 取消了主键自动递增的规则)
        <insert id="addEmployee" parameterType="com.sst.cx.domain.Employee">
            <!-- 
              先使用selectKey标签定义主键,然后再定义SQL语句 
              1. keyProperty属性:指定对应类的id属性。
              2. order属性:取值为BEFORE或AFTER。BEFORE表示先执行 <selectKey> 标签内的语句,再执行插入语句。
            -->
            <selectKey keyProperty="id" resultType="Integer" order="BEFORE">
              select if(max(id) is null,1,max(id)+1) as newId from EMPLOYEE
            </selectKey>
            insert into EMPLOYEE(id,first_name,last_name,salary) values(${id},#{firstName},#{lastName},#{salary})
        </insert>
    
    insert元素的常用属性 描述
    id 同select元素
    parameterType 同select元素
    flushCache 同select元素
    timeout 同select元素
    keyProperty 将插入操作生成的主键值赋给Java类的某个属性(通常为主键对应的属性)。如果是联合主键,可以将多个值用逗号隔开。
    useGeneratedKeys 是否使用JDBC提供的getGenereatedKeys()方法获取数据库内部产生的自增主键(MySQL、SQL Server)并赋值到keyProperty属性对应的对象属性中,默认值为false。和keyProperty属性组合使用可实现主键回填。
    databaseId 取值范围oracle、mysql等数据库厂商。元素内部可通过 <if test="_databaseId = 'oracle'"> 来为不同的数据库指定不同的sql语句。 MyBatis会加载不带databaseId属性或带有匹配当前数据库databaseId属性(优先级高)的所有语句。
    keyColumn 设置第几列是主键,当主键列不是表中的第1列时需要设置。如果是联合主键,可以将多个值用逗号隔开。
    3. update元素
      对应一个更新语句。
    例
      定义一个id为updateEmployee的更新语句
        <update id="updateEmployee">
            update EMPLOYEE set salary = ${salary} WHERE first_name=#{firstName} AND last_name=#{lastName}
        </update>
    
    update元素的常用属性 描述
    id 同select元素
    parameterType 同select元素
    flushCache 同select元素
    timeout 同select元素
    statementType 同select元素。
    4. delete元素
      对应一个删除语句。
    例
      定义一个id为deleteEmployee的删除语句
        <delete id="deleteEmployee">
            delete from EMPLOYEE WHERE first_name=#{firstName} AND last_name=#{lastName}
        </delete>
    
    delete元素的常用属性 描述
    id 同select元素
    parameterType 同select元素
    flushCache 同select元素
    timeout 同select元素
    statementType 同select元素。
    5. resultMap元素(MyBatis中最复杂的元素)
      将查询结果映射成实体对象。主要用于解决类属性名与表字段名不一致的情况。
      现有的MyBatis版本只支持resultMap查询,不支持更新、保存,级联更新、删除和修改。
      由以下元素构成:
        <!--
          id属性:resultMap的唯一标识,在select元素的resultMap属性中引用;
          type属性:结果集对应的类的完全限定名 
        -->
        <resultMap id="" type="">
            <!-- 
            当一个 POJO 没有无参数构造方法时使用,类在实例化时用来注入结果到构造方法。
            -->
            <constructor>
                <idArg/><!-- ID参数,结果为ID -->
                <arg/><!-- 注入到构造方法的一个普通结果 --> 
            </constructor>
            <!-- 
              注入主键到JavaBean的id属性(允许多个主键,多个主键称为联合主键)
            -->
            <id/>
            <!-- 
              注入普通字段到JavaBean的普通属性
              id和result元素的属性:
                1. property属性:类属性名
                2. column属性:表字段名
                3. javaType属性:Java类型(完全限定名 或 别名)
                4. jdbcType属性:数据库类型
                5. typeHandler属性:指定类型处理器(覆盖MyBatis默认的处理器,需要指定jdbcType和javaType相互转化的规则)。
            -->
            <result/>
            <!-- 级联 -->
            <association property=""/><!-- 用于一对一关联 -->
            <collection property=""/><!-- 用于一对多、多对多关联 -->
            <discriminator javaType=""><!-- 使用结果值来决定使用哪个结果映射 -->
                <case value=""/><!-- 基于某些值的结果映射 -->
            </discriminator>
        </resultMap>
    
    例
      <resultMap type="com.sst.cx.Employee" id="myResult">
        <!-- 
          property属性:类属性名
          column属性:表字段名
        -->
        <id property="id" column="id"/>
        <result property="firstName" column="first_name"/>
      </resultMap>
    
    resultType和resultMap的区别
      MyBatis的每一个查询映射的返回类型都是resultMap。resultMap和resultType不能同时使用。
      当指定返回类型为resultType时,MyBatis会自动完成映射(将查询的字段映射到对应的Java对象属性中)。
      当指定返回类型为resultMap时(当属性名和字段名不一致时使用,需要创建resultMap元素将字段映射到属性),MyBatis会自动完成映射。
    

    1. MyBatis注解

    1. SQL语句映射相关
        1. @Insert注解:修饰插入语句。
          例:
            @Insert(value = "insert into EMPLOYEE(first_name,last_name,salary) values(#{firstName},#{lastName},#{salary})")
            public void addEmployee(Employee employee);
        2. @Select注解:修饰查询语句。
          例:
            @Select("Select * from user")
            @Results({
                @Result(id = true, column = "id", property = "id"),
                @Result(column = "name", property = "name"),
                @Result(column = "sex", property = "sex"),
                @Result(column = "age", property = "age")
            })
            List<User> queryAllUser();
        3. @SelectKey注解:执行插入语句后,获取id的值。
          statement属性:获取id的SQL语句。
          keyProperty属性:可选,将查询结果赋值给哪个类属性。
          keyColumn属性:可选,将查询结果赋值给哪个表字段。
          resultType属性:指定SQL语句的返回值的类型。
          before属性:是否在执行插入语句之前,默认值为true。
          例:
            @Insert("insert into user(id,name) values(#{id},#{name})")
            @SelectKey(statement = "select last_insert_id()", keyProperty = "id", keyColumn = "id", resultType = int,before = false)
            public int insert(User user);
        4. @Update注解:修饰更新语句
          例:
            @Update("update user set name= #{name},sex = #{sex},age =#{age} where id = #{id}")
            void updateUserById(User user);
        5. @Delete注解:修饰删除语句
          例:
            @Delete("delete from  user  where id =#{id}")
            void deleteById(Integer id);
        6. @Param注解:修饰方法参数
          value属性:指定别名
          例:
            int saveUser(@Param(value="user") User user,@Param("name") String name,@Param("age") Int age);
    
    2. 结果集映射相关
        @Result、@Results、@ResultMap
        id属性:当前结果集的唯一标识。
        value属性:可选。映射字段到Java类属性。
        @Result注解:代表一个字段的映射关系(property指定属性名;column指定字段名;jdbcType:字段类型;id为true表示为主键)。
        例:
        @Select({"select id, name, class_id from student"})
        @Results(id="studentMap", value={
            @Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
            @Result(column="name", property="name", jdbcType=JdbcType.VARCHAR),
            @Result(column="class_id ", property="classId", jdbcType=JdbcType.INTEGER)
        })
        List<Student> selectAll();
    
    3. 关系映射相关
        1. @one注解:用于一对一关系映射
          例:
          @Select("select * from student") 
          @Results({ 
            @Result(id=true,property="id",column="id"), 
            @Result(property="name",column="name"), 
            @Result(property="age",column="age"), 
            @Result(property="address",column="address_id",one=@One(select="com.sst.cx.mapper.AddressMapper.getAddress")) 
          }) 
          public List<Student> getAllStudents();  
        2. @many:用于一对多关系映射
          例:
          @Select("select * from t_class where id=#{id}") 
          @Results({ 
            @Result(id=true,column="id",property="id"), 
            @Result(column="class_name",property="className"), 
            @Result(property="students", column="id", many=@Many(select="com.sst.cx.mapper.StudentMapper.getStudentsByClassId")) 
          }) 
          public Class getClass(int id); 
    

    2. 级联查询

    获取关联数据时十分便捷,但级联过多(超过3层时不再使用)会增加系统的复杂度和性能。
    
    3种关联关系:
      1. 一对一(如:学生和学号)
        通过resultMap元素的association子元素处理一对一级联关系。
        1. property属性:对象属性的名称。
        2. javaType属性:对象属性的类型(完全限定名)。
        3. column属性:表字段名(对应的外键)。
        4. select属性:指定嵌套查询的子SQL。
        例:
          <association property="studentCard" column="card_id" javaType="com.sst.cx.domain.StudentCard" select="com.sst.cx.mapper.StudentCardMapper.selectStudentCardById" />
      2. 一对多(如:班级和学生)
        通过resultMap元素的collection子元素处理一对多级联关系(会将关联查询的多条记录映射到一个list集合属性中)。
        例:
          <collection property="studentList" ofType="com.sst.cx.domain.Student" column="id" select="com.sst.cx.mapper.StudentMapper.selectStudentById" />
      3. 多对多(如:学生和课程)
        MyBatis没有实现多对多级联,推荐通过两个一对多级联替换多对多级联,以降低关系的复杂度,简化程序。
    

    示例(一对一)

    ===》实体类
    public class Student {
        private int id;
        private String name;
        private int sex;
        private StudentCard studentCard;
        /* 省略setter和getter方法 */
    }
    public class StudentCard {
        private int id;
        private int studentId;
        /* 省略setter和getter方法 */
    }
    
    ===》xml映射文件
    方式1. 单步查询(通过关联查询实现)
    <resultMap type="com.sst.cx.domian.Student" id="cardAndStudent">
      <id property="id" column="id" />
      <result property="name" column="name" />
      <result property="sex" column="sex" />
      <!-- 一对一级联查询 -->
      <association property="studentCard" javaType="com.sst.cx.domian.StudentCard">
        <id property="id" column="id" />
        <result property="studentId" column="student_id" />
      </association>
    </resultMap>
    <select id="selectStudentById" parameterType="Integer" resultMap="cardAndStudent">
      SELECT * FROM student s,studentCard sc WHERE s.card_id = sc.id AND s.id=#{id}
    </select>
    
    方式2. 分步查询(通过多次查询实现)
    1. StudentCardMapper.xml:
    <mapper namespace="com.sst.cx.mapper.StudentCardMapper">
      <select id="selectStudentCardById" resultType="com.sst.cx.domian.StudentCard">
          SELECT * FROM studentCard WHERE id = #{id}
      </select>
    </mapper>
    2. StudentMapper.xml:
    <mapper namespace="com.sst.cx.mapper.StudentMapper">
      <!-- 嵌套查询(会执行两个SQL语句) -->
      <resultMap type="com.sst.cx.domian.Student" id="cardAndStudent">
          <id property="id" column="id" />
          <result property="name" column="name" />
          <result property="sex" column="sex" />
          <!-- 一对一级联查询 -->
          <association property="studentCard" column="card_id" javaType="com.sst.cx.domian.StudentCard" select="com.sst.cx.mapper.StudentCardMapper.selectStudentCardById" />
      </resultMap>
      <select id="selectStudentById" parameterType="Integer" resultMap="cardAndStudent">
          select * from student where id=#{id}
      </select>
    </mapper>
    
    ===》映射接口(StudentMapper.java)
    public Student selectStudentById(int id);
    

    示例(一对多)

    ===》实体类
    public class User {
        private int id;
        private String name;
        private String pwd;
        private List<Order> orderList;
        /*省略setter和getter方法*/
    }
    public class Order {
        private int userId;
        private int orderNum;
        /*省略setter和getter方法*/
    }
    
    ===》xml映射文件
    方式1. 单步查询(通过关联查询实现)
    <resultMap type="com.sst.cx.domian.User" id="userAndOrder">
      <id property="id" column="id" />
      <result property="name" column="name" />
      <result property="pwd" column="pwd" />
      <!-- ofType属性:表示集合中的元素类型 -->
      <collection property="orderList" ofType="com.sst.cx.domian.Order">
          <id property="orderId" column="order_id" />
          <result property="orderNum" column="order_num" />
      </collection>
    </resultMap>
    <select id="selectUserOrderById" parameterType="Integer" resultMap="userAndOrder">
      SELECT * FROM user u,order o WHERE u.id=o.user_id AND u.id=#{id}
    </select>
    
    方式2. 分步查询(通过多次查询实现)
    1. OrderMapper.xml:
    <!-- 根据id查询订单信息 -->
    <mapper namespace="com.sst.cx.mapper.OrderMapper">
      <select id="selectOrderById" resultType="com.sst.cx.domian.Order" parameterType="Integer">
        SELECT * FROM order where user_id=#{userId}
      </select>
    </mapper>
    2. UserMapper.xml:
    <mapper namespace="com.sst.cx.mapper.UserMapper">
      <resultMap type="com.sst.cx.domian.User" id="userAndOrder">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <result property="pwd" column="pwd" />
        <!-- ofType属性:表示集合中的元素类型,column属性:将id传递给selectOrderById -->
        <collection property="orderList" ofType="com.sst.cx.domian.Order" column="id" select="com.sst.cx.mapper.OrderMapper.selectOrderById" />
      </resultMap>
      <select id="selectUserOrderById" parameterType="Integer" resultMap="userAndOrder">
        select * from user where id=#{id}
      </select>
    </mapper>
    

    示例(多对多)

    ===》实体类
    public class Order {
        private int oid;
        private int ordernum;
        private List<Product> products;
        /*省略setter和getter方法*/
    }
    public class Product {
        private int pid;
        private String name;
        private Double price;
        private List<Order> orders;
        /*省略setter和getter方法*/
    }
    
    ===》xml映射文件
    OrderMapper.xml:
    <mapper namespace="com.sst.cx.mapper.OrderMapper">
        <resultMap type="com.sst.cx.domian.Order" id="orderMap">
            <id property="oid" column="oid" />
            <result property="ordernum" column="ordernum" />
            <collection property="products" ofType="com.sst.cx.domian.Product">
                <id property="pid" column="pid" />
                <result property="name" column="name" />
                <result property="price" column="price" />
            </collection>
        </resultMap>
        <select id="selectAllOrdersAndProducts" parameterType="Integer" resultMap="orderMap">
            SELECT o.oid,o.ordernum,p.pid,p.name,p.price FROM
            order o
            INNER JOIN orders_detail od ON o.oid=od.orderId
            INNER JOIN
            product p
            ON p.pid = od.productId
        </select>
    </mapper>
    

    3. 动态SQL(减少代码工作量)

    1. if元素(没有else功能)
      判断语句(常用于:当条件为true时拼接where语句)
        <if test="判断条件">
            SQL语句
        </if>
    
    例:
        <select id="selectAllWebsite" resultMap="myResult">
            select id,name,url from website where 1=1
            <if test="name != null">
                AND name like #{name}
            </if>
            <if test="url!= null">
                AND url like #{url}
            </if>
        </select>
    
    2. choose(when、otherwise)元素
      相当于switch-case语句  
        <choose>
            <when test="判断条件1">
                SQL语句1
            </when >
            <when test="判断条件2">
                SQL语句2
            </when >
            <when test="判断条件3">
                SQL语句3
            </when >
            <otherwise>
                SQL语句4
            </otherwise>
        </choose>
    
    例:
        <mapper namespace="com.sst.cx.mapper.WebsiteMapper">
            <select id="selectWebsite"
                parameterType="com.sst.cx.domian.Website"
                resultType="com.sst.cx.domian.Website">
                SELECT id,name,url,age,country
                FROM website WHERE 1=1
                <choose>
                    <when test="name != null and name !=''">
                        AND name LIKE CONCAT('%',#{name},'%')
                    </when>
                    <when test="url != null and url !=''">
                        AND url LIKE CONCAT('%',#{url},'%')
                    </when>
                    <otherwise>
                        AND age is not null
                    </otherwise>
                </choose>
            </select>
        </mapper>
    
    3. where元素
      用于生成SQL的where字符串,否则需要加WHERE 1=1。
        <where>
            <if test="判断条件">
                AND/OR ...
            </if>
        </where>
    
    例(不需要再加WHERE 1=1)
        <select id="selectWebsite" resultType="com.sst.cx.domian.Website">
            select id,name,url from website
            <where>
                <if test="name != null">
                    AND name like #{name}
                </if>
                <if test="url != null">
                    AND url like #{url}
                </if>
            </where>
        </select>
    
    4. trim元素
      用于选择性插入、更新、删除或者条件查询等操作(去除SQL语句中多余的AND关键字、逗号;给SQL语句拼接where、set等后缀)。
        <trim prefix="前缀" suffix="后缀" prefixOverrides="替换前缀字符" suffixOverrides="替换后缀字符">
            SQL语句
        </trim>
        1. prefix属性:给SQL语句拼接的前缀(为trim元素包含的内容加上前缀)。
        2. suffix属性:给SQL语句拼接的后缀(为trim元素包含的内容加上后缀)。
        3. prefixOverrides属性:替代SQL语句前面的关键字或字符。
        4. suffixOverrides属性:替代SQL语句后面的关键字或者字符。
    
    例
        <select id="selectWebsite" resultType="net.biancheng.po.Website">
            SELECT id,name,url,age,country
            FROM website
            <trim prefix="where" prefixOverrides="and">
                <if test="name != null and name !=''">
                    AND name LIKE CONCAT ('%',#{name},'%')
                </if>
                <if test="url!= null">
                    AND url like concat ('%',#{url},'%')
                </if>
            </trim>
        </select>
    
    4. foreach元素
      循环语句(用于配合in关键字)。
      应提前预估集合的长度(过长影响性能;部分数据库有SQL长度限制)。
        <foreach item="item" index="index" collection="list|array|map key" open="(" separator="," close=")">
            参数值
        </foreach>
        1. item属性:表示集合中每一个元素进行迭代时的别名。
        2. index属性:表示在迭代过程中每次迭代到的位置。
        3. collection 属性:必填。
          如果传入的是单参数且参数类型是一个 List,collection 属性值为 list。
          如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array。
          如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。
        4. open属性:表示该语句以什么开始(既然是 in 条件语句,所以必然以(开始)。
        5. separator属性:表示在每次进行迭代之间以什么符号作为分隔符(既然是 in 条件语句,所以必然以,作为分隔符)。
        6. close属性:表示该语句以什么结束(既然是 in 条件语句,所以必然以)开始)。
    
    例
        <select id="selectWebsite" parameterType="com.sst.cx.domian.Website" resultType="com.sst.cx.domian.Website">
            SELECT id,name,url,age,country
            FROM website WHERE age in
            <foreach item="age" index="index" collection="list" open="("
                separator="," close=")">
                #{age}
            </foreach>
        </select>
    
    5. bind元素
      用于对参数进行特殊处理后,再拼接到SQL中。
      每个数据库的拼接函数或连接符号不同(如: MySQL的concat 函数、Oracle的连接符号“||”等),这样SQL映射文件就需要根据不同的数据库提供不同的实现,不利于代码的移植而且麻烦。因此,MyBatis 提供了bind标签来解决这一问题。
        1. name属性:给对应参数取的别名。
        2. value属性:给对应参数进行特殊处理。
    
    例
        <select id="selectWebsite" resultType="com.sst.cx.domian.Website">
            <bind name="pattern_name" value="'%'+name+'%'" />
            <bind name="pattern_url" value="'%'+url+'%'" />
            SELECT id,name,url,age,country FROM website
            WHERE name like #{pattern_name}
            AND url like #{pattern_url}
        </select>
    
    6. set元素
      添加set关键字(用于更新语句),去除条件尾部多余的逗号。
    
    例
    <mapper namespace="com.sst.cx.mapper.WebsiteMapper">
        <select id="selectWebsite" resultType="om.sst.cx.domian.Website">
            SELECT * FROM website
            <where>
                <if test="id!=null and id!=''">
                    id=#{id}
                </if>
            </where>
        </select>
        <!--使用set元素动态修改一个网站记录 -->
        <update id="updateWebsite" parameterType="com.sst.cx.domian.Website">
            UPDATE website
            <set>
                <if test="name!=null">name=#{name}</if>
                <if test="url!=null">url=#{url}</if>
            </set>
            WHERE id=#{id}
        </update>
    </mapper>
    

    4. 分页(属于DAO层操作)

    MyBatis的分页功能是基于内存的分页(即先查询出所有记录,再按起始位置和页面容量取出结果)。

    ===》WebsiteMapper.java
    public List<Website> selectWebsite(@Param("site") Website site, @Param("from") Integer currentPageNo,@Param("pageSize") Integer pageSize);
    
    ===》WebsiteMapper.xml
        <select id="selectWebsite" resultType="com.sst.cx.domian.Website">
            SELECT id,name,url,age,country FROM website
            <trim prefix="where" prefixOverrides="and">
                <if test="site.name != null and site.name !=''">
                    AND name LIKE CONCAT ('%',#{site.name},'%')
                </if>
                <if test="site.url!= null and site.url !=''">
                    AND url LIKE CONCAT ('%',#{site.url},'%')
                </if>
                ORDER BY id limit #{from},#{pageSize}
            </trim>
        </select>
    
    ===》测试
        Website site = new Website();
        site.setUrl("http");
        Integer pageSize = 3;
        Integer currentPageNo = 0;
        List<Website> siteList = new ArrayList<Website>();
        siteList = sqlSession.getMapper(WebsiteMapper.class).selectWebsite(site, currentPageNo, pageSize);
        for (Website ws : siteList) {
            System.out.println(ws);
        }
    

    5. 缓存

    MyBatis支持一级缓存(默认开启)和二级缓存。

      1. 都是基于PerpetualCache(MyBatis自带)的HashMap缓存,但作用域不同。
      2. 执行R查询操作会将查询结果进行缓存。
      3. 执行CUD增删改操作后,缓存会被清空。手动清空缓存:session.clearCache(); 。如果指定了刷新时间间隔flushInterval,会定时清空缓存。
      4. 使用(一级缓存:必须是同一个;二级缓存:可以不同)SqlSession对象执行同一个查询SQL(且参数相同)时,(如果缓存没有清空或超时)会从缓存中直接获取结果。
    
    一级缓存(Session缓存,默认开启)
      生命周期和Session保持一致(当Session调用flush或close后,缓存中所有的数据会被清空)。一级缓存是Session独享的(每个Session不能访问其他Session的缓存区)
    
    二级缓存(全局缓存/跨session缓存:可以被所有SqlSession共享)
      可以自定义存储源。
      使用步骤:
        1. 在核心配置文件中开启二级缓存。
        <settings>
            <setting name="cacheEnabled" value="true" />
        </settings>
        2. 在映射文件中配置缓存。
        <mapper namescape="com.sst.cx.mapper.WebsiteMapper">
          <!-- 
            cache配置 
            可以直接<cache/>全部使用默认配置,也可以按需求修改cache参数。
          -->
          <cache
                eviction="FIFO"
                flushInterval="60000"
                size="512"
                readOnly="true" />
          <select id="getWebsiteList" resultType="com.sst.cx.domain.Website" usecache="true">
            ...
          </select>
            ...
        </mapper>
        说明:
          1. eviction属性(缓存回收策略)
            1. LRU:默认。使用较少,移除最长时间不用的对象;
            2. FIFO:先进先出,按对象进入缓存的顺序来移除它们;
            3. SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象;
            4. WEAK:弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象。
          2. flushInterval属性(刷新间隔时间,单位为毫秒)
            默认执行SQL时才会刷新缓存。
          3. size属性(缓存最多可以存储的个数,正整数)
            默认1024,不宜设置过大(过大会导致内存溢出)。
          4. readOnly属性(缓存数据是否只能读取而不能修改)
            默认值为false,可以快速读取缓存但不能修改缓存。
    

    6. 存储过程

    示例

    ===》创建表和存储过程
    
    create table p_user(
        id int primary key auto_increment,
        name varchar(10),
        sex char(2)
    );
    insert into p_user(name,sex) values('A',"男");
    insert into p_user(name,sex) values('B',"女");
    insert into p_user(name,sex) values('C',"男");
    
    -- 创建存储过程(查询得到男性或女性的数量, 传入0表示女性否则是男性)
    DELIMITER $
    CREATE PROCEDURE mybatis.ges_user_count(IN sex_id INT, OUT user_count INT)
    BEGIN
    IF sex_id=0 THEN
    SELECT COUNT(*) FROM mybatis.p_user WHERE p_user.sex='女' INTO user_count;
    ELSE
    SELECT COUNT(*) FROM mybatis.p_user WHERE p_user.sex='男' INTO user_count;
    END IF;
    END
    $
    
    -- 调用存储过程
    DELIMITER ;
    SET @user_count = 0;
    CALL mybatis.ges_user_count(1, @user_count);
    SELECT @user_count;
    
    ===》userMapper.xml
        <!--
            查询得到男性或女性的数量, 如果传入的是0就女性否则是男性
         -->
        <select id="getUserCount" parameterMap="getUserCountMap" statementType="CALLABLE">
            CALL mybatis.ges_user_count(?,?)
        </select>
        <!--
            parameterMap.put("sexid", 0);
            parameterMap.put("usercount", -1);
         -->
        <parameterMap type="java.util.Map" id="getUserCountMap">
            <parameter property="sexid" mode="IN" jdbcType="INTEGER"/>
            <parameter property="usercount" mode="OUT" jdbcType="INTEGER"/>
        </parameterMap>
    
    ===》测试
            Map<String, Integer> parameterMap = new HashMap<String, Integer>();
            parameterMap.put("sexid", 1);
            parameterMap.put("usercount", -1);
            sqlSession.selectOne(com.sst.cx.mapping.userMapper.getUserCount, parameterMap);
            Integer result = parameterMap.get("usercount");
    

    7. (根据数据库表)自动生成:POJO类、mapper映射文件、mapper 接口

    Mybatis提供了一个逆向工程工具,可以根据数据表自动生成针对单表的POJO类、mapper映射文件、mapper 接口。
    从Github下载mybatis-generator-core-xxx.jar

    示例

    ===》1. 添加依赖 
    
    如果使用maven项目时,只需在pom.xml中添加依赖(不再需要手动导入jar包)
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.4.0</version>
        </dependency>
    
    如果不是maven项目,需要导入依赖包(复制mybatis-generator-core-xxx.jar到WEB-INF/lib目录下)。
    
    ===》2. 创建表(user、student、studentCard、website)
    
    DROP TABLE IF EXISTS `student`;
    
    CREATE TABLE `student` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
      `sex` tinyint(4) DEFAULT NULL,
      `cardId` int(20) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `cardId` (`cardId`),
      CONSTRAINT `student_ibfk_1` FOREIGN KEY (`cardId`) REFERENCES `studentcard` (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
    
    DROP TABLE IF EXISTS `studentcard`;
    
    CREATE TABLE `studentcard` (
      `id` int(20) NOT NULL AUTO_INCREMENT,
      `studentId` int(20) DEFAULT NULL,
      `startDate` date DEFAULT NULL,
      `endDate` date DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `studentId` (`studentId`)
    ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
    
    DROP TABLE IF EXISTS `user`;
    
    CREATE TABLE `user` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(20) DEFAULT NULL,
      `pwd` varchar(20) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
    
    DROP TABLE IF EXISTS `website`;
    
    CREATE TABLE `website` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
      `url` varchar(30) COLLATE utf8_unicode_ci DEFAULT '',
      `age` tinyint(3) unsigned NOT NULL,
      `country` char(3) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
      `createtime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    
    ===》3. generatorConfig.xml配置文件(在项目根目录/config目录下创建)
    
    <?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>
        <context id="DB2Tables" targetRuntime="MyBatis3">
            <commentGenerator>
                <!-- 是否去除自动生成的注释 -->
                <property name="suppressAllComments" value="true" />
            </commentGenerator>
            <!-- Mysql数据库连接的信息:驱动类、连接地址、用户名、密码 -->
            <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                connectionURL="jdbc:mysql://localhost:3306/test" userId="root"
                password="12345678" />
    
            <!-- 默认为false,把JDBC DECIMAL 和NUMERIC类型解析为Integer,为true时 把JDBC DECIMAL 和NUMERIC类型解析为java.math.BigDecimal -->
            <javaTypeResolver>
                <property name="forceBigDecimals" value="false" />
            </javaTypeResolver>
    
            <!-- targetProject:生成POJO类的位置 (maven项目路径为./src/main/java)-->
            <javaModelGenerator
                targetPackage="com.sst.cx.domain" targetProject="./src">
                <!-- enableSubPackages:是否让schema作为包的后缀 -->
                <property name="enableSubPackages" value="false" />
                <!-- 从数据库返回的值被清理前后的空格 -->
                <property name="trimStrings" value="true" />
            </javaModelGenerator>
    
            <!-- targetProject:mapper映射文件生成的位置 -->
            <sqlMapGenerator targetPackage="com.sst.cx.mapper"
                targetProject="./src">
                <!-- enableSubPackages:是否让schema作为包的后缀 -->
                <property name="enableSubPackages" value="false" />
            </sqlMapGenerator>
    
            <!-- targetProject:mapper接口生成的的位置 -->
            <javaClientGenerator type="XMLMAPPER"
                targetPackage="com.sst.cx.dao" targetProject="./src">
                <!-- enableSubPackages:是否让schema作为包的后缀 -->
                <property name="enableSubPackages" value="false" />
            </javaClientGenerator>
    
            <!-- 指定数据表 -->
            <table tableName="website"></table>
            <table tableName="student"></table>
            <table tableName="studentcard"></table>
            <table tableName="user"></table>
        </context>
    </generatorConfiguration>
    
    ===》4. GeneratorSqlmap.java(运行后会自动生成POJO类、Mapper类、Mapper映射文件)
    
    package com.sst.cx;
    import java.io.File;
    import java.util.*;
    import org.mybatis.generator.api.MyBatisGenerator;
    import org.mybatis.generator.config.Configuration;
    import org.mybatis.generator.config.xml.ConfigurationParser;
    import org.mybatis.generator.internal.DefaultShellCallback;
    public class GeneratorSqlmap {
        public void generator() throws Exception {
            List<String> warnings = new ArrayList<String>();
            boolean overwrite = true;
            // 指定配置文件
            File configFile = new File("./config/generatorConfig.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);
        }
        // 执行main方法以生成代码
        public static void main(String[] args) {
            try {
                GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
                generatorSqlmap.generator();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    运行后,可以看到在pojo包中,有一部分是名字为 XxxExample 的类,类中包含以下3个成员变量,如下:
        1. protected String orderByClause;
          用于指定ORDER BY条件,这个条件没有构造方法,直接通过传递字符串值指定。
        2. protected boolean distinct;
          用于指定DISTINCT查询。
        3. protected List<Criteria> oredCriteria;
          用于自定义查询条件。
    
    也可以在终端执行如下命令来自动生成
      java -jar mybatis-generator-core-1.3.2.jar -configfile generatorConfig.xml -overwrite
    

    8. Maven+Spring+MyBatis

    1. 创建MavenWeb项目
    
    方式1. 在终端执行如下命令
      还需要手动创建如下目录:src/main/java、src/test/java、src/test/resources。
      导入到Eclipse中。
    mvn archetype:generate -DgroupId=com.sstx -DartifactId=SpringMavenMyBatis -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
    
    方式2. 使用Eclipse、MyEclipse等开发工具创建
    
    2. 编辑pom.xml文件内容
    
    <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/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.sst.cx</groupId>
        <artifactId>SpringMavenMyBatis</artifactId>
        <packaging>war</packaging>
        <version>1.0-SNAPSHOT</version>
        <name>SpringMavenMyBatis</name>
        <url>http://maven.apache.org</url>
        <!-- 解决报错(不再支持源选项 5。请使用 7 或更高版本) -->
        <properties>
            <project.build.sourceencoding>UTF-8</project.build.sourceencoding>
            <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
            <java.version>1.8</java.version>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
        </properties>
        <dependencies>
            <!-- 用于:自动生成POJO类、Mapper映射文件、Mapper接口 -->
            <dependency>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-core</artifactId>
                <version>1.4.1</version>
            </dependency>
            <!-- 添加mybatis依赖 -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.11</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>2.0.7</version>
            </dependency>
            <!-- 添加mysql依赖 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.31</version>
            </dependency>
            <!-- 添加junit单元测试 -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13.2</version>
            </dependency>
            <!-- 添加servlet -->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>4.0.1</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet.jsp</groupId>
                <artifactId>javax.servlet.jsp-api</artifactId>
                <version>2.3.3</version>
            </dependency>
            <!-- 添加jstl -->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>jstl</artifactId>
                <version>1.2</version>
            </dependency>
            <!-- 添加druid连接池 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.2.14</version>
            </dependency>
            <!-- 添加spring依赖 -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>5.3.23</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.3.23</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
                <version>5.3.23</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.3.23</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>5.3.23</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
                <version>5.3.23</version>
            </dependency>
            <!-- 添加Aspectj依赖 -->
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.9.1</version>
            </dependency>
        </dependencies>
        <build>
            <finalName>SpringMavenMyBatis</finalName>
            <plugins>
                <!-- 用于:解决pom.xml文件报错 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.3.2</version>
                </plugin>
            </plugins>
            <!-- 解决com.sst.cx.mapper包下xml文件没有打包到target目录中 -->
            <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>
                </resource>
            </resources>
        </build>
    </project>
    
    3. 创建数据库和表
    
    Create DATABASE Test;
    USE Test;
    
    DROP TABLE IF EXISTS EMPLOYEE;
    create table EMPLOYEE (
       id INT NOT NULL auto_increment,
       first_name VARCHAR(20) default NULL,
       last_name  VARCHAR(20) default NULL,
       salary     INT  default NULL,
       PRIMARY KEY (id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    4. 根据数据库表自动生成POJO类、Mapper映射文件、Mapper接口
    
    在项目根目录/config目录下创建genertorConfig.xml配置文件(内容同上,修改数据库表名)。
    在com.sst.cx包下创建GeneratorSqlmap.java(内容同上),并运行(运行后删除)。
    
    自动生成的结果
    5. 编写相关配置文件
      在src/main/resources目录下创建
    
    ===》1. database.properties文件(数据库连接参数)
    jdbc_driver = com.mysql.cj.jdbc.Driver
    jdbc_url = jdbc:mysql://localhost:3306/Test?characterEncoding=utf8
    jdbc_username = root
    jdbc_password = 12345678
    validationQuery = SELECT 1
    
    
    ===》2. spring.xml文件(springIoc容器配置---spring) 
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context-3.0.xsd">
        <!-- 
            引入database.properties文件 
        -->
        <context:property-placeholder location="classpath:database.properties"/>
        <!-- 
            自动扫描包下的类,从类的注解中获取Bean的定义信息。 
        -->
        <context:component-scan base-package="com.sst.cx.service"/>
    </beans>
    
    
    ===》3. spring-mybatis.xml文件(springIoc容器配置---spring+mybatis)
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
        <!-- 
            配置数据源
        -->
        <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
            <property name="url" value="${jdbc_url}" />
            <property name="username" value="${jdbc_username}" />
            <property name="password" value="${jdbc_password}" />
            <!-- 初始化连接大小 -->
            <property name="initialSize" value="0" />
            <!-- 连接池最大使用连接数量 -->
            <property name="maxActive" value="20" />
            <!-- 连接池最大空闲 -->
            <property name="maxIdle" value="20" />
            <!-- 连接池最小空闲 -->
            <property name="minIdle" value="0" />
            <!-- 获取连接最大等待时间 -->
            <property name="maxWait" value="60000" />
            <!-- 
                <property name="poolPreparedStatements" value="true" /> 
                <property name="maxPoolPreparedStatementPerConnectionSize" value="33" /> 
            -->
            <property name="validationQuery" value="${validationQuery}" />
            <property name="testOnBorrow" value="false" />
            <property name="testOnReturn" value="false" />
            <property name="testWhileIdle" value="true" />
            <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
            <property name="timeBetweenEvictionRunsMillis" value="60000" />
            <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
            <property name="minEvictableIdleTimeMillis" value="25200000" />
            <!-- 打开removeAbandoned功能 -->
            <property name="removeAbandoned" value="true" />
            <!-- 1800秒,也就是30分钟 -->
            <property name="removeAbandonedTimeout" value="1800" />
            <!-- 关闭abanded连接时输出错误日志 -->
            <property name="logAbandoned" value="true" />
            <!-- 监控数据库 mergeStat、stat、-->
            <property name="filters" value="mergeStat" />
        </bean>
        
        <!--
            配置Mybatis 
        -->
        <!-- 配置sqlSessionFactory(数据源、mapper映射文件) -->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <property name="mapperLocations" value="classpath:com/sst/cx/mapper/*.xml" />
        </bean>
        <!-- 配置扫描器(扫描包下的所有mapper接口) -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.sst.cx.dao" />
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
        </bean>
        
        <!-- 
            配置事务
        -->
        <!-- 配置Spring的事务管理器 -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource" />
        </bean>
        <!-- 注解方式配置事务:<tx:annotation-driven transaction-manager="transactionManager"/> -->
        <!-- 配置事务通知(指定事务作用于哪些类的哪些方法,对方法调用前后进行拦截,添加事务逻辑代码) -->
        <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="add*" propagation="REQUIRED" />
                <tx:method name="append*" propagation="REQUIRED" />
                <tx:method name="insert*" propagation="REQUIRED" />
                <tx:method name="save*" propagation="REQUIRED" />
                <tx:method name="update*" propagation="REQUIRED" />
                <tx:method name="modify*" propagation="REQUIRED" />
                <tx:method name="edit*" propagation="REQUIRED" />
                <tx:method name="delete*" propagation="REQUIRED" />
                <tx:method name="remove*" propagation="REQUIRED" />
                <tx:method name="repair" propagation="REQUIRED" />
                <tx:method name="delAndRepair" propagation="REQUIRED" />
    
                <tx:method name="get*" propagation="SUPPORTS" />
                <tx:method name="find*" propagation="SUPPORTS" />
                <tx:method name="load*" propagation="SUPPORTS" />
                <tx:method name="search*" propagation="SUPPORTS" />
                <tx:method name="datagrid*" propagation="SUPPORTS" />
    
                <tx:method name="*" propagation="SUPPORTS" />
            </tx:attributes>
        </tx:advice>
        <aop:config>
            <aop:pointcut id="transactionPointcut" expression="execution(* com.sst.cx.service.*Impl.*(..))" />
            <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />
        </aop:config>
    
        <!-- 配置druid监控springJdbc -->
        <bean id="druid-stat-interceptor" class="com.alibaba.druid.support.spring.stat.DruidStatInterceptor">
        </bean>
        <bean id="druid-stat-pointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut" scope="prototype">
            <property name="patterns">
                <list>
                    <value>com.sst.cx.service.*</value>
                </list>
            </property>
        </bean>
        <aop:config>
            <aop:advisor advice-ref="druid-stat-interceptor" pointcut-ref="druid-stat-pointcut" />
        </aop:config>
    </beans>
    
    6. 添加业务逻辑
    
    ===》EmployeeService.java(com.sst.cx.service)
    package com.sst.cx.service;
    import com.sst.cx.domain.Employee;
    public interface EmployeeService {
        // 添加员工
        void addEmployee(Employee employee);
        // 根据id获取员工
        Employee getEmployeeById(int userId);
    }
    
    ===》EmployeeServiceImpl.java(com.sst.cx.service.impl)
    package com.sst.cx.service.impl;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import com.sst.cx.dao.EmployeeMapper;
    import com.sst.cx.domain.Employee;
    import com.sst.cx.service.EmployeeService;
    @Service("employeeServiceImpl")
    public class EmployeeServiceImpl implements EmployeeService{
        @Autowired
        private EmployeeMapper employeeMapper;
        public void addEmployee(Employee employee) {
            employeeMapper.insert(employee);
        }
        public Employee getEmployeeById(int userId) {
            return employeeMapper.selectByPrimaryKey(userId);
        }
    }
    
    7. 测试
    
    ===》MyBatisTest.java(src/test/java目录下创建com.sst.cx.test包)常规Junit测试框架
    package com.sst.cx.test;
    import org.junit.Before;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import com.sst.cx.domain.Employee;
    import com.sst.cx.service.EmployeeService;
    public class MyBatisTest {
        private EmployeeService employeeService;
        private ApplicationContext context;
        /**
         * before方法:在所有的测试方法之前执行,且只执行一次。
         * 在进行Junit单元测试时,负责做一些初始化工作(如:初始化ApplicationContext)
         */
        @Before
        public void before() {
            // 获取SpringIoc容器
            context = new ClassPathXmlApplicationContext(new String[] { "spring.xml", "spring-mybatis.xml" });
            // 从SpringIoc容器中获取EmployeeServiceImpl对象
            employeeService = (EmployeeService) context.getBean("employeeServiceImpl");
        }
        @Test
        public void testAddEmployee() {
            Employee employee = new Employee();
            employee.setFirstName("张");
            employee.setLastName("三");
            employee.setSalary(500000);
            employeeService.addEmployee(employee);
        }
        @Test
        public void testGetEmployeeById() {
            Employee employee = employeeService.getEmployeeById(39);
            System.out.println(employee.getFirstName() + employee.getLastName());
        }
    }
    
    
    ===》MyBatisSpringTest.java(src/test/java目录的com.sst.cx.test包下创建)Spring的Junit测试框架
    package com.sst.cx.test;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import com.sst.cx.domain.Employee;
    import com.sst.cx.service.EmployeeService;
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {"classpath:spring.xml", "classpath:spring-mybatis.xml" })
    public class MyBatisSpringTest {
        @Autowired
        private EmployeeService employeeService;
        @Test
        public void testAddEmployee(){
            Employee employee = new Employee();
            employee.setFirstName("张");
            employee.setLastName("三");
            employee.setSalary(500000);
            employeeService.addEmployee(employee);
        }
        @Test
        public void testGetEmployeeById(){
            Employee employee = employeeService.getEmployeeById(39);
            System.out.println(employee.getFirstName()+employee.getLastName());
        }
    }
    
    进行JUnit测试,会在数据库中插入一条数据
    8. 测试2(获取所有员工信息并展示在页面中)
    
    ===》1. 编辑web.xml文件
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                          http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
        version="3.0">
        <welcome-file-list>
            <welcome-file>index.jsp</welcome-file>
        </welcome-file-list>
        <listener>
            <description>Spring监听器</description>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
        <!-- ContextLoaderListener初始化Spring上下文时需要使用到的contextConfigLocation参数 -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring.xml,classpath:spring-mybatis.xml</param-value>
        </context-param>
    </web-app>
    
    ===》2. 在EmployeeMapper.xml映射文件中添加
    <!-- 按需求进行扩展 -->
    <select id="getAllEmployee" resultMap="BaseResultMap">
      select * from EMPLOYEE
    </select>
    ===》3. 在EmployeeMapper.java接口中添加
      // 按需求进行扩展
      // 获取所有员工信息
      List<Employee> getAllEmployee();
    ===》4. 在EmployeeService.java中添加
      // 获取所有员工信息
      List<Employee> getAllEmployee();
    ===》5. 在EmployeeServiceImpl.java中添加
      // 获取所有员工信息
      public List<Employee> getAllEmployee() {
        return employeeMapper.getAllEmployee();
      }
    
    ===》6. 创建EmployeeServlet.java(在src/main/java目录的com.sst.cx.web.controller包下)
    package com.sst.cx.web.controller;
    import java.io.IOException;
    import java.util.List;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.context.ApplicationContext;
    import org.springframework.web.context.support.WebApplicationContextUtils;
    import com.sst.cx.domain.Employee;
    import com.sst.cx.service.EmployeeService;
    @WebServlet("/EmployeeServlet")
    public class EmployeeServlet extends HttpServlet {
        private static final long serialVersionUID = 1L;
        private EmployeeService employeeService;
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            // 获取所有的员工信息,添加到request域中
            List<Employee> employeeList = employeeService.getAllEmployee();
            request.setAttribute("employeeList", employeeList);
            request.getRequestDispatcher("/index.jsp").forward(request, response);
        }
        protected void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            doGet(request, response);
        }
        public void init() throws ServletException {
            // 获取Ioc容器
            ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
            // 从Ioc容器中获取employeeServiceImpl
            employeeService = (EmployeeService) context.getBean("employeeServiceImpl");
        }
    }
    
    ===》7. 编辑index.jsp文件
    <%@ page language="java" pageEncoding="UTF-8"%>
    <%--引入JSTL核心标签库 --%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    <!DOCTYPE html>
    <html>
        <head>
            <title>显示所有员工信息</title>
            <style type="text/css">
                table,td{
                    border: 1px solid;
                    border-collapse: collapse;
                }
            </style>
        </head>
        <body>
            <table>
                <tr>
                    <td>员工的ID</td>
                    <td>员工的名字</td>
                    <td>员工的工资</td>
                </tr>
                <%--遍历lstUsers集合中的User对象 --%>
                <c:forEach var="employee" items="${employeeList}">
                    <tr>
                        <td>${employee.id}</td>
                        <td>${employee.firstName}${employee.lastName}</td>
                        <td>${employee.salary}</td>
                    </tr>
                </c:forEach>
            </table>
        </body>
    </html>
    
    ===》8. 打包并放置在Tomcat/webapps目录下
    在浏览器中输入http://127.0.0.1:8080/SpringMavenMyBatis/EmployeeServlet
    
    运行结果

    相关文章

      网友评论

        本文标题:JavaWeb了解之MyBatis框架

        本文链接:https://www.haomeiwen.com/subject/sdzizrtx.html