mybatis

官网

https://mybatis.org/mybatis-3/zh/index.html

简介

mybatis

MyBatis 本是Apache的一个开源项目iBatis, 2010年这个项目由Apache Software Foundation 迁移到了Google Code,且改名为MyBatis 。2013年11月迁移到GitHub。iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。

MyBatis是一个半自动ORM框架,其本质是对JDBC的封装。使用MyBatis重点需要程序员编写SQL命令,不需要写一行JDBC代码。

ORM

ORM,Object-Relationl Mapping,对象关系映射,它的作用是在关系型数据库和对象之间作一个映射,这样我们在具体的操作数据库的时候,只要像平时操作对象一样操作它就可以了,ORM框架会根据映射完成对数据库的操作,就不需要再去和复杂的SQL语句打交道了。

JDBC的缺点:需要手动的完成面向对象的Java语言、面向关系的数据库之间数据的转换,代码繁琐无技术含量,影响了开发效率。

查询是需要手动的将结果集的列数据转换为Java对象的属性;而添加操作时需要手动将Java对象的属性转换为数据库表的列字段。

关于面向对象的Java语言、面向关系的数据库之间数据的转换必须要做,问题在于这个转换是否可以不由开发者来做。可以的。ORM框架就是专门来做这个问题的,相当于在面向对象语言和关系数据库之间搭建一个桥梁。

项目搭建

引入依赖

    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!--打印sql语句日志-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.6.1</version>
        </dependency>
        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.23</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <!--mybatis自动生成代码插件,用完必须注释掉-->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.6</version>
        </dependency>


        <!--mybatis二级缓存 依赖  下面需要在配置文件中开启二级缓存-->
        <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>3.8.1</version>
        </dependency>
    </dependencies>


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

resource中mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--全局参数设置 方案一-->
    <!--<properties>-->
    <!--<property name="driver" value="com.mysql.cj.jdbc.Driver"/>-->
    <!--<property name="url" value="jdbc:mysql://124.223.81.248:3307/mybatis"/>-->
    <!--<property name="username" value="root"/>-->
    <!--<property name="password" value="xqm@123456"/>-->
    <!--</properties>-->

    <!--全局参数设置 方案二-->
    <!--如果一个属性在不只一个地方进行了配置,那么,MyBatis 将按照下面的顺序来加载:

        首先读取在 properties 元素体内指定的属性。
        然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
        最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。
        因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。-->
    <properties resource="jdbc.properties">
        <property name="url" value="jdbc:mysql://124.223.81.248:3307/mybatis1"></property>
        <property name="url1" value="jdbc:mysql://124.223.81.248:3307/mybatis"></property>
        <!-- 启用默认值特性 -->
        <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
    </properties>
  
    <settings>
        <!--开启二级缓存-->
        <!--<setting name="cacheEnabled" value="true"/>-->
        <!--开启懒加载/延迟加载   针对级联、关联查询使用-->
        <setting name="lazyLoadingEnabled" value="true"></setting>
        <!--将积极加载改为消极加载,即按需加载-->
        <setting name="aggressiveLazyLoading" value="false"></setting>
        <!--是否开启二级缓存 作用域是mapper/namespace   下面去各自的sql文件中单独开启二级缓存-->
        <setting name="cacheEnabled" value="true"></setting>
    </settings>

    <typeAliases>
        <!--<typeAlias type="com.xqm.model.User" alias="User"></typeAlias>-->
        <!--直接给包设置别名-->
        <package name="com.xqm.model"></package>
    </typeAliases>

    <!--设置连接数据库的环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--引用上面的全局变量-->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>

        <environment id="prduction">
            <!--事务管理 MANAGED  JDBC -->
            <transactionManager type="JDBC"/>
            <!--数据源配置 type="[UNPOOLED 不使用连接池|POOLED 连接池|JNDI]")-->
            <dataSource type="POOLED">
                <!--引用上面的全局变量-->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url1}"/>
                <property name="username" value="${username}"/>
                <!--后面的:xqm@123456代表设置默认值-->
                <property name="password" value="${password:xqm@123456}"/>
                <!--默认是10 在任意时间可存在的活动(正在使用)连接数量-->
                <property name="poolMaximumActiveConnections" value="10"></property>

            </dataSource>
        </environment>
    </environments>
    <!--引入映射文件-->
    <mappers>
        <mapper resource="mappers/UserMapper.xml"/>
        <mapper resource="mappers/User1Mapper.xml"/>
        <mapper resource="mappers/EmpMapper.xml"/>
        <mapper resource="mappers/Emp1Mapper.xml"/>
        <mapper resource="mappers/DeptMapper.xml"/>
        <mapper resource="mappers/Dept1Mapper.xml"/>
        <mapper resource="mappers/EmpDeptMapper.xml"/>

        <mapper resource="mappers/UsersMapper.xml"/>
        <mapper resource="mappers/OrdersMapper.xml"/>
        <mapper resource="mappers/OrderdetailMapper.xml"/>
        <mapper resource="mappers/ItemsMapper.xml"/>
        <!--<package name="mappers"></package>-->
        <!--使用package的时候必须要同名,并且必须在同一包下-->
    </mappers>
</configuration>

jdbc.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://124.223.81.248:3307/mybatis
username=root
password=自己的密码

UserMapper.xml

映射配置文件

resources下新建mappers目录,里面存放xxxxMapper.xml文件

resources.mappers.UserMapper.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">

