JavaWeb了解之MyBatis框架

目录
  前言
    1. MyBatis简介
    2. 下载MyBatis框架
    3. 示例(使用MyBatis访问数据库)
    核心配置文件
    映射配置文件、MyBatis注解
  1. 级联查询
  2. 动态SQL(减少代码工作量) 
  3. 分页
  4. 缓存
  5. 存储过程
  6. (根据数据库表)自动生成:POJO类、mapper映射文件、mapper接口
  7. Maven+Spring+MyBatis

MyBatis(前身为iBatis)是一款免费开源的轻量级访问数据库的JavaORM框架(用来取代JDBC、Hibernate),专注于SQL语句本身(使用xml配置文件或注解 将SQL和代码分离)。

前言

  1. MyBatis简介
MyBatis
  半自动ORM框架(只能自动将表映射到Java对象:读,不能自动将Java对象映射到表:存)。
  最简单的持久化框架(灵活、易学)。
  Dao层解决方案(适用于性能要求高,且需求变化较多的项目,如:互联网项目)。

优点
  1. 简化了JDBC开发(对JDBC作了进一步封装)。
    简化了加载驱动、创建连接、创建statement等繁杂的过程,开发者只需要关注SQL语句本身。几乎所有的JDBC代码
  2. SQL写在xml文件中,和代码分离,降低了耦合度,便于统一管理和优化,提高了代码的可重用性。 
  3. 支持编写动态SQL语句。
  4. 支持以存储过程的形式封装SQL,可以将业务逻辑保留在数据库之外,增强应用的可移植性,更易于部署和测试。
缺点
  1. 编写SQL语句的工作量较大。
  2. SQL语句依赖于数据库(数据库移植性差:不能随意更换数据库)。

MyBatis和Hibernate的区别

  1. Hibernate
    1. 全自动ORM框架。
    2. 使用HQL语句,独立于数据库(数据库移植性高)。不需要编写大量的SQL就可以完成映射,但会多消耗性能,且开发员不能自主的进行SQL性能优化。级联比MyBatis强大。Hibernate的DAO层开发比MyBatis简单,Mybatis使用注解方式时需要维护SQL和结果映射。
    3. 更好的二级缓存(如果出现脏数据,系统会报出错误并提示);可集成第三方缓存。
      1. 在核心配置文件配置缓存提供者。
      2. 每个映射配置文件中配置并发策略。
  2. MyBatis
    1. 半自动ORM框架。
      存:使用xml或注解手动配置SQL映射(将Java类属性映射到SQL字符串中对应的表字段)。
      读:对于查询结果,会自动将字段映射到对应的Java类属性中。
    2. 需手动编写SQL(灵活多变,但工作量相对较大),依赖于数据库(数据库移植性差)。支持动态SQL、处理列表、动态生成表名、支持存储过程。可以进行更为细致的SQL优化,可以减少不必要的SQL语句执行(提高性能)。
    3. 使用二级缓存时需特别小心(如果不能完全确定数据更新操作的波及范围,应避免Cache的盲目使用,否则脏数据的出现会给系统的正常运行带来很大的隐患)。
      1. 在核心配置文件配置开启二级缓存;
      2. 每个映射配置文件中配置多个的缓存机制,针对不同的表可使用不同的缓存机制。Mybatis可以在命名空间中共享相同的缓存配置和实例,通过Cache-ref来实现。

综上所述
  1. Hibernate适合需求明确、业务固定的项目(如:OA项目、ERP项目、CRM项目等)。
  2. MyBatis适合需求多变、性能要求苛刻的互联网项目(如:电商项目、金融类型、旅游类、售票类项目等)。
  1. 下载MyBatis框架

官网(慢)github下载MyBatis框架

MyBatis目录

目录说明:
  1. lib目录
    存放 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接口)
  2. mybatis-3.5.11.jar
    MyBatis核心jar包。
  3. mybatis-3.5.11.pdf
    MyBatis使用说明英文文档。

在项目中集成MyBatis(以Mysql数据库为例)

1. 在非Maven项目中集成MyBatis。
  需要导入如下依赖包:
    1. mysql-connector-java-xxx.jar
    2. mybatis-xxx.jar、MyBatis的lib目录下的所有依赖jar。

2. 在Maven项目中集成MyBatis。
  只需在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访问数据库)
===》创建表(在数据库中)
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. 创建Web项目,并导入依赖包。
  将mysql-connector-java-xxx.jar、mybatis-xxx.jar、MyBatis的lib目录下的所有依赖jar复制到/WEB-INF/lib目录中。

