2020:0602--mybatis(一)

288 阅读12分钟

mybatis框架(共四天):大纲

1 第一天:mybatis入门
        mybatis的概述
        mybatis的环境搭建
        mybatis入门案例
        自定义mybatis框架(主要是为了了解mybatis中执行细节)
    
2 第二天:mybatis基本使用
        mybatis的单标CRUD操作
        mybatis的参数和返回值
        mybatis的dao编写
        mybatis配置的细节
            几个标签的使用
        
3 第三天:mybatis的深入和多表
        mybatis的连接池
        mybatis的事务控制及设计的方法
        mybatis的多表查询
            一对多(多对一)
            多对多

4 第四天:mybatis的缓存和注解开发
        mybatis中的加载时机(查询的时机)
        mybatis中的一级缓存,二级缓存
        mybatis的注解开发
            单表CRUD
            多表查询

mybatis框架:第一天

1、框架概述

1.1 什么是框架?

    * 通俗一点说,它是我们软件开发中的一套解决方法,不同的框架解决的是不同的问题。
    * 使用框架的好处:
        框架封装了很多的细节,使开发者可以使用极简的方式实现功能,大大提高开发效率。
    * 不同的框架解决不同的问题。------> 提一下三层架构。
            mybatis框架:持久层框架,是和数据库交互的
            SpringMVC框架:表现层框架。

2.三层架构
    表现层:
        是用来展示数据的。
    业务层:
        是处理也无需求的。
    持久层:
        是和数据库交互的。
        
3.持久层技术解决方案
    JDBC:涉及到三个对象
        Connection
        PreparedStatement
        ResultSet
    Spring的JdbcTemplate:
        Spring中对jdbc的简单封装
    Apache的DBUtils:
        它和Spring的JdbcTemplate很像,也是对Jdbc的简单封装
    
    以上这些都不是矿建:
        *JDBC是规范
        *Spring的JdbcTemplate和Apache的DBUtils都只是工具类。
            是对这个规范的一个实现,一个简单的封装,不算是框架。只是一个使用的封装,并没有一
            套完整的解决方案,封装的还不够细致。即我们使用中还需要处理很多事情,达不到框架使
            开发者用极简的方式实现功能,大大提高开发效率。
           
4 .mybatis的概述
    mybatis是一个持久层框架,用java编写的。
    
    它封装了jdbc操作的很多细节,使开发者只需关注sql语句本身,而无需关注注册驱动,创建
    连接等繁杂过程。
    
    它使用了ORM思想实现了结果集的封装。
        * ORM:Object Relational Mapping 对象关系映射
            简单的说:就是把数据库表和实体类及实体类的属性对应起来
            让我们可以操作实体类就可以操作数据库表。

            user(表)          User(类)
            id                  userId
            user_name           userName
    
    今天我们需要做到:
        实体类中的属性和数据库表中的字段名称保持一致
            user(表)          User(类)
            id                  id
            user_name           user_name 
            表中的列名就是实体类中的字段名称。

1.2、JDBC编程分析

* 1.代码:
```
public static void main(String[] args) { 

    Connection connection = null; 
    PreparedStatement preparedStatement = null; 
    ResultSet resultSet = null; 
    
    try { 
    //加载数据库驱动 
    Class.forName("com.mysql.jdbc.Driver"); 
    //通过驱动管理类获取数据库链接 
    connection = DriverManager.getConnection
        ("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8","root", "root"); 
        
    //定义sql语句 ?表示占位符 
    String sql = "select * from user where username = ?";
    
    //获取预处理
    statement preparedStatement = connection.prepareStatement(sql);
    
    //设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值 
    preparedStatement.setString(1, "王五");
    
    //向数据库发出sql执行查询,查询出结果集 
    resultSet = preparedStatement.executeQuery(); 
    
    //遍历查询结果集 
    while(resultSet.next()){ 
        System.out.println(resultSet.getString("id")+"
            "+resultSet.getString("username")); 
        } 
    } catch (Exception e) { 
        e.printStackTrace(); 
    }finally{ 
        //释放资源 
        if(resultSet!=null){ 
            try { 
                resultSet.close(); 
            } catch (SQLException e) { 
                e.printStackTrace(); 
            } 
        } 
        if(preparedStatement!=null){ 
            try { 
                preparedStatement.close(); 
            } catch (SQLException e) { 
                e.printStackTrace(); 
            } 
        } 
        if(connection!=null){ 
            try { 
                connection.close(); 
            } catch (SQLException e) { 
                // TODO Auto-generated catch block 
                e.printStackTrace(); 
            } 
        } 
    } 

```

2、mybatis的入门

2.1 mybatis的环境搭建

2.1.0 数据库建表
CREATE DATABASE	txl_mybatis;

