一、理论

1、历史

1)MyBatis是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation 迁移到了Google Code,随着开发团队转投Google Code旗下, iBatis3.x 正式更名为MyBatis ,代码于2013年11月迁移到Github
2)iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。 iBatis 提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
3) 什么是持久层框架?
看到持久层—> 保存到磁盘的技术。
持久层的框架很多很多: jdbc—>jdbcTemplate—>mybatis\jpa\Hibernate.…..

2、简介

1) MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架
2) MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
3) MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录
4) Mybatis 是一个半自动的ORM(Object Relation Mapping)框架,Hibernate 完全的ORM
数据库 ——-映射关系——- java实体
因为mybatis 还需要编写sql语句才能操作数据库,Hibernate无需操作数据库,直接操作java代码即可实现增删改查 from User

3、为什么选择MyBatis?

越接近底层的技术,运行速度越快,编写的代码越困难
JDBC —-> Hibernate (封装的有点狠)
随着互联网的发展,我们要求编码速度要快,运行速度也要快,就选择了一个折中的方案—MyBatis
随着MyBatis越来越流行,开发人员觉得MyBatis也有一些编码上的问题—-MyBatis-Plus

4、如何下载?

https://github.com/mybatis/mybatis-3/
该网站是全球最大的代码托管网站,现在由于一些技术封锁的原因,国内支持不是特别好,但是国内也诞生了一个类似的网站—码云。
目前所有项目都使用mavan创建了,所以jar包我们也不需要下载了,直接上Maven坐标。

二、入门案例

mybatis编写方式有很多,很灵活,今天所有同学都以我这个为准。
第一步:新建一个java的maven项目。打包方式写成 jar
第二步:导入依赖的jar包

  1. <dependency>
  2. <groupId>org.mybatis</groupId>
  3. <artifactId>mybatis</artifactId>
  4. <version>3.4.5</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>mysql</groupId>
  8. <artifactId>mysql-connector-java</artifactId>
  9. <version>5.1.6</version>
  10. <scope>runtime</scope>
  11. </dependency>
  12. <dependency>
  13. <groupId>log4j</groupId>
  14. <artifactId>log4j</artifactId>
  15. <version>1.2.12</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>junit</groupId>
  19. <artifactId>junit</artifactId>
  20. <version>4.12</version>
  21. <scope>test</scope>
  22. </dependency>

第三步:导入log4j的配置文件,因为我们将来想在控制台打印日志
在resources 文件夹下,放log4j.properties
关于Log4J的内容说明
五种日志级别 debug info warn error fatal 级别依次提高,级别越低,打印出来的日志越多
image.png
第四步:准备一些表 hqll 数据库中customer表
第五步:编写Customer对应的实体 POJO

package com.qfedu.pojo;

import java.util.Date;

public class Customer {
    private int id;
    private String name;
    private String phone;
    private String email;
    private String level;
    private Date createTime;
    private Date endTime;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getLevel() {
        return level;
    }

    public void setLevel(String level) {
        this.level = level;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getEndTime() {
        return endTime;
    }

    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", phone='" + phone + '\'' +
                ", email='" + email + '\'' +
                ", level='" + level + '\'' +
                ", createTime=" + createTime +
                ", endTime=" + endTime +
                '}';
    }
}

第六步:创建一个全局的配置文件(mybatis 有两种配置文件,一种是总配置,一种是小配置文件)
创建一个SqlMapConfig.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>
    <!--配置数据库连接的环境-->
    <environments default="development">
        <environment id="development">
            <!--使用jdbc默认的事务-->
            <transactionManager type="JDBC"></transactionManager>
            <!--使用默认的数据库连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/hqll"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

第七步:创建接口文件
com.qfedu.mapper包下,创建CustomerMapper.java 接口

package com.qfedu.mapper;

import com.qfedu.pojo.Customer;

public interface CustomerMapper {

    public Customer getCustomerById(int id);
}

第八步:创建小配置文件— CustomerMapper.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.qfedu.mapper.CustomerMapper">
    <select id="getCustomerById" resultType="com.qfedu.pojo.Customer">
        select * from customer where id=#{id}
    </select>