===》2. 创建核心配置文件 mybatis-config.xml(在src目录下,一般命名为mybatis-config)
  定义了:数据库连接相关参数(通常会将这些参数放置在单独的properties文件中,再使用properties元素引入properties文件,然后在dataSource元素的property子元素中使用${参数名}来引用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>

===》3. 创建日志配置文件 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

===》4. 创建持久化类 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;
       }
}

===》5. 创建映射配置文件 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属性值为占位符赋值。

===》6. 创建测试文件 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对象(对应一个数据库。程序运行期间只需创建一次,程序关闭后会销毁该对象,对应application作用域)
  由SqlSessionFactoryBuilder配置对象创建。每次访问数据库时负责创建SqlSession对象。
  看一下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。
  常用方法:
    1. void clearCache();
    2. Configuration getConfiguration();
    3. void rollback(boolean force);
    4. void commit(boolean force);
    5. 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}来引用)
2种方式:  
  1. 引入外部properties文件(可以将数据库连接参数放置在database.properties文件中,然后引入该文件)。
    <properties resource="database.properties"/>
  2. 在property子元素中设置name-value。
    <properties>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </properties>
  1. 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>  
setting配置项 作用 取值
cacheEnabled 是否开启二级缓存。 true(默认)、false
lazyLoadingEnabled 是否开启延迟加载。开启后所有关联对象都会延迟加载,可在特定关联关系中设置fetchType属性进行覆盖该值。 true、false(默认)
aggressiveLazyLoading 当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载。 true、false。版本3.4.1 (不包含)之前的默认值为true,之后为false。
multipleResultSetsEnabled 是否允许单一语句返回多结果集(需要兼容驱动)。 true(默认)、false
useColumnLabel 使用列标签代替列名。不同的驱动会有不同的表现。 true(默认)、false
useGeneratedKeys 是否自动生成主键(需要驱动兼容。尽管一些驱动不能兼容但仍可正常工作,如:Derby)。 true、false(默认)
autoMappingBehavior 指定如何自动映射列字段到属性。NONE表示取消自动映射、PARTIAL表示自动映射(不包括结果集)、FULL表示自动映射(包括结果集,无论是否嵌套)。 NONE、PARTIAL(默认)、FULL
autoMappingUnkno wnColumnBehavior 指定自动映射当中未知列(或未知属性类型)时的行为。 NONE:不处理;WARNING:只有当日志级别达到WARN级别或以下才会显示相关日志;FAILING:如果处理失败会抛出SqlSessionException异常。 NONE(默认)、WARNING、FAILING
defaultExecutorType 配置默认的执行器。SIMPLE:普通的执行器;REUSE:会重用预处理语句;BATCH:执行器将重用语句并执行批量更新。 SIMPLE(默认)、REUSE、BATCH
defaultStatementTimeout 设置超时时间(决定驱动等待数据库响应的秒数) 正整数(默认为null)
defaultFetchSize 设置数据库驱动程序默认返回的条数限制 正整数(默认为null)
safeRowBoundsEnabled 允许在嵌套语句中使用分页(RowBounds),如果允许,设置false true、false(默认)
safeResultHandlerEnabled 允许在嵌套语句中使用分页(ResultHandler)。如果允许,设置false true(默认)、false
mapUnderscoreToCamelCase 是否开启自动驼峰命名规则映射。 true、false(默认)
localCacheScope MyBatis利用本地缓存机制防止循环引用和加速联复嵌套査询。默认值为SESSION(缓存一个会话中执行的所有查询)。若设置值为STATEMENT,本地会话仅用在语句执行上,对相同SqlScssion的不同调用将不会共享数据 SESSION(默认)、STATEMENT
jdbcTypeForNull 当没有为参数提供特定的JDBC类型时,为空值指定 JDBC 类型。某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER NULL、VARCHAR、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(默认)
logPrefix 指定 MyBatis 增加到日志名称的前缀 任何字符串
loglmpl 指定 MyBatis 所用日志的具体实现,未指定时将自动査找 SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、ST DOUT_LOGGING、NO_LOGGING
proxyFactory 指定MyBatis创建具有延迟加栽能力的对象所用到的代理工具 CGLIB、JAVASSIST(MyBatis3.3及以上)
vfsImpl 指定VFS的实现类 提供VFS类的全限定名(多个时以逗号分隔)
useActualParamName 允许用方法参数中声明的实际名称引用参数。要使用此功能,项目必须被编译为 Java 8 参数的选择。(从版本 3.4.1 开始可以使用) true(默认) 、false
  1. typeAliases元素(定义别名,在映射文件中不再需要使用类的全限定名)
