MyBatis入门

1. MyBatis 框架概述

MyBatis 是一个持久层框架,它内部封装了 JDBC,使开发者只需要关注 sql 语句本身,而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。

2. ORM 思想

MyBatis 使用了 ORM 思想实现了结果集的封装

ORM(Object Relational Mapping):把数据库表和实体类及实体类的属性对应起来,让我们可以操作实体类就实现操作数据库表

例如:

数据库表的字段实体类的属性
idid
user_nameuser_name

注意:实体类中的属性和数据库表的字段名称保持一致。

3. MyBatis 入门

3.1 MyBatis 的入门案例(基于 XML 配置文件)

  1. 创建 Maven 的 Java工程,工程信息如下:

    <groupId>com.zt</groupId>
    <artifactId>mybatis_day01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    
  2. 添加 Mybatis3.4.5 的坐标

    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>
    
  3. 编写 User 实体类

    package com.zt.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 + '\'' +
                    '}';
        }
    }
    
    
  4. 编写持久层接口 UserDao

    package com.zt.dao;
    
    import com.zt.domain.User;
    
    import java.util.List;
    
    /**
     * 用户持久层接口
     */
    public interface UserDao {
        /**
         * 查询所有用户
         * @return
         */
        public List<User> findAll();
    }
    
    
  5. 在 resources 包中编写持久层接口的映射文件 UserDao.xml

    创建要求:

    1. 必须和持久层接口在相同的包中。
    2. 必须以持久层接口名称命名文件名,扩展名是 .xml

    如图:

    在这里插入图片描述

    代码如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.zt.dao.UserDao">
        <!--配置查询所有用户-->
        <select id="findAll" resultType="com.zt.domain.User">
            select * from user
        </select>
    </mapper>
    

    注意:id 属性的取值必须是 dao 接口的方法名,resultType 是查询完后封装的返回类型

  6. 在 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">
    
    <configuration>
        <!-- 配置 mybatis 的环境 -->
        <environments default="mysql">
            <!-- 配置 mysql 的环境 -->
            <environment id="mysql">
                <!-- 配置事务的类型 -->
                <transactionManager type="JDBC"></transactionManager>
                <!-- 配置连接数据库的信息:用的是数据源(连接池) -->
                <dataSource type="POOLED">
                    <!--配置连接数据库的四个基本信息-->
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis_day01"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
    
        <!-- 告知 mybatis 映射配置的位置 -->
        <mappers>
            <mapper resource="com/zt/dao/UserDao.xml"/>
        </mappers>
    
    </configuration>
    
  7. 编写测试类

    package com.zt.test;
    
    import com.zt.dao.UserDao;
    import com.zt.domain.User;
    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 java.io.InputStream;
    import java.util.List;
    
    /**
     * 入门案例
     */
    public class MybatisTest {
        public static void main(String[] args) throws Exception {
            // 1.读取配置文件
            InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
            // 2.创建 SqlSessionFactory 的构建者对象
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            // 3.使用构建者创建工厂对象 SqlSessionFactory
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
            // 4.使用 SqlSessionFactory 生产 SqlSession 对象
            SqlSession sqlSession = sqlSessionFactory.openSession();
            // 5.使用 SqlSession 创建 dao 接口的代理对象
            UserDao mapper = sqlSession.getMapper(UserDao.class);
            // 6.使用代理对象执行查询所有用户方法
            List<User> all = mapper.findAll();
            for (User user : all) {
                System.out.println(user);
            }
            // 7.释放资源
            sqlSession.close();
            in.close();
    
        }
    }
    
    
  8. 环境搭建的注意事项

    1. 在 MyBatis 中它把持久层的操作接口名称和映射文件也叫做:Mapper,所以 UserDao 和 UserMapper 是一样的
    2. 在 IDEA 的 resource 中创建 Directory 的时候,它和 Package 是不一样的
      Package 在创建时:com.zt.dao 它是三级结构
      Directory 在创建时:com.zt.dao 是一级目录,因此 Directory 要逐级创建
    3. MyBatis 的映射配置文件位置必须和 dao 接口的包结构相同
    4. 映射配置文件的 mapper 标签 namespace 属性的取值必须是 dao 接口的全限定类名
    5. 映射配置文件的操作配置(select),id 属性的取值必须是 dao 接口的方法名

    注意:当我们遵从了 3,4,5 点之后,我们在开发中就无须再写 dao 的实现类了。