</mapper>

第九步:将小配置文件,告知给主配置文件
image.png
第十步:测试

@Test
    public void testSelect01() throws IOException {
        //测试mybatis
        String resource="SqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sessionFactory.openSession();
        CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
        Customer customer = customerMapper.getCustomerById(14);
        System.out.println(customer);
    }

以上方案—mapper接口编程,讲究以下几点照样:
1、接口名称和xml的配置文件名称一致,路径一致。
image.png
2、CustomerMapper.xml中的namespace 必须是接口的全路径
3、sql 中的id 必须是接口的方法名
4、SQL中的resultType必须和方法名的返回值一致。
image.png

三、一些细节上的配置

1、如果配置了log4j 日志不输出


<settings>
<setting name=”logImpl” value=”log4j”/>
</settings>
发现:SqlMapConfig.xml中各种标签是有先后书写顺序的,一不留神就报错。

2、能不能给各个实体起别名?

<!--起别名-->
    <typeAliases>
        <!--给某个实体起别名-->
       <!--<typeAlias type="com.qfedu.pojo.Customer" alias="customer"/>-->
        <!--指定一个包,该包下的所有的实体全部起别名,别名就是自己的类名,不区分大小写-->
        <!--
           Customer   customer
                      cUstomer
        -->
        <package name="com.qfedu.pojo"/>
    </typeAliases>

mybatis自身也给一个数据类型起了别名,别名如下:
image.png
3、每次编写一个mapper .xml都需要在SqlMapConfig.xml中进行报备,能不能有种简写的方式

<mappers>
        <!--告知主配置文件,我的配置文件在哪里-->
        <!--指定一个配置文件-->
        <!--<mapper resource="com/qfedu/mapper/CustomerMapper.xml"/>-->
        <!--指定一个包名,该包名下所有的xxxxMapper.xml全部加载进来-->
        <package name="com.qfedu.mapper"/>
    </mappers>

4、之前我们使用jdbc.properties来配置数据源,现在能够还使用这种方式呢?

<properties resource=”db.properties”/>
<environments default=”development”>
<environment id=”development”>

<transactionManager type=”JDBC”></transactionManager>

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

总结一个比较全的SqlMapConfig.xml
标签中的顺序如下: properties < settings < typeAliases < environments < mappers
官网给出的比较全的顺序:
image.png
以上顺序不需要记忆,当你需要写的时候,如果报错,换个位置,直到不报错就可以了。

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

    <!--通过该标签,将db.properties文件给加载过来-->
    <properties resource="db.properties"/>

    <!--解决一些同学控制台日志不打印的问题-->
    <settings>
        <setting name="logImpl" value="log4j"/>
    </settings>

    <!--起别名-->
    <typeAliases>
        <!--给某个实体起别名-->
       <!--<typeAlias type="com.qfedu.pojo.Customer" alias="customer"/>-->
        <!--指定一个包,该包下的所有的实体全部起别名,别名就是自己的类名,不区分大小写-->
        <!--
           Customer   customer
                      cUstomer
        -->
        <package name="com.qfedu.pojo"/>
    </typeAliases>
    <!--配置数据库连接的环境-->
    <environments default="development">
        <environment id="development">
            <!--使用jdbc默认的事务-->
            <transactionManager type="JDBC"></transactionManager>
            <!--使用默认的数据库连接池-->
            <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>



    <mappers>
        <!--告知主配置文件,我的配置文件在哪里-->
        <!--指定一个配置文件-->
        <!--<mapper resource="com/qfedu/mapper/CustomerMapper.xml"/>-->
        <!--指定一个包名,该包名下所有的xxxxMapper.xml全部加载进来-->
        <package name="com.qfedu.mapper"/>
    </mappers>

</configuration>

四、通过MyBatis实现增删改查

1、查询

public interface CustomerMapper {

    Customer getCustomerById(int id);
    List<Customer> selectAll();
}
<select id="getCustomerById" resultType="customer">
        select * from customer where id=#{id}
    </select>
    <select id="selectAll" resultType="customer">
       select * from customer
    </select>

2、新增

