JavaEE

JavaWeb了解之MyBatis框架

2022-10-27  本文已影响0人  平安喜乐698
目录
  前言
    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
运行结果
上一篇 下一篇

猜你喜欢

热点阅读