3.2 MyBatis 的入门案例(基于注解)

  1. 创建 Maven 的 Java工程,工程信息如下:

    <groupId>com.zt</groupId>
    <artifactId>mybatis_day01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    
  2. 添加 Mybatis3.4.5 的坐标

    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>
    
  3. 编写 User 实体类

    package com.zt.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 + '\'' +
                    '}';
        }
    }
    
    
  4. 编写持久层接口 UserDao

    package com.zt.dao;
    
    import com.zt.domain.User;
    import org.apache.ibatis.annotations.Select;
    
    import java.util.List;
    
    /**
     * 用户持久层接口
     */
    public interface UserDao {
        /**
         * 查询所有用户
         * @return
         */
        @Select("select * from user")
        public List<User> findAll();
    }
    
    
  5. 在 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">
    
    <configuration>
        <!-- 配置 mybatis 的环境 -->
        <environments default="mysql">
            <!-- 配置 mysql 的环境 -->
            <environment id="mysql">
                <!-- 配置事务的类型 -->
                <transactionManager type="JDBC"></transactionManager>
                <!-- 配置连接数据库的信息:用的是数据源(连接池) -->
                <dataSource type="POOLED">
                    <!--配置连接数据库的四个基本信息-->
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis_day01"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
    
        <!-- 告知 mybatis 映射配置的位置 -->
        <mappers>
            <mapper class="com.zt.dao.UserDao"/>
        </mappers>
    
    </configuration>
    
  6. 编写测试类

    /**
     * 入门案例
     */
    public class MybatisTest {
        public static void main(String[] args) throws Exception {
            // 1.读取配置文件
            InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
            // 2.创建 SqlSessionFactory 的构建者对象
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            // 3.使用构建者创建工厂对象 SqlSessionFactory
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
            // 4.使用 SqlSessionFactory 生产 SqlSession 对象
            SqlSession sqlSession = sqlSessionFactory.openSession();
            // 5.使用 SqlSession 创建 dao 接口的代理对象
            UserDao mapper = sqlSession.getMapper(UserDao.class);
            // 6.使用代理对象执行查询所有用户方法
            List<User> all = mapper.findAll();
            for (User user : all) {
                System.out.println(user);
            }
            // 7.释放资源
            sqlSession.close();
            in.close();
    
        }
    }
    
    

3.3 MyBatis 入门案例的设计模式分析

在这里插入图片描述

4. 自定义 MyBatis 框架

4.1 自定义 MyBatis 框架分析

  1. MyBatis 在使用代理 dao 的方式实现查询时做了以下两件事:

    1. 创建代理对象
    2. 在代理对象中调用 selectList
  2. 自定义 MyBatis 框架分析

    在这里插入图片描述

4.2 自定义 MyBatis 框架的实现

4.2.1 前期准备

  1. 导入之前 MyBatis 的入门案例(基于 XML 配置文件)的工程
  2. 删除 MyBatis 的依赖
  3. 删除持久层接口的映射文件 UserDao.xml 中的约束
  4. 删除配置文件 SqlMapConfig.xml 中的约束

4.2.2 补齐测试类中缺少的类和接口

由于我们删除了 Mybatis 的依赖,所以测试类中有关 MyBatis 的类和接口都会报错,接下来我们补齐它们:

  1. 创建 mybatis.io 包

    1. 创建 Resources

      /**
       * 使用类加载器读取配置文件的类
       */
      public class Resources {
          /**
           * 根据传入的参数获取一个字节输入流
           * @param filePath
           * @return
           */
          public static InputStream getResourceAsStream(String filePath){
              return Resources.class.getClassLoader().getResourceAsStream(filePath);
          }
      }
      
      
  2. 创建 mybatis.sqlsession

    1. 创建 SqlSessionFactoryBuilder

      /**
       * 用于创建一个 SqlSessionFactory 对象
       */
      public class SqlSessionFactoryBuilder {
          /**
           * 根据参数的字节输入流来构建一个 SqlSessionFactory 工厂
           * @param inputStream
           * @return
           */
          public SqlSessionFactory build(InputStream inputStream){
              return null;
          }
      }
      
      
    2. 创建 SqlSessionFactory 接口

      public interface SqlSessionFactory {
          /**
           * 用于打开一个新的 SqlSession 对象
           * @return
           */
          SqlSession openSession();
      }
      
    3. 创建 SqlSession 接口

      /**
       * 自定义 Mybatis 中和数据库交互的核心类
       * 它里面可以创建 dao 接口的代理对象
       */
      public interface SqlSession {
          /**
           * 根据参数创建一个代理对象
           * @param daoInterfaceClass dao 的接口字节码
           * @param <T>
           * @return
           */
          <T> T getMapper(Class<T> daoInterfaceClass);
      
          /**
           * 释放资源
           */
          void close();
      }
      