先创建一个接口
int saveCustomer(Customer customer);
编写xml配置文件

image.png
<insert id=”saveCustomer” parameterType=”customer”>
insert into customer(name,phone,email,level) values(#{name},#{phone},#{email},#{level})
</insert>
此接口,返回值是int类型,我们无需在xml中管这个返回值,默认就有的,修改和删除也是一样的。

3、修改

int updateCustomerById(Customer customer);
<update id=”updateCustomerById” parameterType=”customer”>
update customer set name=#{name},phone=#{phone},email=#{email},level=#{level}
where id=#{id}
</update>

4、删除

int deleteById(int id);
<delete id=”deleteById” parameterType=”int”>
delete from customer where id=#{id}
</delete>

总结:
1、新增 insert 修改 update 删除 使用delete 标签 查询使用 select 标签
2、新增,修改,删除都需要进行事务的提交才能更新数据。
3、新增,修改,删除 都默认会有一个int类型的返回值,如果想使用这个返回值只需要接口中返回值是int即可。
4、parameterType 就是接口中的参数,类型一定要一致
5、resultType 就是接口中的返回值类型,如果接口的返回值是List集合,此处不需要写成List,而是写list集合中泛型的类型

5、如何获取自增的id值

如果主键是自增的,我如何保存完数据之后就知道该数据在数据库中的id是多少呢?
如何在新增完数据之后,就能拿到新数据在数据库中的id值呢,需要在配置文件中多如下配置:

select last_insert_id()

原理是通过执行mysql 中的一个内置函数 last_insert_id() 查询到最后一个数据的id.
keyColumn 指的是数据库中的字段 id
keyProperty 指的是实体中的字段 id
resultType 返回值类型 int
order 只有两个值 BEFORE AFTER 因为id是数据保存之后才会得出一个的id,所有类型是After

int insertCustomer(Customer customer);
xml配置中多了一段配置:

<insert id="insertCustomer" parameterType="customer">
        <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
            select last_insert_id()
        </selectKey>
        insert into customer(name,phone,email,level) values(#{name},#{phone},#{email},#{level})
    </insert>

测试代码:

@Test
    public void testInsert2() throws IOException {

        SqlSession sqlSession = sessionFactory.openSession();
        CustomerMapper customerMapper = sqlSession.getMapper(CustomerMapper.class);
        Customer customer = new Customer();
        customer.setName("老闫");
        customer.setPhone("18137884406");
        customer.setEmail("838700991@qq.com");
        customer.setLevel("年卡会员");
        int result = customerMapper.insertCustomer(customer);
        System.out.println(result);
        System.out.println("保存数据之后,新数据在数据库中的id="+customer.getId());
        //记得提交事务,否则数据不保存
        sqlSession.commit();
        sqlSession.close();
    }

五、查询数据时数据库中的字段和实体中的字段不一致,查询不出来,怎么解决?
Customer{id=14, name=’刘晓磊’, phone=’18634455554’, email=’343223ddddd2@qq.com’, level=’年卡用户’, createTime=null, endTime=null}
需要使用resultMap == 专门解决实体中的属性和数据库中的字段不一致的问题

<resultMap id="customerMap" type="customer">
        <!--id 表示该列是一个主键-->
        <id property="id" column="id"></id>
        <result property="createTime" column="create_time"/>
        <result property="endTime" column="end_time"/>
    </resultMap>

1、resultMap中的id一般随便写,type就是实体的全路径,可以使用别名。
2、里面的标签 主键使用id标签,普通字段使用 result标签
3、标签里面的property 指的是实体中的属性,column指的是数据库中的字段。
4、只需要编写属性和列不一致的字段即可,无需都写出来

使用的时候,记得resultType 换成 resultMap即可
image.png
测试查询出来的结果如下:
Customer{id=14, name=’刘晓磊’, phone=’18634455554’, email=’343223ddddd2@qq.com’, level=’年卡用户’, createTime=Thu Sep 09 16:04:53 CST 2021, endTime=null}

如果插入数据出现乱码,记得添加如下参数: useUnicode=true&characterEncoding=UTF-8
image.png