<!--
cache – 该命名空间的缓存配置。
cache-ref – 引用其它命名空间的缓存配置。
resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
sql – 可被其它语句引用的可重用语句块。
insert – 映射插入语句。
update – 映射更新语句。
delete – 映射删除语句。
select – 映射查询语句。
-->
<mapper namespace="com.xqm.mapper.UserMapper">
    <!--int insertUser();-->
    <insert id="insertUser" parameterType="user">
		insert into user values(null,#{username},#{age},'男')
	</insert>
    <!--#{}是预编译处理 ${}是字符串替换-->
    <select id="selectAll" parameterType="int" resultType="user">
        select * from user where id = #{id}
    </select>

    <insert id="insertNewUser" parameterType="user">
		insert into user values(#{id},#{username},#{age},#{sex})
	</insert>

    <update id="updateTest" parameterType="user">
        update user set age = #{age} where id=#{id}
    </update>

    <delete id="deleteTest" parameterType="int">
        delete from user where id = #{id}
    </delete>
</mapper>

user.sql

/*
 Navicat Premium Data Transfer

 Source Server         : 124.223.81.248-3307-自己数据库
 Source Server Type    : MySQL
 Source Server Version : 80027
 Source Host           : 124.223.81.248:3307
 Source Schema         : mybatis

 Target Server Type    : MySQL
 Target Server Version : 80027
 File Encoding         : 65001

 Date: 22/08/2022 15:22:54
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `username` varchar(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `age` int(0) NULL DEFAULT NULL,
  `sex` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

model.User.class

实体类

package com.xqm.model;

/**
 * @author xqm
 * @Class: TODO
 * @Description:
 * @date: 2022年05月30日 14:19
 */
public class User {

    private Integer id;
    private String username;
    private Integer age;
    private String sex;

    public User() {
    }

    public User(Integer id, String username, Integer age, String sex) {
        this.id = id;
        this.username = username;
        this.age = age;
        this.sex = sex;
    }

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}

mapper.UserMapper.interface

映射接口

package com.xqm.mapper;

import com.xqm.model.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface UserMapper {

    /**
     *
     * 1.mapper接口映射文件的namespace要和mapper接口的全类名保持一致
     * 2.方法名要和配置文件中的id一致
     * @return
     */
    int insertUser(@Param("username") String username,@Param("age") int age);

    List<User> selectAll(int id);

    void insertNewUser(User user);
    void updateTest(@Param("age") int age,@Param("id") int id);

    void deleteTest(int id);
}

mapper映射文件和核心配置文件关系

image.png

log4j日志

resource下新建log4j.properties文件

# \u5168\u5C40\u65E5\u5FD7\u914D\u7F6E
log4j.rootLogger=ERROR, stdout
# MyBatis \u65E5\u5FD7\u914D\u7F6E
#log4j.logger.com.xqm=TRACE
# \u63A7\u5236\u53F0\u8F93\u51FA
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

测试

package test;

import com.xqm.mapper.EmpDeptMapper;
import com.xqm.mapper.User1Mapper;
import com.xqm.mapper.UserMapper;
import com.xqm.model.EmpDept;
import com.xqm.model.User;
import com.xqm.model.User1;
import com.xqm.util.AutoNameUtil;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * @author xqm
 * @Class: TODO
 * @Description:
 * @date: 2022年05月30日 14:37
 */
public class TestMybatis {

    private SqlSessionFactory factory;

    @Test
    public void testMybatis() throws IOException {

        // 获取SqlSession
        SqlSession sqlSession = factory.openSession();
        //通过代理模式创建UserMapper接口的代理实现类对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配元素文件,通过调用的方法名匹配映射文件中的SQL标签,并执行标签中的SQL语句
        // int result = userMapper.insertUser();
        // sqlSession.commit();
        List<User> users = userMapper.selectAll(7);
        //提交事务
        // sqlSession.commit();
        // System.out.println("result:" + result);
        // for (User user : users) {
        //     System.out.println(user.toString());
        // }
        sqlSession.close();

    }


    @Before
    public void before() throws IOException {
        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        // 获取SqlSessionFactoryBuilder
        SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
        // 获取SqlSessionFactory
        factory = ssfb.build(resourceAsStream, "prduction");

    }

}

mybatis-config.xml

properties

全局参数设置,需要使用时用${name}来使用

properties的优先级最低

参数的优先级:

方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性

比如下面,就会优先使用jdbc.properties文件中的数据库配置文件

<properties resource="jdbc.properties">
        <property name="url" value="jdbc:mysql://124.223.81.248:3307/mybatis1"></property>
        <property name="url1" value="jdbc:mysql://124.223.81.248:3307/mybatis"></property>
        <!-- 启用默认值特性 -->
        <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
    </properties>

settings

设置自带属性的开闭

 <settings>
        <!--开启二级缓存-->
        <!--<setting name="cacheEnabled" value="true"/>-->
        <!--开启懒加载/延迟加载   针对级联、关联查询使用-->
        <setting name="lazyLoadingEnabled" value="true"></setting>
        <!--将积极加载改为消极加载,即按需加载-->
        <setting name="aggressiveLazyLoading" value="false"></setting>
        <!--是否开启二级缓存 作用域是mapper/namespace   下面去各自的sql文件中单独开启二级缓存-->
        <setting name="cacheEnabled" value="true"></setting>
    </settings>

typeAliases

设置实体类的别名,mappers.xxxMapper.xml中,可以通过别名来查找到实体类

resultType 返回值类型 和paramterType 上就可以使用别名了

可以直接设置一个类的别名,或者设置整个包,这也这个包下所有的类的都可以使用别名

<typeAliases>
        <!--<typeAlias type="com.xqm.model.User" alias="User"></typeAlias>-->
        <!--直接给包设置别名-->
        <package name="com.xqm.model"></package>
    </typeAliases>

environments

数据库环境设置,可以设置多个,比如生产环境、测试环境、开发环境

<!--设置连接数据库的环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--引用上面的全局变量-->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>

        <environment id="prduction">
            <!--事务管理 MANAGED  JDBC -->
            <transactionManager type="JDBC"/>
            <!--数据源配置 type="[UNPOOLED 不使用连接池|POOLED 连接池|JNDI]")-->
            <dataSource type="POOLED">
                <!--引用上面的全局变量-->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url1}"/>
                <property name="username" value="${username}"/>
                <!--后面的:xqm@123456代表设置默认值-->
                <property name="password" value="${password:xqm@123456}"/>
                <!--默认是10 在任意时间可存在的活动(正在使用)连接数量-->
                <property name="poolMaximumActiveConnections" value="10"></property>

            </dataSource>
        </environment>
    </environments>

mappers

引入映射文件,这样mybatis就可以找到mapper.xml文件,通过mapper.xml文件的namespace找到映射的mapper接口,通过参数类型parameterType找到实体类

   <!--引入映射文件-->
    <mappers>
        <mapper resource="mappers/UserMapper.xml"/>
        <mapper resource="mappers/UsersMapper.xml"/>
        <mapper resource="mappers/OrdersMapper.xml"/>
        <mapper resource="mappers/OrderdetailMapper.xml"/>
        <mapper resource="mappers/ItemsMapper.xml"/>
        <!--<package name="mappers"></package>-->
        <!--使用package的时候必须要同名,并且必须在同一包下-->
    </mappers>