USE txl_mybatis;
CREATE TABLE `user` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(32) NOT NULL COMMENT '用户名称',
  `birthday` DATETIME DEFAULT NULL COMMENT '生日',
  `sex` CHAR(1) DEFAULT NULL COMMENT '性别',
  `address` VARCHAR(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY  (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

INSERT  INTO `user`(`id`,`username`,`birthday`,`sex`,`address`) VALUES (41,'老王','2018-02-27 17:47:08','男','北京'),(42,'小二王','2018-03-02 15:09:37','女','北京金燕龙'),(43,'小二王','2018-03-04 11:34:34','女','北京金燕龙'),(45,'传智播客','2018-03-04 12:04:06','男','北京金燕龙'),(46,'老王','2018-03-07 17:37:26','男','北京'),(48,'小马宝莉','2018-03-08 11:44:00','女','北京修正');

2.1.1 创建maven工程
创建mybatis01的工程,工程信息如下。
Groupid :cn.itcast
ArtifactId : day0602_mybatis01
Packing : jar
2.1.2 添加Mybatis坐标和其他jar包依赖
```
    <dependencies>
        <!--导入mybatis的坐标-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.4</version>
        </dependency>
        <!--因为是数据库的操作还需要:添加mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
            <scope>runtime</scope>
        </dependency>
        <!--日志坐标-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <!--导入JUNIT依赖-->
            <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>4.12</version>
              <scope>test</scope>
            </dependency>
    </dependencies>
```
2.1.3 编写User实体类
package cn.itcast.domain;

import java.io.Serializable;
import java.util.Date;

public class User implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    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 Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
2.1.4 编写持久层接口IUserDao
    package cn.itcast.dao;
    
    import cn.itcast.domain.User;
    
    import java.util.List;
    
    /**
     * 用户的持久层接口
     */
    public interface IUserDao {
    
        /**
         * 查询所有
         * @return
         */
        List<User> findAll();
    }
2.1.5 编写持久层接口的映射文件IUserDao.xml
*要求:
    创建位置:必须和持久层接口在相同的包中。
    名称:必须以持久层接口名称命名文件名。扩展名为.xml

*持久层接口的映射文件IUserDao.xml: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">
    
    <!--dao的全限定类名-->
    <mapper namespace="cn.itcast.dao.IUserDao">
        <!--配置查询所sql-->
        <!--操作配置:因为是查询,select标签;id是dao中的方法名-->
        <select id="findAll" resultType="cn.itcast.domain.User">
            select * from user
        </select>
    </mapper>
```
2.1.6 编写SqlMapConfig.xml配置文件:Config的约束
* 在resources目录下配置

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

<!--这些都是和数据库相关的,和环境相关的:它是mybatis的主配置文件-->
<configuration>
    <!--配置环境:mybatis的主配置文件-->
    <environments default="mysql">
        <!--配置mysql的环境-->
        <environment id="mysql">
            <!--配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置数据源(连接池)-->
            <dataSource type="POOLED">
                <!--配置连接数据库的4个基本信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/txl_mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <!--指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件-->
    <mappers>
        <mapper resource="cn/itcast/dao/IUserDao.xml"></mapper>
    </mappers>
</configuration>
```
2.1.7 mybatis的环境搭建步骤小结
1.创建maven工程,并导入坐标依赖
2.创建实体类和dao接口
3.创建Mybatis的主配置文件
    SqlMapConfig.xml
4.创建映射配置文件
    IUserDao.xml

* 环境搭建做完了。
2.1.8 mybatis的环境搭建的注意事项
1.创建IUserDao.xml 和 IUserDao.java时名称是为了和我们之前的知识保持一致。在
Mybatis中它把持久层的操作接口名称和映射文件也叫做:
    Mapper
    所以IUserDao也可以叫做IUserDaoMapper是一样的。
    
2.在IDEA创建目录时,它和包的创建是不一样的
    创建包时:cn.itcast.dao是三级目录
    目录在创建时:cn.itcast.dao是一级目录

3.Mybatis的映射配置文件位置必须和dao接口的包结构相同

4.映射配置文件的mapper标签属性namespace属性必须是dao接口的全限定类名。
5.映射配置文件的操作配置,id属性的取值必须是dao接口的方法名
2.1.8 mybatis的环境搭建的为什么有这些强制要求?有什么好处呢?
当我们遵从了3.4.5点之后,我们在开发中就无须再写dao的实现类。接口的功能实现
由mybatis来为我们实现。

2.2 mybatis的入门案例

```
public class MybatisTest {
/**
 * 入门案例(没写dao实现类)
 * @param args
 */
public static void main(String[] args) throws Exception {
    //1.读取配置文件:为了将SqlMapConfig中的信息加载出来
    InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");

    //2.创建SqlSessionFactory工厂
    //它是一个接口,我们用这个SqlSessionFactory工厂,无外乎就是想要创建它的对象。
    // 于是Mybatis给我们提供这个工厂的时候,把工厂的创建细节给省略了,
    // 为我们准备了SqlSessionFactoryBuilder这个对象,我们用这个对象来构建工厂
    // 意思就是:怎么解析配置文件,和我们无关了
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(in);


    //3.使用工厂生产一个SqlSession对象:为我们提供dao的实现
    SqlSession session = factory.openSession();


    //没有接口的具体实现,所以我们要想一个办法将它弄出来
    //不改变源码的基础上,对已有方法增强---->动态代理
    //4.使用SqlSession创建Dao接口的代理对象
    IUserDao userDao = session.getMapper(IUserDao.class);

    //5.使用代理对象执行方法
    List<User> users = userDao.findAll();
    for (User user : users) {
        System.out.println(user);
    }

    //6.释放资源:session对象,in对象
    session.close();
    in.close();
}

} ```

2.2.2 mybatis的入门步骤小结
1.读取配置文件
    InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
    
2.创建SqlSessionFactory工厂
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(in);
    
3.创建SqlSession对象
    SqlSession session = factory.openSession();

4.创建Dao接口的代理对象
    IUserDao userDao = session.getMapper(IUserDao.class);

5.执行dao中的方法
    List<User> users = userDao.findAll();
    
6.释放资源
    session.close();
    in.close();
    
注意:
    * 不要忘了在映射配置(持久层接口的映射文件IUserDao.xml)中告知mybatis要封
      装到哪个实体类。
      配置的方式:指定实体类的全限定类名 resultType="cn.itcast.domain.User"
        ```
        <select id="findAll" resultType="cn.itcast.domain.User">
            select * from user;
        </select>
        ```
    
报错:找不到或无法加载主类
    * 情况一:
        idea本身缓存的问题:IDEA-》file-》invalidate Cache/restart;
    * 情况二:
        查看structure→modules,其中会包含很多的main,删除掉就行

3、mybatis的框架概述

    mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需
关注sql语句本身,而不需要花费精力去处理加载驱动,创建连接,创建statement
等繁杂的过程。
    mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对
象和statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架
执行sql并将结果映射为java对象并返回。
    持久层接口的映射文件IUserDao.xml有这样一段配置:
    <select id="findAll" resultType="cn.itcast.domain.User">
        select * from user;
    </select>
    采用ORM思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc 
api底层访问细节,是我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。

3.1 mybatis基于注解方式的案例--annotation

1. 主配置文件:SqlMapConfi.xml保留,
   且修改指定指定映射文件的位置为指定配注解的dao位置
   ```
      <mappers>
        <!--<mapper resource="cn/itcast/dao/IUserDao.xml"></mapper>-->
        <mapper class="cn.itcast.dao.IUserDao"></mapper>
    </mappers>
   ```
2. 持久层接口的映射文件:IUserDao.xml不需要

3. 在持久层接口加上注解:@Select("select * from user")
    @Select,并且指定sql语句。
    ```
    public interface IUserDao {
    
        @Select("select * from user")
        List<User> findAll();
    }
    ```
    
4. 虽然可以使用实现类,但是在开发中越简单越好,随意都是不采用dao实现类的方式,
   不管使用xml还是注解配置。
   尽管mybatis它是支持写dao实现类的。

3.2 不改变原来的方式,来看看dao实现类怎么用

写dao实现类也能实现功能,但是没有意义,增加开发时间。

为了说清楚:namespace和id的作用,我们做这个演示。
```
        <mapper namespace="cn.itcast.dao.IUserDao">
            <select id="findAll" resultType="cn.itcast.domain.User">
                    select * from user
            </select>
        </mapper>
```
    因为光靠id无法定位到IUserDao.xml中的Sql语句,需要加上namespace才能找到
sql语句;同理mybatis也要通过namespace和id找到这个sql语句。

* 具体代码演示:参见day0602_mybatis03_daoimpl

4、了解mybatis在应用中的各个过程。

* mybatis入门案例中,设计模式的分析

4.1 mybatis在基于代理dao实现功能(增删改查)的过程中,干了什么事。

4.1.1 组装selectList方法

    * 我们在DAO实现类方式中用过该方法
      List<User> users = session.selectList("cn.itcast.dao.IUserDao.findAll");

* 这就是整个:
    List<User> users = session.selectList("cn.itcast.dao.IUserDao.findAll");所做的事情。
    要想让selectList()方法执行,我们需要给方法提供两个信息:
    1.连接信息
    2.映射信息:两个部分
	第一:执行的sql语句
	第二:封装结果的实体类的全限定类名。
        我们要将这两个信息组合到一起封装,所以说把这两个信息组合起来定义成一个对象(Mapper)

    现在我们要组装的信息,第一部分selectList就组装完了。

4.1.2 创建代理对象

    我们通过一个方法实现了代理对象的创建   
    //4.使用SqlSession创建Dao接口的代理对象
    IUserDao userDao = session.getMapper(IUserDao.class);

4.1.3 在代理对象中调用selectList方法

5、自定义mybatis

我们不导入mybatis的依赖,自定义一个mybatis

5.1 自定义mybatis能通过入门案例看到的类

package org.apache.ibatis.io;           class Resources
package org.apache.ibatis.session;      class SqlSessionFactoryBuilder
package org.apache.ibatis.session;      interface SqlSessionFactory
package org.apache.ibatis.session;      interface SqlSession extends Closeable
这些是我们要自定义的

* 详见项目day0602_mybatis_design