4.2.3 编写解析 XML 的工具类

  1. 导入 dom4j 和 jaxen 的依赖

  2. 创建 cfg 包

    1. 创建 Configuration

      /**
       * 自定义 Mybatis 的配置类
       */
      public class Configuration {
          private String driver;
          private String url;
          private String username;
          private String password;
          private Map<String,Mapper> mappers = new HashMap<String, Mapper>();
      
          public Map<String, Mapper> getMappers() {
              return mappers;
          }
      
          public void setMappers(Map<String, Mapper> mappers) {
              this.mappers.putAll(mappers);
          }
      
          public String getDriver() {
              return driver;
          }
      
          public void setDriver(String driver) {
              this.driver = driver;
          }
      
          public String getUrl() {
              return url;
          }
      
          public void setUrl(String url) {
              this.url = url;
          }
      
          public String getUsername() {
              return username;
          }
      
          public void setUsername(String username) {
              this.username = username;
          }
      
          public String getPassword() {
              return password;
          }
      
          public void setPassword(String password) {
              this.password = password;
          }
      }
      
      
    2. 创建 Mapper

      /**
       * 用于封装执行的 sql 语句和结果集类型的全限定名
       */
      public class Mapper {
          private String queryString;
          private String resultType;
      
          public String getQueryString() {
              return queryString;
          }
      
          public void setQueryString(String queryString) {
              this.queryString = queryString;
          }
      
          public String getResultType() {
              return resultType;
          }
      
          public void setResultType(String resultType) {
              this.resultType = resultType;
          }
      }
      
  3. 创建 utils 包

    1. 创建 XMLConfigBuilder

      /**
       * 用于解析配置文件
       */
      public class XMLConfigBuilder {
      
          /**
           * 解析主配置文件,把里面的内容填充到DefaultSqlSession所需要的地方
           * 使用的技术:
           * dom4j+xpath
           */
          public static Configuration loadConfiguration(InputStream config) {
              try {
                  //定义封装连接信息的配置对象(mybatis的配置对象)
                  Configuration cfg = new Configuration();
      
                  //1.获取SAXReader对象
                  SAXReader reader = new SAXReader();
                  //2.根据字节输入流获取Document对象
                  Document document = reader.read(config);
                  //3.获取根节点
                  Element root = document.getRootElement();
                  //4.使用xpath中选择指定节点的方式,获取所有property节点
                  List<Element> propertyElements = root.selectNodes("//property");
                  //5.遍历节点
                  for (Element propertyElement : propertyElements) {
                      //判断节点是连接数据库的哪部分信息
                      //取出name属性的值
                      String name = propertyElement.attributeValue("name");
                      if ("driver".equals(name)) {
                          //表示驱动
                          //获取property标签value属性的值
                          String driver = propertyElement.attributeValue("value");
                          cfg.setDriver(driver);
                      }
                      if ("url".equals(name)) {
                          //表示连接字符串
                          //获取property标签value属性的值
                          String url = propertyElement.attributeValue("value");
                          cfg.setUrl(url);
                      }
                      if ("username".equals(name)) {
                          //表示用户名
                          //获取property标签value属性的值
                          String username = propertyElement.attributeValue("value");
                          cfg.setUsername(username);
                      }
                      if ("password".equals(name)) {
                          //表示密码
                          //获取property标签value属性的值
                          String password = propertyElement.attributeValue("value");
                          cfg.setPassword(password);
                      }
                  }
                  //取出mappers中的所有mapper标签
                  List<Element> mapperElements = root.selectNodes("//mappers/mapper");
                  //遍历集合
                  for (Element mapperElement : mapperElements) {
                      // 获取 mapperElement 的 resource 元素
                      Attribute attribute = mapperElement.attribute("resource");
                      // 取出属性的值
                      String mapperPath = attribute.getValue();// 获取属性的值"com/zt/dao/UserDao.xml"
                      // 把映射配置文件的内容获取出来,封装成一个map
                      Map<String, Mapper> mappers = loadMapperConfiguration(mapperPath);
                      //给configuration中的 mappers 赋值
                      cfg.setMappers(mappers);
                  }
                  //返回Configuration
                  return cfg;
              } catch (Exception e) {
                  throw new RuntimeException(e);
              } finally {
                  try {
                      config.close();
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
      
          }
      
          /**
           * 根据传入的参数,解析XML,并且封装到Map中
           * @param mapperPath 映射配置文件的位置
           * @return map中包含了获取的唯一标识(key是由dao的全限定类名和方法名组成)
           * 以及执行所需的必要信息(value是一个Mapper对象,里面存放的是执行的SQL语句和要封装的实体类全限定类名)
           */
          private static Map<String, Mapper> loadMapperConfiguration(String mapperPath) throws IOException {
              InputStream in = null;
              try {
                  //定义返回值对象
                  Map<String, Mapper> mappers = new HashMap<String, Mapper>();
                  //1.根据路径获取字节输入流
                  in = Resources.getResourceAsStream(mapperPath);
                  //2.根据字节输入流获取Document对象
                  SAXReader reader = new SAXReader();
                  Document document = reader.read(in);
                  //3.获取根节点
                  Element root = document.getRootElement();
                  //4.获取根节点的namespace属性取值
                  String namespace = root.attributeValue("namespace");//是组成map中key的部分
                  //5.获取所有的select节点
                  List<Element> selectElements = root.selectNodes("//select");
                  //6.遍历select节点集合
                  for (Element selectElement : selectElements) {
                      //取出id属性的值      组成map中key的部分
                      String id = selectElement.attributeValue("id");
                      //取出resultType属性的值  组成map中value的部分
                      String resultType = selectElement.attributeValue("resultType");
                      //取出文本内容            组成map中value的部分
                      String queryString = selectElement.getText();
                      //创建Key
                      String key = namespace + "." + id;
                      //创建Value
                      Mapper mapper = new Mapper();
                      mapper.setQueryString(queryString);
                      mapper.setResultType(resultType);
                      //把key和value存入mappers中
                      mappers.put(key, mapper);
                  }
                  return mappers;
              } catch (Exception e) {
                  throw new RuntimeException(e);
              } finally {
                  in.close();
              }
          }
      
      
      }
      
      

4.2.4 创建两个默认实现类

  1. 在 sqlsession 包中创建一个 defaults 包

    1. 创建 DefaultSqlSessionFactory

      /**
       * SqlSessionFactory 接口的实现类
       */
      public class DefaultSqlSessionFactory implements SqlSessionFactory {
      
          private Configuration configuration;
      
          public DefaultSqlSessionFactory(Configuration configuration) {
              this.configuration = configuration;
          }
      
          public SqlSession openSession() {
              return new DefaultSqlSession(configuration);
          }
      }
      
    2. 创建 DefaultSqlSession

      /**
       * SqlSession 接口的实现类
       */
      public class DefaultSqlSession implements SqlSession {
      
          private Configuration configuration;
      
          public DefaultSqlSession(Configuration configuration) {
              this.configuration = configuration;
          }
      
          /**
           * 用于创建代理对象
           * @param daoInterfaceClass dao 的接口字节码
           * @param <T>
           * @return
           */
          public <T> T getMapper(Class<T> daoInterfaceClass) {
              return null;
          }
      
          public void close() {
      
          }
      }
      
  2. 完善 SqlSessionFactoryBuilder 类

    /**
     * 用于创建一个 SqlSessionFactory 对象
     */
    public class SqlSessionFactoryBuilder {
        /**
         * 根据参数的字节输入流来构建一个 SqlSessionFactory 工厂
         * @param inputStream
         * @return
         */
        public SqlSessionFactory build(InputStream inputStream){
            Configuration configuration = XMLConfigBuilder.loadConfiguration(inputStream);
            return new DefaultSqlSessionFactory(configuration);
        }
    }
    

4.2.5 实现基于 XML 的查询所有操作

  1. 在 utils 包中创建 Executor

    /**
     * 负责执行SQL语句,并且封装结果集
     */
    public class Executor {
    
        public <E> List<E> selectList(Mapper mapper, Connection conn) {
            PreparedStatement pstm = null;
            ResultSet rs = null;
            try {
                //1.取出mapper中的数据
                String queryString = mapper.getQueryString();//select * from user
                String resultType = mapper.getResultType();//com.itheima.domain.User
                Class domainClass = Class.forName(resultType);
                //2.获取PreparedStatement对象
                pstm = conn.prepareStatement(queryString);
                //3.执行SQL语句,获取结果集
                rs = pstm.executeQuery();
                //4.封装结果集
                List<E> list = new ArrayList<E>();//定义返回值
                while(rs.next()) {
                    //实例化要封装的实体类对象
                    E obj = (E)domainClass.newInstance();
    
                    //取出结果集的元信息:ResultSetMetaData
                    ResultSetMetaData rsmd = rs.getMetaData();
                    //取出总列数
                    int columnCount = rsmd.getColumnCount();
                    //遍历总列数
                    for (int i = 1; i <= columnCount; i++) {
                        //获取每列的名称,列名的序号是从1开始的
                        String columnName = rsmd.getColumnName(i);
                        //根据得到列名,获取每列的值
                        Object columnValue = rs.getObject(columnName);
                        //给obj赋值:使用Java内省机制(借助PropertyDescriptor实现属性的封装)
                        PropertyDescriptor pd = new PropertyDescriptor(columnName,domainClass);//要求:实体类的属性和数据库表的列名保持一种
                        //获取它的写入方法
                        Method writeMethod = pd.getWriteMethod();
                        //把获取的列的值,给对象赋值
                        writeMethod.invoke(obj,columnValue);
                    }
                    //把赋好值的对象加入到集合中
                    list.add(obj);
                }
                return list;
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                release(pstm,rs);
            }
        }
    
    
        private void release(PreparedStatement pstm,ResultSet rs){
            if(rs != null){
                try {
                    rs.close();
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
    
            if(pstm != null){
                try {
                    pstm.close();
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
    
    
  2. 在 utils 包中创建 DataSourceUtils

    /**
     * 用于创建数据源的工具类
     */
    public class DataSourceUtils {
        /**
         * 用于获取一个连接
         * @param configuration
         * @return
         */
        public static Connection getConnection(Configuration configuration){
            try {
                Class.forName(configuration.getDriver());
                return DriverManager.getConnection(configuration.getUrl(), configuration.getUsername(), configuration.getPassword());
            }catch(Exception e){
                throw new RuntimeException(e);
            }
        }
    }
    
    
  3. 在 sqlsession 包中创建 proxy 包,创建 MapperProxy

    public class MapperProxy implements InvocationHandler {
    
        private Map<String,Mapper> mapperMap;
        private Connection connection;
    
        public MapperProxy(Map<String, Mapper> mapperMap, Connection connection) {
            this.mapperMap = mapperMap;
            this.connection = connection;
        }
    
        /**
         * 用于对方法进行增强,这里的增强就是调用 SelectList 方法
         * @param proxy
         * @param method
         * @param args
         * @return
         * @throws Throwable
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //1.获取方法名
            String methodName = method.getName();
            //2.获取方法所在类的名称
            String className = method.getDeclaringClass().getName();
            //3.组合key
            String key = className+"."+methodName;
            //4.获取mappers中的Mapper对象
            Mapper mapper = mapperMap.get(key);
            //5.判断是否有mapper
            if(mapper == null){
                throw new IllegalArgumentException("传入的参数有误");
            }
            //6.调用工具类执行查询所有
            return new Executor().selectList(mapper,connection);
        }
    }
    
  4. 完善 DefaultSqlSession

    /**
     * SqlSession 接口的实现类
     */
    public class DefaultSqlSession implements SqlSession {
    
        private Configuration configuration;
        private Connection connection;
    
        public DefaultSqlSession(Configuration configuration) {
            this.configuration = configuration;
            this.connection = DataSourceUtils.getConnection(configuration);
        }
    
        /**
         * 用于创建代理对象
         *
         * @param daoInterfaceClass dao 的接口字节码
         * @param <T>
         * @return
         */
        public <T> T getMapper(Class<T> daoInterfaceClass) {
            return (T) Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(), new Class[]{daoInterfaceClass},
                    new MapperProxy(configuration.getMappers(), connection));
    
        }
    
        public void close() {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    

4.2.6 测试类

/**
 * 入门案例
 */
public class MybatisTest {
    public static void main(String[] args) throws Exception {
        // 1.读取配置文件
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 2.创建 SqlSessionFactory 的构建者对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        // 3.使用构建者创建工厂对象 SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
        // 4.使用 SqlSessionFactory 生产 SqlSession 对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 5.使用 SqlSession 创建 dao 接口的代理对象
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        // 6.使用代理对象执行查询所有用户方法
        List<User> all = mapper.findAll();
        for (User user : all) {
            System.out.println(user);
        }
        // 7.释放资源
        sqlSession.close();
        in.close();

    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bm1998

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值