Mybatis的简单使用

概述

  • Mybatis是一个半自动化的持久化层框架
    为什么要用Mybatis?
    1.原生JDBC的SQL代码夹杂在Java代码里面,维护成本高,代码耦合度高
    2.Hibernate和JPA处理复杂SQL难度较大,内部自动生成的SQL开发人员无法做特殊优化,基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难,导致数据库性能下降;
    3.对于开发人员,SQL和java编码分开,功能边界清洗;

HelloWorld

  • 相关表
CREATE TABLE `emp` (
  `id` int NOT NULL AUTO_INCREMENT,
  `emp_name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
  `emp_gender` char(1) COLLATE utf8mb4_bin DEFAULT NULL,
  `emp_email` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
  • 创建对应实体类
public class Employee {

    private Integer id;
    private String empName;
    private String empGender;
    private String empEmail;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public String getEmpGender() {
        return empGender;
    }

    public void setEmpGender(String empGender) {
        this.empGender = empGender;
    }

    public String getEmpEmail() {
        return empEmail;
    }

    public void setEmpEmail(String empEmail) {
        this.empEmail = empEmail;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", empName='" + empName + '\'' +
                ", empGender='" + empGender + '\'' +
                ", empEmail='" + empEmail + '\'' +
                '}';
    }
}
  • 导入相关依赖
<dependencies>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
        <!--为了看看打印的sql-->
        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>
  • 编写mybatis全局配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;characterEncoding=utf-8&amp;serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 将写好的sqlMapper文件注册到全局配置文件中 -->
    <mappers>
        <mapper resource="EmployeeMapper.xml"/>
    </mappers>
</configuration>
  • 编写sql映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace:名称空间-->
<mapper namespace="per.xgt.mapper.EmployeeMapper">
<!--    namespace+id:唯一标识,resultType:返回值类型 -->
    <select id="selectEmp" resultType="per.xgt.entity.Employee">
        select * from emp where id = #{id}
    </select>
</mapper>
  • 测试类
public class testMybatis {

    /**
     * 1.根据xml配置文件创建一个sqlSessionFactory对象
     * 2.获取sqlSession实例,能直接执行已经映射的sql语句
     * @throws IOException
     */
    @Test
    public void test1() throws IOException {
        // 根据xml配置文件创建一个sqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 获取sqlSession实例
        SqlSession sqlSession = sqlSessionFactory.openSession();
        Employee employee = sqlSession.selectOne("per.xgt.mapper.EmployeeMapper.selectEmp", 1);
        System.out.println(employee);
        sqlSession.close();
    }
}
  • 执行结果
DEBUG 07-26 15:17:45,204 ==>  Preparing: select * from emp where id = ?   (BaseJdbcLogger.java:145) 
DEBUG 07-26 15:17:45,230 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:145) 
DEBUG 07-26 15:17:45,293 <==      Total: 1  (BaseJdbcLogger.java:145) 
Employee{id=1, empName='null', empGender='null', empEmail='null'}
  • 字段为null
    查询的字段列要与类属性一致,如果不一致需要取别名
<?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">
<!--namespace:名称空间-->
<mapper namespace="per.xgt.mapper.EmployeeMapper">
<!--    namespace+id:唯一标识,resultType:返回值类型 -->
    <select id="selectEmp" resultType="per.xgt.entity.Employee">
        select id,emp_name empName,emp_gender empGender,emp_email empEmail from emp where id = #{id}
    </select>
</mapper>

接口式编程

  • 编写接口
public interface EmployeeMapper {
    
    public Employee getEmpById(Integer id);
    
}
  • sql映射文件的namespace为接口的全类名
namespace="per.xgt.mapper.EmployeeMapper"
  • sql的id为接口的方法名
<select id="getEmpById" resultType="per.xgt.entity.Employee">
        select id,emp_name empName,emp_gender empGender,emp_email empEmail from emp where id = #{id}
    </select>

mybatis会为接口自动的创建一个代理对象,代理对象去执行增删改查方法

  • 测试
@Test
    public void test2() throws IOException {
        SqlSessionFactory sqlSessionFactory = get();
        SqlSession sqlSession = sqlSessionFactory.openSession();

        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        Employee emp = mapper.getEmpById(1);
        System.out.println(emp);
        sqlSession.close();
    }

小结

  • sqlSession和connection一样都是现成非安全的,代表个数据库的一次回话,用完必须关闭,每次使用都应该去获取新的对象
  • Mapper接口没有实现类,但是mybatis会为这个借口生成一个代理对象,将接口和xml进行绑定
  • 两个配置文件:mybatis的全局配置文件,包含数据库连接池信息,事务管理器信息等系统运行环境;sql映射文件,包含了每一个sql语句的映射信息

mapper.xml文件位置

如果mapper文件在resource目录下,编译时可以直接找到,如果mapper文件在java目录下,则需要在pom文件中添加以下内容说明需要编译java文件下的mapper文件

<build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

全局配置文件

properties引入外部配置文件

<!--properties标签:引入外部properties文件
            resource:引入类路径下的资源
            url:引入网络路径或者磁盘路径下的资源
    -->
    <properties resource="jdbc.properties"></properties>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

settings运行时行为设置

完整的settings配置

<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>
  • 举例:驼峰命名
<!--settings包含很多重要的设置项
            setting:用来设置每一个设置项
                name:设置项名
                value:设置项取值
    -->
    <settings>
        <!--设置驼峰命名法:-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

运行结果

DEBUG 07-26 16:15:37,186 ==>  Preparing: select * from emp where id = ?   (BaseJdbcLogger.java:145) 
DEBUG 07-26 16:15:37,213 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:145) 
DEBUG 07-26 16:15:37,275 <==      Total: 1  (BaseJdbcLogger.java:145) 
Employee{id=1, empName='tom', empGender='0', empEmail='tom@163.com'}

typeAliases别名

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写

<!--typeAliases别名处理器:可以为java类型取别名-->
    <typeAliases>
        <!--type:指定要取别名的全类名;alias:要取的别名,如果不写,默认为类名小写-->
        <typeAlias type="per.xgt.entity.Employee" alias="emp"/>
    </typeAliases>

mapper文件使用别名

<select id="getEmpById" resultType="emp">
        select * from emp where id = #{id}
    </select>

批量取别名

<!--typeAliases别名处理器:可以为java类型取别名-->
    <typeAliases>
        <!--type:指定要取别名的全类名;alias:要取的别名,如果不写,默认为类名小写-->
        <typeAlias type="per.xgt.entity.Employee" alias="emp"/>
        <!--package:为某个包下的所有类批量取别名
                name:指定包名(为当前包以及下面所有的子包都会取别名);
                别名为类名,别名不区分大小写;
                如果当前包和子包下有相同名字的类,可以在类上使用注解@Alias指定别名
        -->
        <package name="per.xgt.entity"/>
    </typeAliases>
@Alias("emp")
public class Employee

typeHandlers类型处理器

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

plugins插件

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

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)
    这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心。
  • 通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

enviroments运行环境

<!--environments:mybatis可以配置多种环境,default;指定使用哪种环境,达到快速切换环境
            environment:配置一个具体的环境信息,必须有transactionManager和dataSource,id代表当前环境的唯一标识
                transactionManager:事务管理器;
                    type:事务管理器的类型;JDBC(使用JdbcTransactionFactory管理)|MANAGED(使用ManagedTransactionFactory管理)
                        自定义事务管理器:实现TransactionFactory接口,type指定为全类名
                dataSource:数据源
                    type:UNPOOLED|POOLED|JNDI
                    自定义数据源:实现DataSourceFactory接口,type是自定义数据源的全类名
    -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

databaseIdProvider多数据库支持

<!--databaseIdProvider:支持多数据厂商
        type:DB_VENDOR:得到数据库厂商的标识(驱动),mybatis就能根据数据库厂商标识来执行不同的sql;
    -->
    <databaseIdProvider type="DB_VENDOR">
        <!--为不痛数据库厂商起别名-->
        <property name="Mysql" value="mysql"/>
        <property name="SQL Server" value="sqlserver"/>
        <property name="DB2" value="db2"/>
        <property name="Oracle" value="oracle" />
    </databaseIdProvider>

为sql指定数据库厂商

<select id="getEmpById" resultType="EMP" databaseId="mysql">
        select * from emp where id = #{id}
    </select>

mappers sql映射注册

<!-- mappers:将sql映射注册到全局配置中
            mapper:注册一个sql映射
                resource:引用类路径下的资源;
                url:引用网络路径下或磁盘路径下的资源;
                class:使用映射器接口实现类的完全限定类名;
                    方式一:有sql映射文件,映射文件名必须和就扣同名,并且放在接口同一目录下;
                    方式二:没有sql映射文件,所有的sql都是利用注解写在接口上;
            package:将包内的映射器接口实现全部注册为映射器;
     -->

class

<mappers>
        <mapper resource="EmployeeMapper.xml"/>
        <mapper class="per.xgt.mapper.EmployeeMapperAnnotation"/>
    </mappers>
@Select("select * from emp where id = #{id}")
    public Employee getEmpById(Integer id);

批量注册

<mappers>
        <mapper resource="EmployeeMapper.xml"/>
        <mapper class="per.xgt.mapper.EmployeeMapperAnnotation"/>
        <package name="per.xgt.mapper"/>
    </mappers>

package标签 name属性,映射该包下所有的sql映射文件 ,这种方式能够批量注册,并且每一对接口和xml名称必须相同

package per.xgt.mapper;

import per.xgt.entity.Employee;

/**
 * @author gentao9527
 * @version 1.0
 * @date 2022/7/26 15:01
 * @description
 */
public interface EmpMapper {
    public Employee getEmpById(int id);

}
<?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="per.xgt.mapper.EmpMapper">
    <select id="getEmpById" resultType="per.xgt.entity.Employee">
        select * from emp where id = #{id}
    </select>
</mapper>

Mapper.xml

增删改


  • 使用insert标签,parameterType标识参数类型,返回值为影响行数
public int insertEmp(Employee employee);
<insert id="insertEmp" parameterType="per.xgt.entity.Employee">
        insert into emp values(null,#{empName},#{empGender},#{empEmail})
    </insert>

手动提交回滚测试代码

@Test
    public void test2() throws IOException {
        // 根据xml配置文件创建一个sqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 获取sqlSession实例,或渠道的SQLSession不会自动提交数据
        SqlSession sqlSession = sqlSessionFactory.openSession();
        Employee employee = new Employee();
        employee.setEmpEmail("xgt@163.com");
        employee.setEmpGender("1");
        employee.setEmpName("xgt");
        int insert = sqlSession.insert("per.xgt.mapper.EmployeeMapper.insertEmp", employee);
        System.out.println(insert);
        // 提交数据
        sqlSession.commit();
        // 回滚数据
        sqlSession.rollback();
        sqlSession.close();
    }

sqlSessionFactory.openSession(true);里面参数true|false代表是否自动提交
如果有自增ID 新增执行SQL后不提交或者回滚,自增ID会有id不连续的问题

  • 删除和修改
    删除和修改与新增类似,只是标签不同
    删除:
    修改:
  • 返回值问题
    在增删改时,返回值可以为boolean类型(只要被影响行超过0,就会返回true),或者Integer或者Long类型(返回具体影响行数);

insert获取自增主键ID

支持自增主键的数据库如:Mysql

useGeneratedKeys:使用自增主键获取主键策略
keyProperty:指定对应的主键属性,也就是mybatis获取到主键值后,将封装给bean的哪个属性;

<insert id="insertEmpAndGetKey" parameterType="per.xgt.entity.Employee" useGeneratedKeys="true" keyProperty="id">
        insert into emp values(null,#{empName},#{empGender},#{empEmail})
    </insert>
@Test
    public void test3() throws IOException {
        // 根据xml配置文件创建一个sqlSessionFactory对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 获取sqlSession实例,或渠道的SQLSession不会自动提交数据
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        Employee employee = new Employee();
        employee.setEmpEmail("wp@163.com");
        employee.setEmpGender("1");
        employee.setEmpName("wp");
        int insert = sqlSession.insert("per.xgt.mapper.EmployeeMapper.insertEmpAndGetKey", employee);
        System.out.println(employee);
        sqlSession.close();
    }

获取到的主键ID会自动封装到bean的主键属性

DEBUG 07-27 10:13:56,288 ==>  Preparing: insert into emp values(null,?,?,?)   (BaseJdbcLogger.java:145) 
DEBUG 07-27 10:13:56,316 ==> Parameters: wp(String), 1(String), wp@163.com(String)  (BaseJdbcLogger.java:145) 
DEBUG 07-27 10:13:56,403 <==    Updates: 1  (BaseJdbcLogger.java:145) 
Employee{id=7, empName='wp', empGender='1', empEmail='wp@163.com'}

不支持自增主键的数据库实现如:Oracle

Oracle不支持自增自增,Oracle可以使用序列模拟自增;每次插入数据主键是从序列中拿到的值;

  • 拿到下一个序列值
select SEQ.nextval from dual;
  • 拿到当前序列值
select SEQ.currval from dual;
  • mybatis获取
<insert id="insertEmpAndGetKey" parameterType="per.xgt.entity.Employee" databaseId="oracle">
        /*插入时的主键是从序列中拿到的
        keyProperty:查询出的主键封装给bean的哪个属性
        order:before当前sql在插入sql之前运行,after当前sql在插入之后运行
        resultType:查处的数据的返回值类型
          */
        <selectKey keyProperty="id" order="BEFORE" resultType="Integer">
            select EMPLOYEE_SEQ.NEXTVAL FROM DUAL
        </selectKey>
        insert into emp values(#{id},#{empName},#{empGender},#{empEmail})
    </insert>
  • 或者sql直接获取序列插入
<insert id="insertEmpAndGetKey" parameterType="per.xgt.entity.Employee" databaseId="oracle">
        /*插入时的主键是从序列中拿到的
        keyProperty:查询出的主键封装给bean的哪个属性
        order:before当前sql在插入sql之前运行,after当前sql在插入之后运行
        resultType:查处的数据的返回值类型
        */
        <selectKey keyProperty="id" order="AFTER" resultType="Integer">
            select EMPLOYEE_SEQ.currval FROM DUAL
        </selectKey>
        insert into emp values(EMPLOYEE_SEQ.NEXTVAL,#{empName},#{empGender},#{empEmail})
    </insert>

参数处理

  • 单个参数
    mybatis不会做特殊处理,#{参数名}直接取出;
  • 多个参数
    mybatis会做特殊处理,多个参数会被封装成一个map,key为param1,param2…,#{}就是从map中获取指定的key值。
    取值可以根据参数索引获取
public Employee getEmp(int id,String name);
<select id="getEmp" resultType="per.xgt.entity.Employee">
        select * from emp where id = #{param1} and empName = #{param2}
    </select>

可以使用全局配置,useActualParamName(jdk1.8),name=参数名

  • 命名参数
    明确指定封装参数时的key
public Employee getEmp(@Param("id") int id, 
@Param("name")String name);
<select id="getEmp" resultType="per.xgt.entity.Employee">
        select * from emp where id = #{id} and empName = #{name}
    </select>
  • POJO参数
    如果多个参数,可以封装成一个实体类传参,#{属性名},直接取出传入的pojo属性值。
public int insertEmp(Employee employee);
<insert id="insertEmp" parameterType="per.xgt.entity.Employee">
        insert into emp values(null,#{empName},#{empGender},#{empEmail})
    </insert>
  • Map
    如果多个参数不是业务模型中的数据,没有对应的POJO,为了方便也可以传入map。#{key},直接取出传入的map的对应value值。
public int insertEmpByMap(Map<String,Object> map);
<insert id="insertEmpByMap" parameterType="java.util.Map">
        insert into emp values(null,#{empName},#{empGender},#{empEmail})
    </insert>

$和#

#{}:可以获取mao中的值,或者pojo对象属性的值;
${}:效果和#{}一样

  • ${}
DEBUG 07-27 11:21:33,179 ==>  Preparing: select * from emp where id = 1   (BaseJdbcLogger.java:145) 
DEBUG 07-27 11:21:33,210 ==> Parameters:   (BaseJdbcLogger.java:145) 
DEBUG 07-27 11:21:33,273 <==      Total: 1  (BaseJdbcLogger.java:145) 
Employee{id=1, empName='tom', empGender='0', empEmail='tom@163.com'}
  • #{}
DEBUG 07-27 11:22:34,662 ==>  Preparing: select * from emp where id = ?   (BaseJdbcLogger.java:145) 
DEBUG 07-27 11:22:34,687 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:145) 
DEBUG 07-27 11:22:34,775 <==      Total: 1  (BaseJdbcLogger.java:145) 
Employee{id=1, empName='tom', empGender='0', empEmail='tom@163.com'}
  • 区别
    #{}:是以预编译的形式,将参数设置到sql语句中,PreparedStatement,防止sql注入;
    ${}:取出的值直接拼装在sql语句中,会有安全问题;
  • 注意
    》#{}会在参数上会带着单引号,而单引号会被识别为字符串;
    》大多数情况下,我们取参数的值都应该使用#{};
    》原生JDBC不支持占位符的地方,就可以使用${}取值

#{}相关规则

  • 规则参数的一些规则:
    》javaType:java
    》jdbcType:通常需要在某种特定的条件下被设置;在数据为null的时候,有些数据库可能不能识别mybatis堆null的默认处理jdbc other,比如Oracle;
<insert id="insertEmp" parameterType="per.xgt.entity.Employee">
        insert into emp values(null,#{empName},#{empGender,jdbcType=NULL},#{empEmail})
    </insert>

还可以全局配置:jdbcTypeForNull=NULL;
》mode:存储过程
》numericScale:
》resultMap:
》typeHandler:
》jdbcTypeName:

返回处理

返回List

public List<Employee> getAllEmployee();
<select id="getAllEmployee" resultType="per.xgt.entity.Employee">
        select * from emp
    </select>

返回map

  • 单条记录封装map
    返回一条记录的map,key就是列名,值就是value;
public Map<String,Object> getOneEmployeeByMap(int id);
<select id="getOneEmployeeByMap" resultType="java.util.Map">
        select * from emp where id = #{id}
    </select>
  • 多条记录封装成一个map,key是id,value是一个实体类
    在接口方法声明上用@MapKey(“empName”)注解告诉mybatis封装时map的key用哪个属性;
@MapKey("empName")
    public Map<String,Object> getOneEmployeeByMaps();
<select id="getOneEmployeeByMaps" resultType="per.xgt.entity.Employee">
        select * from emp
    </select>

resultMap自定义结果映射

  • 自动映射:
    全局设置:autoMapptinBenavior默认是PARTIAL,开启自动映射,要求是列名和bean的属性名一致,设置为null则会取消自动映射;
  • resultType和resultMap只能二选一
<!--自定义某个javaBean的封装规则
        type:自定义规则的Java类型
        id:方便引用
    -->
    <resultMap id="myEmp" type="per.xgt.entity.Employee">
        <!--指定主键列的封装规则
                column:指定哪一列
                property:指定对应的javaBean属性名
        -->
        <id column="id" property="id"/>
        <!--定义普通列规则
                其他不指定的列会自动封装
        -->
        <result column="empName" property="emp_name"/>
        <result column="empGender" property="emp_gender"/>
        <result column="empEmail" property="emp_email"/>
    </resultMap>
    <select id="findOneById" resultMap="myEmp">
        select * from emp where id = #{id}
    </select>
关联查询-单个对象的级联属性
public class Employee {
    private Integer id;
    private String empName;
    private String empGender;
    private String empEmail;
    private Department dept;
    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", empName='" + empName + '\'' +
                ", empGender='" + empGender + '\'' +
                ", empEmail='" + empEmail + '\'' +
                ", dept=" + dept +
                '}';
    }
    public Department getDept() {
        return dept;
    }
    public void setDept(Department dept) {
        this.dept = dept;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getEmpName() {
        return empName;
    }
    public void setEmpName(String empName) {
        this.empName = empName;
    }
    public String getEmpGender() {
        return empGender;
    }
    public void setEmpGender(String empGender) {
        this.empGender = empGender;
    }
    public String getEmpEmail() {
        return empEmail;
    }
    public void setEmpEmail(String empEmail) {
        this.empEmail = empEmail;
    }
}
public class Department {

    private Integer id;
    private String deptName;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getDeptName() {
        return deptName;
    }

    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }

    @Override
    public String toString() {
        return "Department{" +
                "id=" + id +
                ", deptName='" + deptName + '\'' +
                '}';
    }
}
public Employee findOneAndDeptById(int id);

使用属性.属性赋值

<!--级联属性封装:
        直接属性.属性
    -->
    <resultMap id="findOneAndDeptByIdMap" type="per.xgt.entity.Employee">
        <id column="id" property="id"/>
        <result column="empName" property="empName"/>
        <result column="empGender" property="empGender"/>
        <result column="empEmail" property="empEmail"/>
        <result column="dId" property="dept.id"/>
        <result column="deptName" property="dept.deptName"/>
    </resultMap>
    <select id="findOneAndDeptById" resultMap="findOneAndDeptByIdMap">
        SELECT e.id id,
               e.emp_gender empGender,
               e.emp_name empName,
               e.emp_email empEmail,
               d.id dId,
               d.dept_name deptName
        from emp e,dept d
        where e.dept_id = d.id
          and e.id = #{id};
    </select>
association指定联合单个对象
<resultMap id="findOneAndDeptByIdMapAssociation" type="per.xgt.entity.Employee">
        <id column="id" property="id"/>
        <result column="empName" property="empName"/>
        <result column="empGender" property="empGender"/>
        <result column="empEmail" property="empEmail"/>
        <!--association指定联合的javaBean对象
                property:指定哪个属性是联合对象
                javaType:指定这个属性对象的类型
        -->
        <association property="dept" javaType="per.xgt.entity.Department">
            <id column="dId" property="id"/>
            <result column="deptName" property="deptName"/>
        </association>
    </resultMap>
    <select id="findOneAndDeptById" resultMap="findOneAndDeptByIdMapAssociation">
        SELECT e.id id,
               e.emp_gender empGender,
               e.emp_name empName,
               e.emp_email empEmail,
               d.id dId,
               d.dept_name deptName
        from emp e,dept d
        where e.dept_id = d.id
          and e.id = #{id};
    </select>
association分布查询
<!--分布查询:查询员工,按照员工查询部门,将部门封装到员工中-->
    <resultMap id="findOneByStepAndIdMap" type="per.xgt.entity.Employee">
        <id column="id" property="id"/>
        <result column="empName" property="emp_name"/>
        <result column="empGender" property="emp_gender"/>
        <result column="empEmail" property="emp_email"/>
        <!--定义关联对象封装规则
                select:表明当前属性是调用select指定的方法查出的结果
                column:指定将哪一列的值传给这个方法
        -->
        <association property="dept" select="per.xgt.mapper.DepartmentMapper.findOneById" column="dept_id">
        </association>
    </resultMap>
    <select id="findOneByStepAndId" resultMap="findOneByStepAndIdMap">
        select * from emp where id = #{id}
    </select>
  • 延迟加载
    全局配置:
    》lazyLoadingEnable=true|false;开启时,只有真正使用数据的时候才发起查询,不用的时候不查询关联的数据
    》aggressiveLazyLoading=true|false;开启会一次性全部加载
collection关联集合封装
public class Department {

    private Integer id;
    private String deptName;

    private List<Employee> emps;

    public List<Employee> getEmps() {
        return emps;
    }

    public void setEmps(List<Employee> emps) {
        this.emps = emps;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getDeptName() {
        return deptName;
    }

    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }

    @Override
    public String toString() {
        return "Department{" +
                "id=" + id +
                ", deptName='" + deptName + '\'' +
                ", emps=" + emps +
                '}';
    }

}
public Department findEmployeesByDept(Integer id);
<resultMap id="findEmployeesByDeptMap" type="per.xgt.entity.Department">
        <id column="id" property="id"/>
        <result column="deptName" property="deptName"/>
        <!--collection定义集合类型的属性的封装规则
            ofType:指定集合里面元素的类型
        -->
        <collection property="emps" ofType="per.xgt.entity.Employee">
            <!--定义集合中元素封装规则-->
            <id column="empId" property="id"/>
            <result column="empName" property="empName"/>
            <result column="empGender" property="empGender"/>
            <result column="empEmail" property="empEmail"/>
        </collection>
    </resultMap>
    <select id="findEmployeesByDept" resultMap="findEmployeesByDeptMap">
        select d.id id,
               d.dept_name deptName,
               e.id empId,
               e.emp_name empName,
               e.emp_gender empGender,
               e.emp_email empEmail
        from dept d
            left join emp e
                on d.id = e.dept_id
        where d.id = #{id}
    </select>
分步查询
<resultMap id="findOneByStepAndIdMap" type="per.xgt.entity.Department">
        <id column="id" property="id"/>
        <result column="deptName" property="deptName"/>
        <collection property="emps" select="per.xgt.mapper.EmployeeMapper.findAllByDeptId" column="id">
        </collection>
    </resultMap>
    <select id="findOneByStepAndId" resultMap="findOneByStepAndIdMap">
        select * from dept where id = #{id}
    </select>
分步查询传递多列参数

将多列的值封装map传递

<resultMap id="findOneByStepAndIdMap" type="per.xgt.entity.Department">
        <id column="id" property="id"/>
        <result column="deptName" property="deptName"/>
        <collection property="emps"
                    select="per.xgt.mapper.EmployeeMapper.findAllByDeptId"
                    column="{id=id}">
        </collection>
    </resultMap>
    <select id="findOneByStepAndId" resultMap="findOneByStepAndIdMap">
        select * from dept where id = #{id}
    </select>

column=“{key1=column1,key2=column2}”

fetchType:定义加载时机eager|lazy
<resultMap id="findOneByStepAndIdMap" type="per.xgt.entity.Department">
        <id column="id" property="id"/>
        <result column="deptName" property="deptName"/>
        <collection property="emps"
                    select="per.xgt.mapper.EmployeeMapper.findAllByDeptId"
                    column="{id=id}" fetchType="lazy">
        </collection>
    </resultMap>
discriminator鉴别器

判断某列的值,改变封装行为;

<resultMap id="findOneByStepAndIdMapMore" type="per.xgt.entity.Employee">
        <id column="id" property="id"/>
        <result column="empName" property="emp_name"/>
        <result column="empGender" property="emp_gender"/>
        <result column="empEmail" property="emp_email"/>
        <!--鉴别器
            column:指定判定的列名
        -->
        <discriminator javaType="string" column="emp_gender">
            <!--resultType:指定封装的结果类型-->
            <case value="0" resultType="per.xgt.entity.Employee">
                <association property="dept" select="per.xgt.mapper.DepartmentMapper.findOneById" column="dept_id">
                </association>
            </case>
            <case value="1" resultType="per.xgt.entity.Employee">
                <id column="id" property="id"/>
                <result column="empName" property="emp_name"/>
                <result column="empGender" property="emp_gender"/>
                <result column="empEmail" property="emp_name"/>
            </case>
        </discriminator>
    </resultMap>
    <select id="findOneByStepAndId" resultMap="findOneByStepAndIdMapMore">
        select * from emp where id = #{id}
    </select>

动态SQL

if

<select id="findEmpsByIf" parameterType="per.xgt.entity.Employee" resultType="per.xgt.entity.Employee">
        select *
        from emp
        where 1 = 1
        /*if test,判断表达式 OGNL
            从参数中取值进行判断;
          遇见特殊符号比如双引号,应该写转义字符;
          */
        <if test="id != null">
            and id = #{id}
        </if>
        <if test="empName != null and empName != ''">
            and emp_name = #{empName}
        </if>
        /*OGNL会进行字符串与数字的转换判断*/
        <if test="empGender == '0' or empGender == '1'">
            and emp_gender = #{empGender}
        </if>
        <if test="empEmail != null and empEmail.trim() != ''">
            and emp_email = #{empEmail}
        </if>
    </select>

注意:
如果id == null,那么where后面直接就是 and emp_name = #{empName},所以需要在后面添上1=1;

where

解决上面if标签的问题,可以使用where标签来将所有的查询条件包括在内,会自动去掉第一个and或者or

<select id="findEmpsByIf" parameterType="per.xgt.entity.Employee" resultType="per.xgt.entity.Employee">
        select *
        from emp
        <where>
            <if test="id != null">
                and id = #{id}
            </if>
            <if test="empName != null and empName != ''">
                and emp_name = #{empName}
            </if>
            <if test="empGender == '0' or empGender == '1'">
                and emp_gender = #{empGender}
            </if>
            <if test="empEmail != null and empEmail.trim() != ''">
                and emp_email = #{empEmail}
            </if>
        </where>
    </select>

trim

**自定义字符串截取:**可以解决前面where标签的问题

<select id="findEmpsByTrim" resultType="per.xgt.entity.Employee">
        select *
        from emp
              /*后面多出的and或者or where标签不能解决
                prefix="":前缀,给整个拼接后的字符串添加一个前缀
                prefixOverrides="":前缀覆盖,去掉整个前面多余的字符
                suffix=“”:还给整个拼接后的字符串添加后缀
                suffixOverrides="":后缀覆盖
                */
        <trim prefix="where" suffixOverrides="and">
            <if test="id != null">
                id = #{id} and
            </if>
            <if test="empName != null and empName != ''">
                emp_name = #{empName} and
            </if>
            <if test="empGender == '0' or empGender == '1'">
                emp_gender = #{empGender} and
            </if>
            <if test="empEmail != null and empEmail.trim() != ''">
                emp_email = #{empEmail}
            </if>
        </trim>
    </select>

choose

分支选择:

<select id="findEmpsByChoose" resultType="per.xgt.entity.Employee">
        select * from emp
        <where>
            <choose>
                <when test="id != null">
                    id = #{id}
                </when>
                <when test="empName != null">
                    emp_name = #{empName}
                </when>
                <when test="empGender != null">
                    emp_gender = #{empGender}
                </when>
                <otherwise>
                    where 1 = 1
                </otherwise>
            </choose>
        </where>
    </select>

set

封装修改条件
问题:根据条件更新的时候,有可能会多出末尾的逗号","

<update id="updateEmpBySet" parameterType="per.xgt.entity.Employee">
        update emp
        set
        <if test="empName != null">
            emp_name = #{empName} ,
        </if>
        <if test="empGender != null">
            emp_gender = #{empGender} ,
        </if>
        <if test="empEmail != null">
            emp_email = #{empEmail}
        </if>
        where id = #{id}
    </update>

解决:更新操作放在set标签

<update id="updateEmpBySet" parameterType="per.xgt.entity.Employee">
        update emp
        <set>
            <if test="empName != null">
                emp_name = #{empName} ,
            </if>
            <if test="empGender != null">
                emp_gender = #{empGender} ,
            </if>
            <if test="empEmail != null">
                emp_email = #{empEmail}
            </if>
        </set>
        where id = #{id}
    </update>

也可以用trim解决问题:

<update id="updateEmpBySet" parameterType="per.xgt.entity.Employee">
        update emp
        <trim prefix="set" suffixOverrides=",">
            <if test="empName != null">
                emp_name = #{empName} ,
            </if>
            <if test="empGender != null">
                emp_gender = #{empGender} ,
            </if>
            <if test="empEmail != null">
                emp_email = #{empEmail}
            </if>
        </trim>
        where id = #{id}
    </update>

但是如果所有条件都不满足,还是会报错;

  • 推荐使用set

foreach

遍历集合

查询
public List<Employee> findEmpsByList(List<Integer> list);
<!--select * from emp where id in (1,2,3)-->
    <select id="findEmpsByList" resultType="per.xgt.entity.Employee">
        select * from emp where id in
        /*collection:指定要遍历的集合
        list类型的参数会特殊处理封装在map中:map的key就是list
        item:遍历出的元素赋值给指定的变量;
        separator:每个元素之间的分隔符;
        open:遍历的所有结果拼接出一个开始的字符;
        open:遍历的所有结果拼接出一个结束的字符;
        index:遍历list的时候是索引,遍历map的时候是map的key,item就是map的value
        */
        <foreach collection="list" item="id" separator="," open="(" close=")" index="">
            #{id}
        </foreach>
    </select>
批量插入
mysql
  • 方法一
public int addEmps(List<Employee> emps);
<insert id="addEmps" parameterType="java.util.List">
        insert into emp (emp_name,emp_gender,emp_email,dept_id) values
        <foreach collection="list" item="emp" separator=",">
            (#{emp.empName},#{emp.empGender},#{emp.empEmail},#{emp.dept.id})
        </foreach>
    </insert>

注意点


1. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list    
2. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array    
3. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可

也可以指定参数名

public int addEmps(@Param("emps") List<Employee> emps);
<insert id="addEmps" parameterType="java.util.List">
        insert into emp (emp_name,emp_gender,emp_email,dept_id) values
        <foreach collection="emps" item="emp" separator=",">
            (#{emp.empName},#{emp.empGender},#{emp.empEmail},#{emp.dept.id})
        </foreach>
    </insert>
  • 方法二
    一次执行多条sql,需要先允许使用“;”分割多条SQL来执行:allowMultiQueries
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&characterEncoding=utf-8&serverTimezone=UTC&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
<insert id="addEmps" parameterType="java.util.List">
        <foreach collection="emps" item="emp" separator=";">
            insert into emp (emp_name,emp_gender,emp_email,dept_id) values
            (#{emp.empName},#{emp.empGender},#{emp.empEmail},#{emp.dept.id})
        </foreach>
    </insert>
Oracle
  • 方法一
    多个insert放在begin end内;
<insert id="addEmpsOracle1" parameterType="java.util.List">
        <foreach collection="emps" item="emp" open="begin" close="end;">
            insert into emp (id,emp_name,emp_gender,emp_email,dept_id) values
            (employee_seq,nextval,#{emp.empName},#{emp.empGender},#{emp.empEmail},#{emp.dept.id});
        </foreach>
    </insert>
  • 方法二
    利用中间表
<insert id="addEmpsOracle2" parameterType="java.util.List">
        insert into emp (id,emp_name,emp_gender,emp_email,dept_id)
        select employees_seq.nextval,emp_name,emp_gender,emp_email,dept_id
        from 
        <foreach collection="emps" item="emp" separator="union" open="(" close=")">
            select #{emp.empName} emp_name,
                   #{emp.empGender} emp_gender,
                   #{empempEmail} emp_email,
                   #{emp.dept.id} dept_id
            from dual
        </foreach>
    </insert>

内置参数

不只是方法参数传递的参数可以判断,mybatis默认还有两个内置参数;

  • _parameter:代表整个参数
    单个参数: _parameter就是这个参数;
    多个参数:参数会被封装为一个map, _parameter就代表这个map;
  • _databaseId:如果配置了databaseIdProvider标签, _databaseId就是代表当前数据库的别名;

bind

可以将OGNL表达式的值绑定到一个变量中,方便后面引用

<select id="getEmpByLikeBind" resultType="per.xgt.entity.Employee">
        <bind name="_name" value="'%' + empName + '%'"/>
        select *
        from emp
        where id = #{value}
        and emp_name like #{_name}
    </select>

sql与include

sql:抽取可重用的sql片段
include:引用一斤股抽取的sql

  • include可以自定义一些property,sql便签内部就能使用这些自定义的属性property,使用${name}
<sql id="insertColumn">
        <if test="_databaseId == 'mysql'">
            (id,emp_name,emp_gender,emp_email,${aa})
        </if>
       <if test="_databaseId == 'oracle'">
           (emp_name,emp_gender,emp_email)
       </if>
    </sql>
    <insert id="addOne">
        insert into emp
        /*引用抽出的sql片段*/
        <include refid="insertColumn">
            <property name="aa" value="vvv"/>
        </include>
        <if test="_databaseId == 'mysql'">
            values (null,'tom','0','tom@163.com')
        </if>
        <if test="_databaseId == 'oracle'">
            values ('tom','0','tom@163.com')
        </if>

    </insert>

缓存

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

  • 默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启;
  • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
  • 为了提高扩展性,mybatis定义了缓存接口Cache,可以实现Cache接口自定义二级缓存

一级缓存

本地缓存:与数据库同一次会话期间查询到的数据会被放在本地缓存中,以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据;

缓存失效的情况

  • SqlSession不同;
  • SqlSession相同,查询条件不同且当前一级缓存还没有这个数据;
  • SqlSession相同,多次查询期间执行了增删改操作;
  • SqlSession相同,手动清除了一级缓存;
sqlSession.clearCache();

二级缓存

全局缓存:基于namespace级别的缓存,一个namespace对应一个二级缓存;

机制

》一个会话查询一条数据,这条数据就会被放在当前会话的一级缓存中;
》如果会话关闭,一级缓存的数据会被保存到二级缓存中,新的会话查询信息,就可以参照二级缓存的内容;
》如果SqlSesison使用了多个namespace,不同namespace查询出的数据会放到自己对应的缓存中;

  • 二级缓存默认是不开启的,需要手动开启二级缓存,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的
public class Employee implements Serializable

配置

  • 开启全局配置
<!--开启全局二级缓存-->
        <setting name="cacheEnabled" value="true"/>
  • 在mapper.xml中配置使用二级缓存
<!--
    eviction:回收策略:默认是 LRU 最近最少回收策略
        LRU - 最近最少回收,移除最长时间不被使用的对象
        FIFO - 先进先出,按照缓存进入的顺序来移除它们
        SOFT - 软引用,移除基于垃圾回收器状态和软引用规则的对象
        WEAK - 弱引用,更积极的移除基于垃圾收集器和弱引用规则的对象
    flushInterval:缓存刷新间隔,缓存多长时间刷新一次,默认不清空,设置一个毫秒值
    readOnly:是否只读;true|false,MyBatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。MyBatis为了加快获取数据,直接就会将数据在缓存中的引用交给用户。不安全,速度快。读写(默认):MyBatis觉得数据可能会被修改
    size:缓存存放多少个元素
    type:指定自定义缓存的全类名(实现Cache 接口即可)
    blocking:若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存。
    -->
    <cache eviction="LRU" flushInterval="1000*60*5" readOnly="false" size="1024" blocking=""></cache>

查询的数据会从二级缓存中获取,但是查出的数据会被默认先放在一级缓存中,只有会话提交或者关闭后,一级缓存的数据才会转移到二级缓存

  • select标签里面的useCache标签,也可以启用二级缓存
<select id="getEmpById" resultType="per.xgt.entity.Employee" useCache="true">
        select *
        from emp
        where id = #{value}
    </select>
  • 查询和更新操作的标签:flushCache=“true”,执行完后会清除一级和二级缓存;

  • 全局配置:mybatis3.3后->localCacheScope:本地缓存作用域,SESSION|STATEMENT,STATEMENT代表不共享当前数据,可以禁用掉一级缓存;

获取数据顺序

二级缓存》一级缓存》数据库

三方缓存

  • 引入依赖
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.0.3</version>
</dependency>
  • 编写ehcache配置文件
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
    <diskStore path="java.io.tmpdir"/>
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
            maxElementsOnDisk="10000000"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
    />

    <!--
        maxElementsInMemory :设置基于内存的缓存中可存放的对象最大数目
        eternal:设置对象是否为永久的,true表示永不过期,此时将忽略
        timeToIdleSeconds 和 timeToLiveSeconds属性; 默认值是false
        timeToIdleSeconds:设置对象空闲最长时间,以秒为单位, 超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。
        timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。如果此值为0,表示对象可以无限期地存在于缓存中. 该属性值必须大于或等于 timeToIdleSeconds 属性值
        overflowToDisk:设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中
        diskPersistent 当jvm结束时是否持久化对象 true false 默认是false
        diskExpiryThreadIntervalSeconds 指定专门用于清除过期对象的监听线程的轮询时间
        memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
    -->
</ehcache>
  • mapper文件中启用ehcache
<!--自定义缓存-->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache">

    </cache>
  • 其他mapper可以声明和哪个namespace使用的缓存一样
<!--指定和哪个名称空间下的缓存一样-->
    <cache-ref namespace="per.xgt.mapper.DepartmentMapper"/>

与Spring整合

导入相关依赖

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>per.xgt</groupId>
  <artifactId>mybatis-04-ssm</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>mybatis-04-ssm Maven Webapp</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <!-- https://mvnrepository.com/artifact/net.sourceforge.cglib/com.springsource.net.sf.cglib -->
    <dependency>
      <groupId>net.sourceforge.cglib</groupId>
      <artifactId>com.springsource.net.sf.cglib</artifactId>
      <version>2.2.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.aopalliance/com.springsource.org.aopalliance -->
    <dependency>
      <groupId>org.aopalliance</groupId>
      <artifactId>com.springsource.org.aopalliance</artifactId>
      <version>1.0.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.aspectj/com.springsource.org.aspectj.weaver -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>com.springsource.org.aspectj.weaver</artifactId>
      <version>1.7.2.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>4.0.0.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.1.3</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.0.0.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>4.0.0.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>4.0.0.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-expression -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>4.0.0.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>4.0.0.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.29</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->
    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.2</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>4.0.0.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>4.0.0.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>4.0.0.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>4.0.0.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-impl -->
    <dependency>
      <groupId>org.apache.taglibs</groupId>
      <artifactId>taglibs-standard-impl</artifactId>
      <version>1.2.1</version>
      <scope>runtime</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-spec -->
    <dependency>
      <groupId>org.apache.taglibs</groupId>
      <artifactId>taglibs-standard-spec</artifactId>
      <version>1.2.1</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.0</version>
    </dependency>

  </dependencies>

  <build>
    <resources>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <filtering>false</filtering>
      </resource>
      <resource>
        <directory>src/main/resources</directory>
        <includes>
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <filtering>false</filtering>
      </resource>
    </resources>
  </build>

</project>

数据库连接properties配置

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&characterEncoding=utf-8&serverTimezone=UTC&allowMultiQueries=true
jdbc.username=root
jdbc.password=root

web.xml配置文件

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>SSM</display-name>

  <!--加载spring配置文件-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>

  <!-- 字符编码过滤器 -->
  <filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
      <param-name>forceResponseEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
    <init-param>
      <param-name>forceRequestEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

<!-- 启动Web容器时,自动装配ApplicationContext的配置信息 -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!--SpringMVC 配置 前端控制器-->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!--指定springmvc的配置文件-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <!--当值为0或者大于0时,代表容器启动时加载该servlet。
    正数的值越小,启动时加载该servlet的优先级越高。
    如果为负数,则容器启动时不会加载该servlet,只有该servlet被选择时才会加载-->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

</web-app>

SpringMVC配置文件

<?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"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--只扫描Controller注解,只是控制网站跳转逻辑-->
    <context:component-scan base-package="per.xgt" use-default-filters="true">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!--试图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--前缀-->
        <property name="prefix" value="/WEB-INF/pages/"></property>
        <!--后缀-->
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--自动处理静态资源,将mvc不能处理的资源交给Tomcat容器-->
    <mvc:default-servlet-handler/>
    <!--开启注解,能支持springmvc更高级的功能-->
    <mvc:annotation-driven></mvc:annotation-driven>

</beans>

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" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">

    <!--spring希望管理所有的业务组件,除了控制器其他的都管理-->
    <context:component-scan base-package="per.xgt">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!--引入外部配置文件properties-->
    <context:property-placeholder location="classpath:jdbc.properties" />
    <!--spring控制业务逻辑,数据源,事务,aop等...-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <!--spring事务管理,开启基于注解的事务-->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--整合mybatis
        1.spring来管理所有组件,包括mapper.xml
        2.spring来管理事务,声明式事务
    -->
    <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <!--指定mybatis的全局配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <!--指定mapper文件的位置-->
        <property name="mapperLocations" value="classpath*:per/xgt/dao/*.xml"></property>
    </bean>

    <!--扫描所有的mapper接口的实现,让这些emapper能够自动注入-->
<!--    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">-->
<!--        <property name="basePackage" value="per.xgt.dao"></property>-->
<!--    </bean>-->
    <!--效果同上-->
    <mybatis-spring:scan base-package="per.xgt.dao" />

    <!--开启注解驱动-->
    <tx:annotation-driven />


</beans>

Mybatis全局配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <settings>
        <setting name="cacheEnabled" value="true"/>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

</configuration>

类和方法

  • POJO
public class Employee {

    private Integer id;
    private String empName;
    private String empGender;
    private String empEmail;

    public Employee(Integer id, String empName, String empGender, String empEmail) {
        this.id = id;
        this.empName = empName;
        this.empGender = empGender;
        this.empEmail = empEmail;
    }

    public Employee() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public String getEmpGender() {
        return empGender;
    }

    public void setEmpGender(String empGender) {
        this.empGender = empGender;
    }

    public String getEmpEmail() {
        return empEmail;
    }

    public void setEmpEmail(String empEmail) {
        this.empEmail = empEmail;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", empName='" + empName + '\'' +
                ", empGender='" + empGender + '\'' +
                ", empEmail='" + empEmail + '\'' +
                '}';
    }

}
  • DAO
public interface EmployeeMapper {

    public Employee findOneById(Integer id);

    public List<Employee> findAllEmps();

}
  • Mapper.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="per.xgt.dao.EmployeeMapper">

    <select id="findOneById" resultType="per.xgt.entity.Employee">
        select * from emp where id = #{id}
    </select>

    <select id="findAllEmps" resultType="per.xgt.entity.Employee">
        select * from emp
    </select>

</mapper>
  • Service接口
public interface EmployeeService {

    public List<Employee> findAllEmps();

}
  • ServiceImpl
@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeMapper employeeMapper;

    @Override
    public List<Employee> findAllEmps() {
        List<Employee> emps = employeeMapper.findAllEmps();
        return emps;
    }

}
  • Controller
@Controller
public class EmployeeController {

    @Autowired
    private EmployeeService employeeService;

    @RequestMapping("findAllEmps")
    public String emps(Map<String,Object> map){
        List<Employee> emps = employeeService.findAllEmps();
        for (Employee emp : emps) {
            System.out.println(emp);
        }
        map.put("emps", emps);
        return "list";
    }

}

  • index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
<h2>Hello World!</h2>
<a href="findAllEmps">查询所有员工</a>
</body>
</html>
  • list.jsp
<%--
  Created by IntelliJ IDEA.
  User: Valen
  Date: 2022/7/28
  Time: 16:53
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <table>
        <tr>
            <td>id</td>
            <td>empName</td>
            <td>empGender</td>
            <td>empEmail</td>
        </tr>
        <c:forEach items="${emps}" var="emp">
            <tr>
                <td>${emp.id}</td>
                <td>${emp.empName}</td>
                <td>${emp.empGender}</td>
                <td>${emp.empEmail}</td>
            </tr>
        </c:forEach>
    </table>
</body>
</html>

插件

所有mybatis对象,都会通过下面封装后返回

parameterHandler = 
(ParameterHandler)this.interceptorChain.pluginAll(parameterHandler);

方法内部:获取所有的Interceprot(插件实现接口)(拦截器)

public Object pluginAll(Object target) {
        Interceptor interceptor;
        for(Iterator i$ = this.interceptors.iterator(); i$.hasNext(); target = interceptor.plugin(target)) {
            interceptor = (Interceptor)i$.next();
        }

        return target;
    }
// 返回target对象
target = interceptor.plugin(target))

插件机制:创建代理对象,通过增强拦截每一个方法执行,然后在方法前后执行增强方法

编写插件

  • 编写Interceptor实现类
public class MyPluginA implements Interceptor {
    /**
     * 拦截目标对象的目标方法的执行;
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 执行目标方法
        Object proceed = invocation.proceed();
        // 返回执行后的返回值
        return proceed;
    }

    /**
     * 包装梅花表对象,为目标对象创建一个代理对象
     * @param o
     * @return
     */
    @Override
    public Object plugin(Object o) {
        // 接住Plugin类的wrap方法来使用当前Interceptor包装目标对象
        Object wrap = Plugin.wrap(o, this);
        // 返回动态代理对象
        return wrap;
    }

    /**
     * 将插件注册时的property属性设置进来
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {
        System.out.println("插件配置的信息:" + properties);
    }
}
  • 使用@Intercepts注解完成插件签名
// 完成插件签名,告诉mybatis当前插件用来拦截哪个对象的哪个方法;
@Intercepts({
        @Signature(type = StatementHandler.class,method = "parameterize",args = java.sql.Statement.class)
})
  • 将写好的插件注册到配置文件当中
<plugins>
        <plugin interceptor="per.xgt.config.MyPluginA">
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </plugin>
    </plugins>

多个插件运行流程

创建动态代理的时候,是按照插件配置顺序创建层层代理对象,执行目标方法的之后,按照逆向顺序执行;

功能修改

// 完成插件签名,告诉mybatis当前插件用来拦截哪个对象的哪个方法;
@Intercepts({
        @Signature(type = StatementHandler.class,method = "parameterize",args = java.sql.Statement.class)
})
public class MyPluginA implements Interceptor {
    /**
     * 拦截目标对象的目标方法的执行;
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("intercept===:"+invocation.getMethod());
        // 改变方法参数
        System.out.println("当前拦截的目标对象:"+invocation.getTarget());
        Object target = invocation.getTarget();
        // 拿到target的源数据
        MetaObject metaObject = SystemMetaObject.forObject(target);
        Object value = metaObject.getValue("parameterHandler.parameterObject");
        System.out.println("所有参数:"+value);
        // 修改完要用的参数
        metaObject.setValue("parameterHandler.parameterObject",10);
        // 执行目标方法
        Object proceed = invocation.proceed();
        // 返回执行后的返回值
        return proceed;
    }

    /**
     * 包装梅花表对象,为目标对象创建一个代理对象
     * @param o
     * @return
     */
    @Override
    public Object plugin(Object o) {
        System.out.println("plugin===:" + o);
        // 接住Plugin类的wrap方法来使用当前Interceptor包装目标对象
        Object wrap = Plugin.wrap(o, this);
        // 返回动态代理对象
        return wrap;
    }

    /**
     * 将插件注册时的property属性设置进来
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {
        System.out.println("插件配置的信息===:" + properties);
    }
}

分页插件PageHelper

引入依赖

<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>5.0.0</version>
    </dependency>

配置

  • 没有Spring直接配置在mybatis配置文件中
<configuration>

    <settings>
        <setting name="cacheEnabled" value="true"/>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>

</configuration>
  • 和Spring整合配置
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <!--指定mybatis的全局配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <!--指定mapper文件的位置-->
        <property name="mapperLocations" value="classpath*:per/xgt/dao/*.xml"></property>
        <property name="plugins">
            <array>
                <bean class="com.github.pagehelper.PageInterceptor">
                    <property name="properties">
                        <props>
                            <prop key="helperDialect">mysql</prop>
                        </props>
                    </property>
                </bean>
            </array>
        </property>
    </bean>
  • PageHelper参数

分页插件可选参数如下:

  • dialect:默认情况下会使用 PageHelper 方式进行分页,如果想要实现自己的分页逻辑,可以实现 Dialect(com.github.pagehelper.Dialect) 接口,然后配置该属性为实现类的全限定名称。

下面几个参数都是针对默认 dialect 情况下的参数。使用自定义 dialect 实现时,下面的参数没有任何作用。

  • helperDialect:分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置helperDialect属性来指定分页插件使用哪种方言。
    配置时,可以使用下面的缩写值:oracle,mysql,mariadb,sqlite,hsqldb,postgresql,db2,sqlserver,informix,h2,sqlserver2012,derby
    特别注意:使用 SqlServer2012 数据库时,需要手动指定为 sqlserver2012,否则会使用 SqlServer2005 的方式进行分页。
    你也可以实现 AbstractHelperDialect,然后配置该属性为实现类的全限定名称即可使用自定义的实现方法。
  • offsetAsPageNum:默认值为 false,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为 true 时,会将 RowBounds 中的 offset 参数当成 pageNum 使用,可以用页码和页面大小两个参数进行分页。
  • rowBoundsWithCount:默认值为false,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为true时,使用 RowBounds 分页会进行 count 查询。
  • pageSizeZero:默认值为 false,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。
  • reasonable:分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。
  • params:为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值;
    可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值;
    默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero。
  • supportMethodsArguments:支持通过 Mapper 接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。 使用方法可以参考测试代码中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和 ArgumentsObjTest。
  • autoRuntimeDialect:默认值为 false。设置为 true 时,允许在运行时根据多数据源自动识别对应方言的分页 (不支持自动选择sqlserver2012,只能使用sqlserver),用法和注意事项参考下面的场景五。
  • closeConn:默认值为 true。当使用运行时动态数据源或没有设置 helperDialect 属性自动获取数据库类型时,会自动获取一个数据库连接, 通过该属性来设置是否关闭获取的这个连接,默认true关闭,设置为 false 后,不会关闭获取的连接,这个参数的设置要根据自己选择的数据源来决定。

重要提示:当 offsetAsPageNum=false 的时候,由于 PageNum 问题,RowBounds查询的时候 reasonable 会强制为 false。使用 PageHelper.startPage 方法不受影响。

简单使用

@RequestMapping("findAllEmps")
    public String emps(Map<String,Object> map){
        PageHelper.startPage(1, 5);
        List<Employee> emps = employeeService.findAllEmps();
        System.out.println("======");
        for (Employee emp : emps) {
            System.out.println(emp);
        }
        System.out.println("======");
        PageInfo<Employee> list = new PageInfo<Employee>(emps);
        map.put("pageInfo", list);
        System.out.println(list);
        List<Employee> employees = list.getList();
        for (Employee employee : employees) {
            System.out.println(employee);
        }
        return "list";
    }

注意:

  • 一定要在查询方法的前一步使用PageHelper.startPage(首页,页面大小);
  • 想要使用页面的信息时使用pageInfo;
  • PageInfo<实体类> pageInfo = new PageInfo<>(数据库中获取到的信息);

pageInfo内的内容

public class PageInfo<T> implements Serializable {
private static final long serialVersionUID = 1L;
//当前页
private int pageNum;
//每页的数量
private int pageSize;
//当前页的数量
private int size;
//由于startRow 和endRow 不常用,这里说个具体的用法
//可以在页面中"显示startRow 到endRow 共size 条数据"
//当前页面第一个元素在数据库中的行号
private int startRow;
//当前页面最后一个元素在数据库中的行号
private int endRow;
//总记录数
private long total;
//总页数
private int pages;
//结果集
private List<T> list;
//前一页
private int prePage;
//下一页
private int nextPage;
//是否为第一页
private boolean isFirstPage = false;
//是否为最后一页
private boolean isLastPage = false;
//是否有前一页
private boolean hasPreviousPage = false;
//是否有下一页
private boolean hasNextPage = false;
//导航页码数
private int navigatePages;
//所有导航页号
private int[] navigatepageNums;
//导航条上的第一页
private int navigateFirstPage;
//导航条上的最后一页
private int navigateLastPage;
}

Page内的内容

public class Page<E> extends ArrayList<E> {
    private static final long serialVersionUID = 1L;
    //当前页码
    private int pageNum;
    //每一页记录数
    private int pageSize;
    //当前页面第一个元素在数据库中的行号
    private int startRow;
    //当前页面最后一个元素在数据库中的行号
    private int endRow;
    //总记录数
    private long total;
    //总页码
    private int pages;
    //
    private boolean count;
    //
    private Boolean reasonable;
    //
    private Boolean pageSizeZero;
}

批量BatchExecutor

@Test
    public void test18() throws IOException {
        SqlSessionFactory sqlSessionFactory = get();
        // 可以执行批量操作的SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        long start = System.currentTimeMillis();
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        Employee employee = new Employee();
        for (int i = 0; i < 10000; i++) {
            employee.setEmpGender(i%2==0?"0":"1");
            employee.setEmpName("批量"+(i+1));
            employee.setEmpEmail(employee.getEmpName() + "@163.com");
            int batch = mapper.addEmpsBatch(employee);
        }
        sqlSession.commit();
        long end = System.currentTimeMillis();
        sqlSession.close();
        System.out.println("执行时长:" + (end - start));
    }

Spring中使用批量SqlSession

  • spring配置文件
<!--配置一个可以批量执行的SqlSession-->
    <bean class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg>
        <constructor-arg name="executorType" value="BATCH"></constructor-arg>
    </bean>
  • 使用
@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeMapper employeeMapper;

    @Autowired
    private SqlSession sqlSession;

    @Override
    public List<Employee> findAllEmps() {
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        // 执行批量操作
        /*
        * xxxxxx
        * */

        List<Employee> emps = employeeMapper.findAllEmps();
        return emps;
    }

}

Oracle的存储过程

使用游标返回数据列表

  • 结果集对象
public class Page {

    private int start;
    private int end;
    private int count;
    private List<Employee> emps;

    public int getStart() {
        return start;
    }

    public void setStart(int start) {
        this.start = start;
    }

    public int getEnd() {
        return end;
    }

    public void setEnd(int end) {
        this.end = end;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public List<Employee> getEmps() {
        return emps;
    }

    public void setEmps(List<Employee> emps) {
        this.emps = emps;
    }
}
  • 调用存储过程
<!--存储过程的调用
        使用select标签调用存储过程
            statementType:CALLABLE标识调用存储过程
        语法:
            call 存储过程名(参数)
    -->
    <select id="getPageByProcedure" statementType="CALLABLE" databaseId="oracle">
        {
            call emp_getpage(
                #{start,mode=IN,jdbcType=INTEGER},
                #{end,mode=IN,jdbcType=INTEGER},
                #{count,mode=OUT,jdbcType=INTEGER},
                #{emps,mode=OUT,jdbcType=CURSOR,javaType=ResultSet,resultMap=pageEmp}
            )
        }
    </select>
    <resultMap id="pageEmp" type="per.xgt.entity.Employee">
        <id column="id" property="id" />
        <result column="emp_name" property="empName" />
        <result column="emp_email" property="empEmail" />
        <result column="emp_gender" property="empGender" />
    </resultMap>

枚举类型

mybatis 处理枚举类型默认是存储枚举的名字,可以改为存储枚举的索引

<typeHandlers>
        <!--指定使用枚举的索引进行存储
            javaType:在处理哪个索引时用handler处理器;
        -->
        <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="per.xgt.entity.EmpStatus"/>
    </typeHandlers>
public enum EmpStatus {

    LOGIN,LOGOUT,REMOVE

}
public class Employee implements Serializable {

    private Integer id;
    private String empName;
    private String empGender;
    private String empEmail;

    private Department dept;

    // 员工状态
    private EmpStatus empStatus = EmpStatus.LOGOUT;

    public EmpStatus getEmpStatus() {
        return empStatus;
    }

    public void setEmpStatus(EmpStatus empStatus) {
        this.empStatus = empStatus;
    }

    public Employee(Integer id, String empName, String empGender, String empEmail, EmpStatus empStatus) {
        this.id = id;
        this.empName = empName;
        this.empGender = empGender;
        this.empEmail = empEmail;
        this.empStatus = empStatus;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", empName='" + empName + '\'' +
                ", empGender='" + empGender + '\'' +
                ", empEmail='" + empEmail + '\'' +
                ", dept=" + dept +
                ", empStatus=" + empStatus +
                '}';
    }

    public Employee() {
    }

    public Employee(Integer id, String empName, String empGender, String empEmail, Department dept) {
        this.id = id;
        this.empName = empName;
        this.empGender = empGender;
        this.empEmail = empEmail;
        this.dept = dept;
    }

    public Employee(Integer id, String empName, String empGender, String empEmail) {
        this.id = id;
        this.empName = empName;
        this.empGender = empGender;
        this.empEmail = empEmail;
    }

    public Department getDept() {
        return dept;
    }

    public void setDept(Department dept) {
        this.dept = dept;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public String getEmpGender() {
        return empGender;
    }

    public void setEmpGender(String empGender) {
        this.empGender = empGender;
    }

    public String getEmpEmail() {
        return empEmail;
    }

    public void setEmpEmail(String empEmail) {
        this.empEmail = empEmail;
    }



}
public int insertEmpByEnum(Employee employee);
<insert id="insertEmpByEnum" parameterType="per.xgt.entity.Employee" useGeneratedKeys="true" keyProperty="id">
        insert into emp (emp_name,emp_gender,emp_email,emp_status)
        values (#{empName},#{empGender},#{empEmail},#{empStatus})
    </insert>

测试

@Test
    public void test19() throws IOException {
        SqlSessionFactory sqlSessionFactory = get();
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        Employee employee = new Employee(null, "enum", "1", "enum@163.com");
        mapper.insertEmpByEnum(employee);
        System.out.println(employee.getId());
        sqlSession.close();
    }

自定义枚举类型处理器

  • 枚举类
public enum EmpStatus {

    LOGIN(1001,"已登录"),
    LOGOUT(1002,"已登出"),
    REMOVE(1003,"不存在");

    private Integer code;
    private String msg;

    private EmpStatus(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

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

    public String getMsg() {
        return msg;
    }

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

    public static EmpStatus getEmpStatusByCode(Integer code){
        switch (code){
            case 1001:return LOGIN;
            case 1002:return LOGOUT;
            case 1003:return REMOVE;
            default:return LOGOUT;
        }
    }

}

  • mybatis配置
<typeHandlers>
        <typeHandler handler="per.xgt.config.MyEnumHandler" javaType="per.xgt.entity.EmpStatus"/>
    </typeHandlers>
  • 标签内指定使用类型处理器
#{empStatus,typeHandler=per.xgt.config.MyEnumHandler}

如果是查询可以在resultMap中指定

<result column="empName" property="emp_name" typeHandler="per.xgt.config.MyEnumHandler"/>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值