一共有四种方式:

resource、url、class、package

image.png

mybatis的普通模式开发

概念

普通模式,也称为传统DAO模式,就是在传统DAO模式下,定义接口和实现类,如 interface EmpDao

class EmpDaoImpl implements EmpDao. 在实现类中,用SQLSession对象调用 select insert delete update 等方法实现.目前极为少见.在传统模式下,我们需要知道SqlSession对象 实现CURD和 参数传递的处理

普通模式开发现在基本不用

sqlSession查询的三种方式

SqlSession对象本身的API中就有三个查询方法,分别能够实现如下查询方式

1返回单个对象 selectOne

2返回对象List集合 selectList

3返回对象Map集合 selectMap

实体类

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Emp implements Serializable {
    private Integer empno;
    private String ename;
    private String job;
    private Integer mgr;
    private Date hiredate;
    private Double sal;
    private Double comm;
    private Integer deptno;

}

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="EmpMapper">

    <!--
    返回单个对象
    public Emp findOne();
    id 相当于方法名
    resultType 相当于返回值类型
        sql语句的查询结果用哪个类来进行封装 如果返回值类型是集合,这里写的也是集合中的元素对应的类,不是集合本身作为类型
    paramaterType 参数类型
    SQL语句就是具体的方法体的实现
    -->
    <select id="findOne" resultType="emp" >
        select * from emp where empno = 7499
    </select>


    <!--
    返回多个对象List集合
    查询全部的员工信息
    public List<Emp> findAll()
    -->
    <select id="findAll" resultType="emp">
        select * from emp
    </select>

    <!--返回多个对象的Map集合
    把查询出来的数据中的某一列作为键,整条数据封装的对象作为值
    public Map<key,Emp> findEmpMap()
    <empno,Emp>
    <key,Emp>
    -->
    <select id="findEmpMap" resultType="map">
        select * from emp
    </select>

</mapper>

mybatis-config导入配置文件

<mappers>
    <mapper resource="com/msb/mapper/DeptMapper.xml"/>
    <mapper resource="com/msb/mapper/EmpMapper.xml"/>
</mappers>

测试

package com.msb.test;

import com.msb.pojo.Dept;
import com.msb.pojo.Emp;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @Author: Ma HaiYang
 * @Description: MircoMessage:Mark_7001
 */
public class Test2 {

    private SqlSession sqlSession;
    @Before
    public void init(){
        SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder();
        InputStream resourceAsStream = null;
        try {
            resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
        SqlSessionFactory factory=ssfb.build(resourceAsStream) ;
        sqlSession=factory.openSession();
    }

    @Test
    public void testSelectOne(){
        // 查询单个对象
        System.out.println("sqlSession查询单个对象");
        Emp emp = sqlSession.selectOne("findOne");
        System.out.println(emp);

    }

    @Test
    public void testSelectList(){
        // 查询多个对象的List集合
        System.out.println("sqlSession查询对象List集合");
        List<Emp> emps = sqlSession.selectList("EmpMapper.findAll");
        emps.forEach(System.out::println);
    }

    @Test
    public void testSelectMap(){
        // 查询多个对象的Map集合
        System.out.println("sqlSession查询对象Map集合");
        Map<Integer, Emp> empMap = sqlSession.selectMap("findEmpMap", "EMPNO");
        Set<Integer> empnos = empMap.keySet();
        for (Integer empno : empnos) {
            System.out.println(empno+" :" +empMap.get(empno));
        }
    }

    @After
    public void release(){
        // 关闭SQLSession
        sqlSession.close();
    }

}

mybatis参数传递的三种方式

1 单个基础数据类型作为参数

2 多个基础数据类型的map 集合作为参数

3 引用类型作为参数

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="EmpMapper2">
    <!--
    参数为一个基本数据类型
    根据员工工号查询员工的全部信息,返回单个员工对象
    public Emp findByEmpno(int empno);
    parameterType 在有参数情况下也是可以省略不写  mybatis 可以根据实际情况自动判断
    如果要写parameterType 那么就要写对
    在SQL语句上可以使用${}  #{} 代表参数的占位
    如果参数是单个基本数据类型,{}中名字可以随便写,见名知意
    ${} 代表mybatis底层使用Statment语句对象,参数是以字符串拼接的形式设置
    #{} 代表mybatis底层使用的preparedStatment语句对象,参数使用?作为占位符处理
    #{} 以后常用
    -->
    <select id="findByEmpno" resultType="emp" parameterType="int">
        select  * from emp where empno = #{empno}
    </select>

    <!--
    参数为map集合
    查询指定部门号和指定最低薪资的员工信息
    20 号部门 且工资在1500以上的员工信息
    public List<Emp> findEmpByDeptnoAndSal(int deptno,double sal);
    <  >  最好要进行转译处理,参照HTML转译  w3school在线文档中有转译符号对应规则
     Map<String,Object> args=new HashMap<>();
        args.put("deptno", 20);
        args.put("sal", 1500.0);
    #{}中写的是map集合中,参数的键
    -->
    <select id="findEmpByDeptnoAndSal" resultType="emp" parameterType="map">
        select * from emp where deptno = #{deptno} and sal >= #{sal}
    </select>


    <!--
   参数为对象
   emp >>>  deptno   sal
   参数是我们自定义的类型,那么 #{}中写的是参数的属性名
   -->
    <select id="findEmpByDeptnoAndSal2" resultType="emp" parameterType="emp">
        select * from emp where deptno = #{deptno} and sal >= #{sal}
    </select>
</mapper>

测试

package com.msb.test;

import com.msb.pojo.Emp;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @Author: Ma HaiYang
 * @Description: MircoMessage:Mark_7001
 */
public class Test3 {