2种方式:  
  1. 对单个类定义别名。
    <typeAliases>
      <typeAlias alias = "Employee" type = "com.sst.cx.domain.Employee"/>
    </typeAliases>
  2. 对包路径下的所有类定义别名(将类名的第一个字母小写作为别名)。
    <typeAliases>
      <package name = "com.sst.cx.domain"/>
    </typeAliases>
  1. typeHandlers元素(有多个typeHandler子元素)
  对数据库类型和Java类型进行转换。
    1. jdbcType属性:指定数据库类型。
    2. javaType属性:指定对应的Java类型。
  对于自定义类型,需要实现TypeHandler接口或继承BaseTypeHandle类。
  1. environments元素(用于配置多套运行环境,一套环境对应一个environment子元素)
  default属性:默认使用哪一套环境(必须指定一个默认环境),对应environment元素的运行环境id。
  environment元素(用来配置MyBatis的一套运行环境,需指定运行环境 ID、事务管理、数据源配置等相关信息。不同的环境使用的数据库不同)有2个子元素:transactionManager元素、dataSource元素。
  例:
    <environments default="development">
      <environment id="development">
      </environment>
    </environments>

transactionManager元素(指定事务管理器)
  有2种:
    1. JDBC
      应用服务器负责事务管理操作(如:提交、回滚)。
    2. MANAGED
      应用服务器负责管理连接生命周期。
  例:
     <!-- 使用JDBC的事务管理 -->
    <transactionManager type="JDBC" />

dataSource元素(用于配置数据库连接相关参数:驱动程序类、URL、用户名、密码)
  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>
  1. mappers元素(用于配置所有映射配置文件的路径。有多个mapper子元素,一个映射文件对应一个mapper元素)
例:
    <!-- 配置映射文件 -->
    <mappers>
        <!-- resource属性:用于指定SQL映射文件的路径 -->
        <mapper resource="com/sst/cx/mapper/EmployeeMapper.xml" />
    </mappers>

映射配置文件

映射器
  用于:配置缓存、定义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. 测试(测试代码同上)

注解方式:

注解主要分为3大类:
  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的值。
      常用属性:
        1. statement属性:获取id的SQL语句。
        2. keyProperty属性:可选,将查询结果赋值给哪个类属性。
        3. keyColumn属性:可选,将查询结果赋值给哪个表字段。
        4. resultType属性:指定SQL语句的返回值的类型。
        5. 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
    @Results的常用属性:
      1. id属性:当前结果集的唯一标识;2. value属性(可选):映射表字段到Java类属性。
    @Result注解(代表一个字段的映射关系)的常用属性:
      1. property属性(指定属性名);2. column属性(指表定字段名);3. jdbcType属性(字段类型);4. 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); 

xml配置文件方式:

映射配置文件的元素 描述
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(双向滚动,及时更新)。
  1. 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列时需要设置。如果是联合主键,可以将多个值用逗号隔开。
  1. update元素(对应一个更新语句)
例
  定义一个id为updateEmployee的更新语句
    <update id="updateEmployee">
        update EMPLOYEE set salary = ${salary} WHERE first_name=#{firstName} AND last_name=#{lastName}
    </update>
  1. delete元素(对应一个删除语句)
例
  定义一个id为deleteEmployee的删除语句
    <delete id="deleteEmployee">
        delete from EMPLOYEE WHERE first_name=#{firstName} AND last_name=#{lastName}
    </delete>
update元素、delete元素的常用属性 描述
id 同select元素
parameterType 同select元素
flushCache 同select元素
timeout 同select元素
statementType 同select元素。
  1. 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. 级联(关联)查询

获取关联数据时十分便捷,但级联过多(超过3层时不再使用)会增加系统的复杂度和性能。

3种关联关系:
  1. 一对一(如:学生和学号)
    通过resultMap元素的association子元素处理一对一级联关系。
    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子元素(ofType属性:表示集合中的元素类型)处理一对多级联关系(会将关联查询的多条记录映射到一个list集合属性中)。
    例:
      <collection property="studentList" ofType="com.sst.cx.domain.Student" column="id" select="com.sst.cx.mapper.StudentMapper.selectStudentById" />
  3. 多对多(如:学生和课程)
    MyBatis没有实现多对多级联,推荐创建一张中间表(3个字段:id、学生id、课程id)通过两个一对多级联替换多对多级联,以降低关系的复杂度,简化程序。

示例(一对一)

===》实体类
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>

2. 动态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元素(用于添加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>
5. 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>
6. 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>
7. 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>
      <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>

3. 分页(属于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);
    }

4. 缓存

MyBatis支持一级缓存(Session缓存:默认强制开启)和二级缓存(全局缓存/跨Session缓存)。

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

5. 存储过程

示例

===》创建表和存储过程
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. 添加依赖 
  1. 对于maven项目时,只需在pom.xml中添加依赖(不再需要手动导入jar包)
    <dependency>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-core</artifactId>
        <version>1.4.0</version>
    </dependency>
  2. 对于非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

7. 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
运行结果
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容