    private SqlSession sqlSession;
    @Before
    public void init(){
        SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder();
        InputStream resourceAsStream = null;
        try {
            resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
        SqlSessionFactory factory=ssfb.build(resourceAsStream) ;
        sqlSession=factory.openSession();
    }

    @Test
    public void testSingleArg(){
        // 测试单个基本数据类型作为参数
        Emp emp = sqlSession.selectOne("findByEmpno", 7499);
        System.out.println(emp);
    }

    @Test
    public void testMapArg(){
        // 测试Map集合作为参数
        Map<String,Object> args=new HashMap<>();
        args.put("deptno", 20);
        args.put("sal", 3000.0);
        List<Emp> emps = sqlSession.selectList("findEmpByDeptnoAndSal", args);
        emps.forEach(System.out::println);
    }

    @Test
    public void testEmpArg(){
        // 测试Map集合作为参数
        Emp arg =new Emp();
        arg.setDeptno(10);
        arg.setSal(2000.0);
        List<Emp> emps = sqlSession.selectList("findEmpByDeptnoAndSal2", arg);
        emps.forEach(System.out::println);
    }


    @After
    public void release(){
        // 关闭SQLSession
        sqlSession.close();
    }

}

mybatis代理模式开发

前面已经使用MyBatis完成了对Emp表的CRUD操作,都是由SqlSession调用自身方法发送SQL命令并得到结果的,实现了MyBatis的入门。

但是却存在如下缺点:

  1. 不管是selectList()、selectOne()、selectMap(),都是通过SQLSession对象的API完成增删改查,都只能提供一个查询参数。如果要多个参数,需要封装到JavaBean或者Map中,并不一定永远是一个好办法。
  2. 返回值类型较固定。
  3. 只提供了映射文件,没有提供数据库操作的接口,不利于后期的维护扩展。

在MyBatis中提供了另外一种成为 Mapper代理(或称为接口绑定) 的操作方式。在实际开发中也使用该方式。下面我们就是要Mapper代理的方式来实现对Emp表的CRUD操作吧,还有完成多个参数传递、模糊查询、自增主键回填等更多的技能实现。搭建好的项目框架如图所示,相比而言,增加了接口EmployeeMapper。但是却会引起映射文件和测试类的变化。

优点:

1有接口 模块之间有规范了

2参数的处理多样了,接口中的方法参数列表由我们自己决定

3通过代理模式由mybatis提供接口的实现类对象 我们不用写实现类了

image.png

mapper接口

package com.xqm.mapper;

import com.xqm.model.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface UserMapper {

    /**
     *
     * 1.mapper接口映射文件的namespace要和mapper接口的全类名保持一致
     * 2.方法名要和配置文件中的id一致
     * @return
     */
    int insertUser(@Param("username") String username,@Param("age") int age);

    List<User> selectAll(int id);

    void insertNewUser(User user);
    void updateTest(@Param("age") int age,@Param("id") int id);

    void deleteTest(int id);
}

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">

<!--
cache – 该命名空间的缓存配置。
cache-ref – 引用其它命名空间的缓存配置。
resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
sql – 可被其它语句引用的可重用语句块。
insert – 映射插入语句。
update – 映射更新语句。
delete – 映射删除语句。
select – 映射查询语句。
-->
<mapper namespace="com.xqm.mapper.UserMapper">
    <!--int insertUser();-->
    <insert id="insertUser" parameterType="user">
		insert into user values(null,#{username},#{age},'男')
	</insert>
    <!--#{}是预编译处理 ${}是字符串替换-->
    <select id="selectAll" parameterType="int" resultType="user">
        select * from user where id = #{id}
    </select>

    <insert id="insertNewUser" parameterType="user">
		insert into user values(#{id},#{username},#{age},#{sex})
	</insert>

    <update id="updateTest" parameterType="user">
        update user set age = #{age} where id=#{id}
    </update>

    <delete id="deleteTest" parameterType="int">
        delete from user where id = #{id}
    </delete>
</mapper>

测试代码

 @Before
    public void before() throws IOException {
        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        // 获取SqlSessionFactoryBuilder
        SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
        // 获取SqlSessionFactory
        factory = ssfb.build(resourceAsStream, "prduction");

    }


    @Test
    public void insertTest(){
        SqlSession sqlSession = factory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        int result = userMapper.insertUser(AutoNameUtil.autoSurAndName(),13);
        sqlSession.commit();
        sqlSession.close();
    }

参数传递

有以下类型的参数传递:

1.单个基本数据类型

2.多个基本数据类型

3.单个引用数据类型

4.map集合数据类型

5.多个引用数据类型

接口

package com.msb.mapper;

import com.msb.pojo.Emp;
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Map;

/**
 * @Author: Ma HaiYang
 * @Description: MircoMessage:Mark_7001
 */
public interface EmpMapper {
    /**
     * 该方法用于查询全部的员工信息
     * @return 全部员工信息封装的Emp对象的List集合
     */
    List<Emp> findAll();

    /**
     * 根据员工编号查询单个员工信息的方法
     * @param empno 员工编号
     * @return 如果找到了返回Emp对象,找不到返回null
     */
    Emp findByEmpno(int empno);


    /**
     * 根据员工编号和薪资下限去查询员工信息
     * @param empno 员工编号
     * @param sal 薪资下限
     * @return 多个Emp对象的List集合
     */
    List<Emp> findByDeptnoAndSal(@Param("deptno") int deptno,@Param("sal") double sal);

    List<Emp> findByDeptnoAndSal2(Map<String,Object> map);

    List<Emp> findByDeptnoAndSal3(Emp emp);

    List<Emp> findByDeptnoAndSal4(@Param("empa") Emp empa,@Param("empb") Emp empb);


}

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.msb.mapper.EmpMapper">

    <!--
    1 接口的名字和Mapper映射为文件名字必须保持一致(不包含拓展名)
    2 Mapper映射文件的namespace必须是接口的全路径名
    3 sql语句的id必须是对应方法的名
    4 DeptMapper映射文件应该和接口编译之后放在同一个目录下
    -->
    <!--List<Emp> findAll();-->
    <select id="findAll" resultType="emp" >
        select * from emp
    </select>


    <!--
    单个基本数据类型作为方法参数
    #{}中可以随便写,遵循见名知意
    Emp findByEmpno(int empno);
    -->
    <select id="findByEmpno" resultType="emp" >
        select * from emp where empno =#{empno}
    </select>


    <!--
     多个基本数据类型作为方法参数
     List<Emp> findByDeptnoAndSal(@Param("detpno") int deptno,@Param("sal") double sal);
     方式1 arg*     arg0 arg1 arg2 数字是索引,从0开始
     方式2 param*   param1 param2 param3 数字是编号,从1开始
     使用别名
     List<Emp> findByDeptnoAndSal(@Param("detpno") int deptno,@Param("sal") double sal);
     通过@Param注解使用别名之后,就不能再使用arg* 但是可以继续使用param*
    -->
    <select id="findByDeptnoAndSal" resultType="emp">
        <!--select * from emp where deptno =#{arg0} and sal >= #{arg1}-->
        <!-- select * from emp where deptno =#{param1} and sal >= #{param2}-->
        <!-- select * from emp where deptno =#{deptno} and sal >= #{sal}-->
    </select>


    <!--
    参数是map,{}写键的名字
    -->
    <select id="findByDeptnoAndSal2" resultType="emp" parameterType="map" >
        <!--select * from emp where deptno =#{arg0} and sal >= #{arg1}-->
        <!-- select * from emp where deptno =#{param1} and sal >= #{param2}-->
         select * from emp where deptno =#{deptno} and sal >= #{sal}
    </select>

    <!--单个引用类型,{}中写的使用对象的属性名-->
    <select id="findByDeptnoAndSal3" resultType="emp" parameterType="emp" >

        select * from emp where deptno =#{deptno} and sal >= #{sal}
    </select>

    <!--
    多个引用类型作为方法参数
     List<Emp> findByDeptnoAndSal4(@Param("empa") Emp empa,@Param("empb") Emp empb);
     如果用@Param定义了别名,那么就不能使用arg*.属性名,但是可以使用param*.属性名和别名.属性名
    -->
    <select id="findByDeptnoAndSal4" resultType="emp"  >
        <!-- select * from emp where deptno =#{arg0.deptno} and sal >= #{arg1.sal} -->
         select * from emp where deptno =#{param1.deptno} and sal >= #{param2.sal}
         <!-- select * from emp where deptno =#{empa.deptno} and sal >= #{empb.sal}-->
    </select>

</mapper>

模糊查询

在进行模糊查询时,在映射文件中可以使用concat()函数来连接参数和通配符。另外注意对于特殊字符,比如<,不能直接书写,应该使用字符实体替换。

and username  like concat('%',#{username,jdbcType=VARCHAR},'%')
    小于的时候使用<![CDATA[<]]>

   <if test="age!=null">
                and age <![CDATA[<]]> #{age}
            </if>
<!--List<Emp> getByName(String name);-->
<select id="findByEname"  resultType="emp" >
    select * from emp where ename like concat('%',#{name},'%')
</select>

动态SQL

MyBatis中动态SQL是编写在mapper.xml中的,其语法和JSTL类似,但是却是基于强大的OGNL表达式实现的。

if标签

示例:

    <select id="findByCondition" resultType="emp">
        select * from emp where 1=1
        <if test="empno != null">
            and empno =#{empno}
        </if>
        <if test="ename != null and ename != ''">
            and ename like concat('%',#{ename},'%')
        </if>
        <if test="job != null and job != ''">
            and job =#{job}
        </if>
    </select>

where标签

where标签中的内容如果全部都不满足,那么where关键字也不会出现。如果满足,那么where后面的第一个and会自动删除

<select id="findEmpByCondition" resultType="emp">
    select * from emp
    <where>
        <if test="empno != null">
            and empno= #{empno}
        </if>
        <if test="ename != null and ename != ''">
            and ename= #{ename}
        </if>
        <if test="job != null and job != ''">
            and job= #{job}
        </if>
        <if test="mgr != null ">
            and mgr= #{mgr}
        </if>
        <if test="hiredate != null ">
            and hiredate= #{hiredate}
        </if>
        <if test="sal != null">
            and sal= #{sal}
        </if>
        <if test="comm != null ">
             and comm =#{comm}
        </if>
        <if test="deptno != null ">
            and deptno= #{deptno}
        </if>
    </where>
</select>

choose标签和when标签

类似于java的switch,只会满足一个when。如果都不满足,就会使用otherwise标签

<select id="findEmpByCondition2" resultType="emp">
    select * from emp
    <where>
        <choose>
            <when test="empno != null">
                and empno= #{empno}
            </when>
            <when test="ename != null and ename != ''">
                and ename= #{ename}
            </when>
            <when test="job != null and job != ''">
                and job= #{job}
            </when>
            <when test="mgr != null ">
                and mgr= #{mgr}
            </when>
            <when test="hiredate != null ">
                and hiredate= #{hiredate}
            </when>
            <when test="sal != null">
                and sal= #{sal}
            </when>
            <when test="comm != null ">
                and comm =#{comm}
            </when>
            <when test="deptno != null ">
                and deptno= #{deptno}
            </when>
			<otherwise>
            and owner = "admin"
        </otherwise>
        </choose>
    </where>
</select>

set标签

特点:

set标签用于更新语句中
set标签解析为set关键字
set可以去除跟新语句中无用的逗号
通常是和if标签一起使用

<update id="updateEmpByCondtion" >
    update emp
    <set>
        <if test="ename != null and ename != '' ">
            , ename =#{ename}
        </if>
        <if test="job != null and ename != '' ">
            , job =#{job}
        </if>
        <if test="mgr != null ">
            , mgr =#{mgr}
        </if>
        <if test="hiredate != null ">
            , hiredate =#{hiredate}
        </if>
        <if test="sal != null ">
            , sal =#{sal}
        </if>
        <if test="comm != null ">
            , comm =#{comm}
        </if>
        <if test="deptno != null ">
            , deptno =#{deptno}
        </if>
    </set>
    where empno =#{empno}
</update>

trim标签

处理前缀后缀

<update id="updateEmpByCondition2" >
    update emp
    <!--prefix 要增加什么前缀
    prefixOverrides 要去除什么前缀
    suffix 要增加什么后缀
    suffixOverrides 要去除什么后缀
    set 是trim的一种特殊情况
    -->
    <trim prefix="set"  suffixOverrides="," >
        <if test="ename != null and ename != ''">
            ename= #{ename},
        </if>
        <if test="job != null and job != ''">
            job= #{job},
        </if>
        <if test="mgr != null ">
            mgr= #{mgr},
        </if>
        <if test="hiredate != null ">
            hiredate= #{hiredate},
        </if>
        <if test="sal != null">
            sal= #{sal},
        </if>
        <if test="comm != null ">
            comm =#{comm},
        </if>
        <if test="deptno != null ">
            deptno= #{deptno},
        </if>
    </trim>
    where  empno = #{empno}
</update>

示例二:

<select id="findEmpByCondition" resultType="emp">
    select * from emp
        <trim prefix="where" prefixOverrides="and">
            <if test="empno != null">
                and empno= #{empno}
            </if>
            <if test="ename != null and ename != ''">
                and ename= #{ename}
            </if>
        </trim>
</select>

bind标签

一般用于模糊查询,key-value的方式

能够通过bind中的name使用bind中的value

<select id="selectByMap1" parameterType="java.util.Map" resultType="User1">
		<bind name="concatName" value="'%'+username+'%'"></bind>
        <include refid="selectAll"></include>
        where
        username like #{concatName} and
        age in
        <foreach collection="user" item="age" separator="," open=" (" close=")">
            #{age}
        </foreach>
    </select>

sql标签和include标签

sql标签是自定义一段sql语句,通过include标签能够进行复用sql标签中的sql语句

 <!--实现重复代码的重用-->
    <sql id="selectAll">
        select * from user1
    </sql>
<select id="selectForTest1" parameterType="java.util.List" resultType="User1">
        <include refid="selectAll"></include>
        <where>
            age in
            <foreach collection="array" open=" (" close=")" separator="," item="age">
                #{age}
            </foreach>
        </where>
    </select>

foreach标签

通过foreach标签遍历传递进来的数组或集合


<!--List<Emp> findByEmpnos1(int[] empnos);
 collection=""  遍历的集合或者是数组
                 参数是数组,collection中名字指定为array
                 参数是List集合,collection中名字指定为list
 separator=""   多个元素取出的时候 用什么文字分隔
 open=""        以什么开头
 close=""       以什么结尾
 item=""        中间变量名
 for(Person per:PersonList)

 -->
<select id="selectByMap" parameterType="java.util.Map" resultType="User1">
       <include refid="selectAll"></include>
        where age in
        <foreach collection="user" item="age" separator="," open=" (" close=")">
            #{age}
        </foreach>
    </select>

多表查询

一对一、一对多、多对多

一对一

实体类

package com.xqm.model;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {
    private Integer id;

    private String username;

    private Integer age;

    private String sex;

    // private Integer deptNum;
	// 将相关联的depyNum更改为关联的实体类
    private Dept dept;

}

mapper.xml

association是处理一对一关系

<?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.xqm.mapper.EmpMapper">
    <resultMap id="EmpAndDept" type="com.xqm.model.Emp">
        <id column="id" property="id"></id>
        <result property="username" column="username"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <association property="dept" javaType="dept">
            <id column="id" property="id"></id>
            <result property="deptName" column="dept_name"></result>
            <result property="deptContemt" column="dept_contemt"></result>
        </association>
    </resultMap>
    <select id="selectData" resultMap="EmpAndDept">
        select e.*,d.* from emp e inner join dept d on e.dept_num=d.id
    </select>
</mapper>

一对多

一对多就是在实体类中组合一个list。mapper.xml使用collection来处理一对多的关系

实体类

package com.xqm.model;

import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp1 {
    private Integer id;

    private String username;

    private Integer age;

    private String sex;

    List<Dept1> dept1List;

}

接口

package com.xqm.mapper;

import com.xqm.model.Emp1;

import java.util.List;

public interface Emp1Mapper {
   Emp1 selectAll(int id);
}

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="com.xqm.mapper.Emp1Mapper">
  <resultMap id="EmpAndDept" type="com.xqm.model.Emp1">
    <id column="id" property="id"></id>
    <result property="username" column="username"></result>
    <result property="age" column="age"></result>
    <result property="sex" column="sex"></result>
    <collection property="dept1List"  ofType="Dept1" javaType="list">
      <id column="id" property="id"></id>
      <result property="deptName" column="dept_name"></result>
      <result property="deptContemt" column="dept_contemt"></result>
    </collection>
  </resultMap>
  <select id="selectAll" resultMap="EmpAndDept" parameterType="int">
        select e.*,d.* from emp1 e , dept1 d where e.id=d.emp_id and e.id=#{id}
    </select>
</mapper>

多对多

多对多实际上就是一对多+一对一/一对多

实体类

package com.xqm.model;

import java.util.Date;
import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Users {
    private Integer uid;

    private String uname;

    private String sex;

    private Date birthday;

    private String address;

    // 用户和订单是1对多关系
    private List<Orders> ordersList;

}

实体类2

package com.xqm.model;

import java.util.Date;
import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Orders {
    private Integer oid;

    private Integer userid;

    private String orderid;

    private Date createtime;

    private String status;

    // 订单和订单详情是一对多关系
    private List<Orderdetail> orderdetailList;

}

接口

package com.xqm.mapper;

import com.xqm.model.Users;
import com.xqm.util.Page;
import org.apache.ibatis.session.RowBounds;

import java.util.List;

public interface UsersMapper {

//    根据用户编号,查询下面所有的订单、订单详情、商品信息
    List<Users> selectAll(int uid);

    int selectCount();
    List<Users> selectUsersByPage(Page page);
    // 分页插件
    List<Users> selectUsersByRows(RowBounds rb);
}

映射配置文件

collection+association的组合

<?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.xqm.mapper.UsersMapper">
    <resultMap id="getAll" type="com.xqm.model.Users">
        <id column="uid" property="uid"></id>
        <result column="uname" jdbcType="VARCHAR" property="uname"/>
        <result column="sex" jdbcType="VARCHAR" property="sex"/>
        <result column="birthday" jdbcType="DATE" property="birthday"/>
        <result column="address" jdbcType="VARCHAR" property="address"/>
        <collection property="ordersList" ofType="orders" javaType="list">
            <id column="oid" property="oid"></id>
            <result property="status" column="status"></result>
            <result property="createtime" column="createTime"></result>
            <result property="userid" column="userid"></result>
            <result property="orderid" column="orderid"></result>
            <collection property="orderdetailList" ofType="Orderdetail" javaType="list">
                <id column="odid" property="odid"></id>
                <!--<result property="itemid" column="itemid"></result>-->
                <result property="orderid" column="orderid"></result>
                <result property="itemnum" column="itemnum"></result>
                <association property="items" javaType="items">
                    <id column="iid" property="iid"></id>
                    <result property="name" column="name"></result>
                    <result property="detail" column="detail"></result>
                    <result property="price" column="price"></result>
                </association>
            </collection>
        </collection>
    </resultMap>
    <!--
     select u.uname,u.sex,u.address,
          o.orderid,o.status,
          d.itemnum,
          i.name,i.detail,i.price
          from users u
          inner join orders o on u.uid=o.userid
          inner join   orderdetail d  on d.orderid=o.orderid
          inner join  items i on i.iid= d.itemid
          WHERE u.uid=#{uid}

           uid, uname, sex, birthday, address, oid, userid, orderid, createTime, status, odid, orderid, itemid, itemnum, iid, name, detail, price
    -->

    <select id="selectAll" parameterType="int" resultMap="getAll">
          select u.*,o.*,d.*,i.*
          from users u
          inner join orders o on u.uid=o.userid
          inner join   orderdetail d  on d.orderid=o.orderid
          inner join  items i on i.iid= d.itemid
          WHERE u.uid=#{uid}
  </select>

    <select id="selectCount" resultType="int">
        select count(uid) from users
    </select>

    <select id="selectUsersByPage" parameterType="com.xqm.util.Page" resultType="users">
        <bind name="beginIndex" value="(currentPage-1)*pageCount"></bind>
        select * from users order by uid limit #{beginIndex},#{pageCount}

    </select>

    <select id="selectUsersByRows" resultType="users">
        select * from users
    </select>

</mapper>

resultMap属性

property 需要映射到JavaBean 的属性名称。
javaType property的类型,一个完整的类名,或者是一个类型别名。如果你匹配的是一个JavaBean,那MyBatis 通常会自行检测到。
column 数据表的列名或者列别名。
jdbcType column在数据库表中的类型。这个属性只在insert,update 或delete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。
typeHandler 使用这个属性可以覆写类型处理器,实现javaType、jdbcType之间的相互转换。一般可以省略,会探测到使用的什么类型的typeHandler进行处理
fetchType 自动延迟加载
select association、collection的属性,使用哪个查询查询属性的值,要求指定namespace+id的全名称
ofType collection的属性,指明集合中元素的类型(即泛型类型)

延迟加载/懒加载

延迟加载,又称按需加载。 延迟加载的内容等到真正使用时才去进行加载(查询)。多用在关联对象或集合中。

延迟加载的好处:先从单表查询、需要时再从关联表去关联查询,大大降低数据库在单位时间内的查询工作量,将工作在时间上的分配更加均匀,而且单表要比关联查询多张表速度要快。

延迟加载的设置

第一步:全局开关:在mybatis-config.xml中打开延迟加载的开关。配置完成后所有的association和collection元素都生效

<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="true"/>
</settings>

lazyLoadingEnabled :是否开启延迟加载。是Mybatis是否启用懒加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态

aggressiveLazyLoading: 当开启时,任何方法的调用都会懒加载对象的所有属性。否则,每个属性会按需加载,

第二步:分开关:指定的association和collection元素中配置fetchType属性。eager:表示立刻加载;lazy:表示延迟加载。 将覆盖全局延迟设置

缓存

概念

MyBatis允许使用缓存,缓存一般放置在高速读/写的存储器上,比如服务器的内存,能够有效的提供系统性能。MyBatis分为一级缓存和二级缓存,同时也可配置关于缓存设置。

一级存储是SqlSession上的缓存,二级缓存是在SqlSessionFactory(namespace)上的缓存。默认情况下,MyBatis开启一级缓存,没有开启二级缓存。当数据量大的时候可以借助一些第三方缓存框架或Redis缓存来协助保存Mybatis的二级缓存数据。

一级缓存

一级存储是SqlSession上的缓存,默认开启,是一种内存型缓存,不要求实体类对象实现Serializable接口。

缓存中的数据使用键值对形式存储数据

namespace+sqlid+args+offset>>> hash值作为键,查询出的结果作为值

image.png

中间发生了增删改或者是调用了SqlSession调用了**commit,* 会自动清空缓存

 sqlSession.commit();// 增删改的时候调用

二级缓存

二级缓存是以namespace为标记的缓存,可以是由一个SqlSessionFactory创建的SqlSession之间共享缓存数据。默认并不开启。下面的代码中创建了两个SqlSession,执行相同的SQL语句,尝试让第二个SqlSession使用第一个SqlSession查询后缓存的数据。要求实体类必须实现序列化接口

image.png

接口

public interface EmpMapper {
    Emp findByEmpno(int empno);
}

mapper

<mapper namespace="com.msb.mapper.EmpMapper">
    <cache/>

    <select id="findByEmpno" resultType="emp" useCache="true" flushCache="false">
        select * from emp where empno =#{empno}
    </select>

</mapper>

测试代码

package com.msb.test;

import com.msb.mapper.EmpMapper;
import com.msb.pojo.Emp;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;

/**
 * @Author: Ma HaiYang
 * @Description: MircoMessage:Mark_7001
 */
public class Test3 {

    private SqlSession sqlSession;
    private SqlSession sqlSession2;
    @Before
    public void init(){
        SqlSessionFactoryBuilder ssfb =new SqlSessionFactoryBuilder();
        InputStream resourceAsStream = null;
        try {
            resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
        SqlSessionFactory factory=ssfb.build(resourceAsStream) ;
        sqlSession=factory.openSession();
        sqlSession2=factory.openSession();
    }


    @Test
    public void testFindDeptByDetpno()   {
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp = mapper.findByEmpno(7521);
        System.out.println(emp);
        // SqlSession提交之后,才会将查询的结果放入二级缓存
        sqlSession.commit();


        EmpMapper mapper2 = sqlSession2.getMapper(EmpMapper.class);
        Emp emp2 = mapper2.findByEmpno(7521);
        System.out.println(emp2);


    }



    @After
    public void release(){
        // 关闭SQLSession
        sqlSession.close();
        sqlSession2.close();
    }

}

注意其中的commit(),执行该命令后才会将该SqlSession的查询结果从一级缓存中放入二级缓存,供其他SqlSession使用。另外执行SqlSession的close()也会将该SqlSession的查询结果从一级缓存中放入二级缓存。两种方式区别在当前SqlSession是否关闭了。

执行结果显示进行了两次对数据库的SQL查询,说明二级缓存并没有开启。需要进行如下步骤完成开启。

1.全局开关:在mybatis-config.xml文件中的<settings>标签配置开启二级缓存

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

2.分开关:在要开启二级缓存的mapper文件中开启缓存

<mapper namespace="com.msb.mapper.EmployeeMapper">
    <cache/>
</mapper>

3.二级缓存未必完全使用内存,有可能占用硬盘存储,缓存中存储的JavaBean对象必须实现序列化接口,

public class Emp implements  Serializable {  }

经过设置后,查询结果如图所示。发现第一个SqlSession会首先去二级缓存中查找,如果不存在,就查询数据库, 在commit()或者close()的时候将数据放入到二级缓存 。第二个SqlSession执行相同SQL语句查询时就直接从二级缓存中获取了。

注意:

  1. MyBatis的二级缓存的缓存介质有多种多样,而并不一定是在内存中,所以需要对JavaBean对象实现序列化接口。
  2. 二级缓存是以 namespace 为单位的,不同 namespace 下的操作互不影响
  3. 加入Cache元素后,会对相应命名空间所有的select元素查询结果进行缓存,而其中的insert、update、delete在操作是会清空整个namespace的缓存。
  4. cache 有一些可选的属性 type, eviction, flushInterval, size, readOnly, blocking。
<cache type="" readOnly="" eviction=""flushInterval=""size=""blocking=""/>
属性 含义 默认值
type 自定义缓存类,要求实现org.apache.ibatis.cache.Cache接口 null
readOnly 是否只读true:给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。false:会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全 false
eviction 缓存策略LRU(默认) – 最近最少使用:移除最长时间不被使用的对象。FIFO – 先进先出:按对象进入缓存的顺序来移除它们。SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。 LRU
flushInterval 刷新间隔,毫秒为单位。默认为null,也就是没有刷新间隔,只有执行update、insert、delete语句才会刷新 null
size 缓存对象个数 1024
blocking 是否使用阻塞性缓存BlockingCachetrue:在查询缓存时锁住对应的Key,如果缓存命中了则会释放对应的锁,否则会在查询数据库以后再释放锁,保证只有一个线程到数据库中查找指定key对应的数据false:不使用阻塞性缓存,性能更好 false

5.如果在加入Cache元素的前提下让个别select 元素不使用缓存,可以使用useCache属性,设置为false。useCache控制当前sql语句是否启用缓存flushCache控制当前sql执行一次后是否刷新缓存

<select id =“findByEmpno” resultType =“emp” useCache =“true” flushCache =“false” >

逆向工程

generatorConfig.xml

<!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="simple" targetRuntime="MyBatis3">
        <!-- 注释 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true"/><!-- 是否取消注释 -->
            <property name="suppressDate" value="false"/> <!-- 是否生成注释代时间戳-->
        </commentGenerator>

        <!--指向数据库连接-->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://124.223.81.248:3307/mybatis?serverTimezone=UTC"
                        userId="root"
                        password="xqm@123456"
        />

        <!--生成对应的实体类
        targetPackage:指定生成java文件的目录
        targetProject:放在那个工程的哪个目录下
        -->
        <javaModelGenerator targetPackage="com.xqm.model" targetProject="src/main/java"/>

        <!--SQL映射文件生成器
        targetPackage:指定生成java文件的目录
        targetProject:放在那个工程的哪个目录下
        -->
        <sqlMapGenerator targetPackage="mappers" targetProject="src/main/resources"/>

        <!--dao接口生成器-->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.xqm.mapper" targetProject="src/main/java"/>

        <!--指定要逆向生成的数据表
        tableName:表名
        domainObjectName:对象名
        -->
        <table tableName="items" domainObjectName="Items" enableCountByExample="false" enableDeleteByExample="false" enableUpdateByExample="false" selectByExampleQueryId="false" enableSelectByExample="false"/>
        <table tableName="orderdetail" domainObjectName="Orderdetail" enableCountByExample="false" enableDeleteByExample="false" enableUpdateByExample="false" selectByExampleQueryId="false" enableSelectByExample="false"/>
        <table tableName="orders" domainObjectName="Orders" enableCountByExample="false" enableDeleteByExample="false" enableUpdateByExample="false" selectByExampleQueryId="false" enableSelectByExample="false"/>
        <table tableName="users" domainObjectName="Users" enableCountByExample="false" enableDeleteByExample="false" enableUpdateByExample="false" selectByExampleQueryId="false" enableSelectByExample="false"/>
        <!--<table tableName="dept" domainObjectName="Dept" enableCountByExample="false" enableDeleteByExample="false" enableUpdateByExample="false" selectByExampleQueryId="false" enableSelectByExample="false"/>-->
    </context>
</generatorConfiguration>

springboot集成mybatis

pom.xml

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.shopping</groupId>
    <artifactId>manage</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>manage</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
        <!--mybatis自动生成代码插件,用完必须注释掉-->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>

        <!--引入mybatis分页插件  排除mybatis 否则会出现循环依赖的问题-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.4.2</version>
            <exclusions>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis-spring</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-crypto</artifactId>
            <version>5.6.2</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.11.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <!--MyBatis自动生成代码的插件和配置-->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.6</version>
                <configuration>
                    <!-- 在控制台打印执行日志 -->
                    <verbose>true</verbose>
                    <!-- 重复生成时会覆盖之前的文件-->
                    <overwrite>true</overwrite>
                    <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

server:
  port: 8001
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://124.223.81.248:3307/mall_system?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: xqm@123456
mybatis:
#  config-location:
  type-aliases-package: com.shopping.manage.model
  mapper-locations: classpath:mapper/*.xml
  configuration:
#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 驼峰命名法
    map-underscore-to-camel-case: true
#logging:
#  level:
#    com.xqm.mybatis.mapper: debug
#  file:
#    name: log.txt
pagehelper:
  auto-dialect: true
  helper-dialect: mysql
  reasonable: true
  support-methods-arguments: true
  params: count=countSql

file:
   localhost: E:\

运行逆向工程

maven-Plugins-mybatis-generator-mybatis-generator:generate-右击选择Run ‘manage[mybatis-gene…]’

image.png

Application文件

在启动类上新增@MapperScan(basePackages = “com.shopping.manage.mapper”)

package com.shopping.manage;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan(basePackages = "com.shopping.manage.mapper")
public class ManageApplication {

    public static void main(String[] args) {
        SpringApplication.run(ManageApplication.class, args);
    }

}