MyBatis 1

第一天

概述
环境搭建
案例
自定义mybatis框架

框架概述

什么是框架

框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用方面而后者是从目的方面给出的定义。简而言之,框架其实就是某种应用的半成品,就是一组组件,供你选用完成你自己的系统。

三层架构

三层架构
表现层:是用于展示数据
业务层:是处理业务需求
持久层:是和数据交库互
持久层技术解决方案
JDBC技术:Connection、PreparedStatement、ResultSet
Spring的JdbcTemplate:Spring中对Jdbc的简单封装
Apache的DBUtils:对Jdbc的简单封装
以上的这些都不是框架:
	JDBC是规范
	Spring的JdbcTemplate和Apache的DBUtils都知识工具类

在这里插入图片描述

MyBatis框架概述

mybatis 是基于 java 的持久层框架,内部封装了 jdbc,开发者只需要关注 sql 语句本身,
而不需要花费精力去处理加载驱动、创建连接、创建 statement 等过程。

mybatis 通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。

采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。

ORM:Object Relational Mapping 对象关系映射,即数据库表和实体类及实体类的属性对应起来,是我们可以操作实体类就实现操作数据库表。如:

user			User
id				userId
user_name		userName
今天需要做到实体类中的属性和数据库字段名称保持一致:
user			User
id				id
user_name		user_name

MyBatis入门:XML和注解

下面的时基于XML方式,注解需要更改部分。

环境搭建

1. 创建maven工程,导入坐标: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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>lee</groupId>
    <artifactId>mybatis_day01</artifactId>
    <version>1.0-SNAPSHOT</version>
    <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>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
    </dependencies>

</project>

2. 创建实体类和dao的接口:domain.User、dao.UserDao

package domain;

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

/**
 * @author: Lee
 * @create: 2020-11-13 18:52
 * @description:
 **/
public class User implements Serializable {
    private int id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    public User() {
    }

    public User(int id, String username, Date birthday, String sex, String address) {
        this.id = id;
        this.username = username;
        this.birthday = birthday;
        this.sex = sex;
        this.address = address;
    }

    public int getId() {
        return id;
    }

    public void setId(int 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 + '\'' +
                '}';
    }
}
package dao;

import domain.User;

import java.util.List;

/**
 * @author: Lee
 * @create: 2020-11-13 19:03
 * @description: 用户持久层操作
 **/
public interface UserDao {
    /**
     * 查询所有操作
     * @return
     */
    List<User> findAll();
}

3. 创建Mybatis的配置文件:resources下的SqlMapConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!--配置环境-->
    <environments default="mysql">
        <!--配置mysql环境-->
        <environment id="mysql">
            <!--配置事务类型:JDBC-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置连接池:有3个-->
            <dataSource type="POOLED">
                <!--配置连接数据库的4个基本信息:driver url username password-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="lee"/>
            </dataSource>
        </environment>
    </environments>

    <!--指定映射文件的位置,映射文件指的是每个dao独立的配置文件:包含dao的全限定类名、方法名、返回值对象的全限定类名、sql语句
    使用注解开发时,需要将resource属性替换为class属性,指定被注解的dao全限定类名
    -->
    <mappers>
        <mapper resource="dao/UserDao.xml"/>
    </mappers>
    <!--<mappers>
        <mapper class="dao.UserDao"/>
    </mappers>-->
</configuration>

4. 创建映射文件:resources下的dao/UserDao.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="dao.UserDao">
    <!--配置查询所有-->
    <select id="findAll">
        select * from user
    </select>

</mapper>

环境搭建的注意事项:

1.在前述创建UserDao.xml和UserDao.java的名称是为了和之前的知识保持一致(我也不知道是那个知识)!
		但是在Mybatis中把持久层的操作接口名称和映射文件的名称也叫做:Mapper,即前述创建的名称UserDao和UserMapper是一样的。
2.idea中创建目录时和包是不一样的。
		包创建时:com.lee是多级结构
		目录创建时:com.lee是一级结构
3.Mybatis的配置文件(SqlMapConfig.xml)的指定位置必修和dao接口的包结构相同。
4..映射文件(UserDao.xml)的mapper标签namespace的属性值必须是dao接口的全限定类名。
5.映射文件的操作配置,select标签的id属性值必须是dao接口的方法名

遵循上述,开发时无需再写dao的实现类!!!

入门案例

查询所有的用户信息

1.读取配置文件
2.创建SqlSessionFactory工厂
3.创建SqlSession
4.创建UserDao接口的代理对象(实现类)
5.执行dao中的方法
6.释放资源
import dao.UserDao;
import 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;

/**
 * @author: Lee
 * @create: 2020-11-13 19:54
 * @description:
 **/
public class MybatisTest {
    public static void main(String[] args) throws Exception {
        //1.读取配置文件
        InputStream is = Resources.class.getClassLoader().getResourceAsStream("SqlMapConfig.xml");
        //2.创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        //3.使用工厂生产SqlSession对象
        SqlSession session = factory.openSession();
        //4.使用SqlSession创建UserDao接口的代理对象(实现类)
        UserDao dao = session.getMapper(UserDao.class);
        //5.使用代理对象执行方法
        List<User> list = dao.findAll();
        for (User user : list) {
            System.out.println(user);
        }
        //6.释放资源
        session.close();
        is.close();
    }
}

修改映射文件UserDao.xml中<select id="findAll" resultType="domain.User">,解决异常问题:没有指定返回类型

基于注解

上述的环境搭建和入门案例是基于XML方式,下面是基于注解:

1.删除UserDao.xml映射文件和dao目录:不删除会报错:org.apache.ibatis.exceptions.PersistenceException
2.修改SqlMapConfig.xml中mapper标签如下:
	    <mappers>
	        <mapper resource="dao/UserDao.xml"/>
	    </mappers>
	修改为:
	    <mappers>
	        <mapper class="dao.UserDao"/>
	    </mappers>
3.修改UserDao.java,添加注解:
		public interface UserDao {
		    /**
		     * 查询所有操作
		     * @return
		     */
		    @Select("select * from user")
		    List<User> findAll();
		}

明确:实际开发中简化流程,所以不需要将dao接口实现,而是使用代理模式简化。但是不论是XML还是注解配置,Mybatis都是支持自己实现dao接口的,具体实现方式简略,部分实现思想如下。

在这里插入图片描述

代理模式分析图:
入门分析案例

自定义Mybatis及分析

上述入门案例流程分析:

自定义Mybatis分析
(自定义)mybatis分析:

(自定义)mybatis在使用代理dao的方式实现crud时做的事情:
		1.创建代理对象
		2.在代理对象中调用selectList
(自定义)mybatis所使用的类:
		class Resources
		class SqlSessionFactoryBuilder
		interface SqlSessionFactory
		interface SqlSession

逻辑思想:
流程分析

1.创建代理对象步骤
	1.获取自定义mybatis的指定配置文件的流对象:Resources
	2.使用XML解析工具类解析配置文件:XMLConfigBuilder
	将解析的数据放入Configuration对象中:连接数据库的4个基本信息,也是连接信息:driver url username password,以及映射文件UserDao.xml位置;
	再使用XMLConfigBuilder解析映射文件:将映射信息存入Configuration类的Map<String, Mapper>成员变量中,key = namespace + "." + id;value=Mapper mapper = new Mapper(queryString,resultType);
	这就是上述自定义mybatis分析图的映射信息:包含全限定dao类名称namespace、方法名称id、返回值类型resultType、sql语句,其中返回值类型resultType、sql语句是封装在Mapper对象中的,而所有的映射信息封装在Map<String, Mapper>集合中。
	这里需要注意,需要new一个Map集合实例对象,并将映射信息mappers追加到(putAll)到集合中,否则会报空指针异常!!
	3.使用SqlSessionFactoryBuilder根据映射配置文件获取的连接信息+映射信息创建SqlSessionFactory的实体类 SqlSessionFactoryImpl,此处需要注意SqlSessionFactory有连接信息+映射信息对象Configuration cfg,那么整个代理过程都是能通过手段获取到该对象,使映射信息得以传递。
	4.由SqlSessionFactoryImpl创建SqlSession的实现类SqlSessionImpl
	5.由SqlSessionImpl根据映射信息实现dao接口的代理对象创建,代理方式MapperProxy调用selectList执行sql语句需求。
2.在代理对象中调用selectList步骤
	Executor有selectList方法,执行此方法需要连接数据库(连接信息)执行sql语句需求(映射信息),其又由MapperProxy执行,而MapperProxy又由SqlSessionImpl调用,追溯完成,所以只需要从最初位置传递需要的参数就可以完成执行。

修改映射配置文件:SqlMapConfig.xml UserDao.xml 删除约束 此处先使用XML配置
修改pom.xml:删除mybatis依赖,此处自定义mybatis,添加依赖:dom4j+jaxen XML解析使用
目录结构

Resources :

package mybatis.io;

import java.io.InputStream;

/**
 * @author: Lee
 * @create: 2020-11-14 14:26
 * @description: 使用类加载器读取映射配置文件
 **/
public class Resources {
    /**
     * 获取指定(配置)文件的字节输入流
     *
     * @param filePath
     * @return InputStream
     */
    public static InputStream getResourceAsStream(String filePath) {
        return Resources.class.getClassLoader().getResourceAsStream(filePath);
    }
}

SqlSessionFactoryBuilder:

package mybatis.sqlsession;

import mybatis.cfg.Configuration;
import mybatis.sqlsession.impl.SqlSessionFactoryImpl;
import mybatis.utils.XMLConfigBuilder;

import java.io.InputStream;

/**
 * @author: Lee
 * @create: 2020-11-14 14:31
 * @description: 创建SqlSessionFactory对象
 **/
public class SqlSessionFactoryBuilder {
    /**
     * 根据映射配置文件创建SqlSessionFactory工厂
     *
     * @param config
     * @return SqlSessionFactory
     */
    public SqlSessionFactory build(InputStream config) {
        Configuration cfg = XMLConfigBuilder.loadConfiguration(config);
        return new SqlSessionFactoryImpl(cfg);
    }
}

XMLConfigBuilder:

package mybatis.utils;

import mybatis.cfg.Mapper;
import mybatis.io.Resources;
import mybatis.cfg.Configuration;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

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

/**
 * @author 黑马程序员
 * @Company http://www.ithiema.com
 * 用于解析配置文件
 */
public class XMLConfigBuilder {


    /**
     * 解析主配置文件,把里面的内容填充到DefaultSqlSession所需要的地方
     * 使用的技术:
     * dom4j+xpath
     * 导入依赖:dom4j+jaxen
     */
    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标签,判断他们使用了resource还是class属性
            List<Element> mapperElements = root.selectNodes("//mappers/mapper");
            //遍历集合
            for (Element mapperElement : mapperElements) {
                //判断mapperElement使用的是哪个属性
                Attribute attribute = mapperElement.attribute("resource");
                if (attribute != null) {
                    System.out.println("使用的是XML");
                    //表示有resource属性,用的是XML
                    //取出属性的值
                    String mapperPath = attribute.getValue();//获取属性的值"dao/IUserDao.xml"
                    //把映射配置文件的内容获取出来,封装成一个map
                    Map<String, Mapper> mappers = loadMapperConfiguration(mapperPath);
                    //给configuration中的mappers赋值
                    cfg.setMappers(mappers);
                } else {
                    /*System.out.println("使用的是注解");
                    //表示没有resource属性,用的是注解
                    //获取class属性的值
                    String daoClassPath = mapperElement.attributeValue("class");
                    //根据daoClassPath获取封装的必要信息
                    Map<String,Mapper> mappers = loadMapperAnnotation(daoClassPath);
                    //给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(queryString,resultType);
                //把key和value存入mappers中
                mappers.put(key, mapper);
            }
            return mappers;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            in.close();
        }
    }
    /*

     */
/**
 * 根据传入的参数,得到dao中所有被select注解标注的方法。
 * 根据方法名称和类名,以及方法上注解value属性的值,组成Mapper的必要信息
 * @param daoClassPath
 * @return
 *//*

    private static Map<String,Mapper> loadMapperAnnotation(String daoClassPath)throws Exception{
        //定义返回值对象
        Map<String,Mapper> mappers = new HashMap<String, Mapper>();

        //1.得到dao接口的字节码对象
        Class daoClass = Class.forName(daoClassPath);
        //2.得到dao接口中的方法数组
        Method[] methods = daoClass.getMethods();
        //3.遍历Method数组
        for(Method method : methods){
            //取出每一个方法,判断是否有select注解
            boolean isAnnotated = method.isAnnotationPresent(Select.class);
            if(isAnnotated){
                //创建Mapper对象
                Mapper mapper = new Mapper();
                //取出注解的value属性值
                Select selectAnno = method.getAnnotation(Select.class);
                String queryString = selectAnno.value();
                mapper.setQueryString(queryString);
                //获取当前方法的返回值,还要求必须带有泛型信息
                Type type = method.getGenericReturnType();//List<User>
                //判断type是不是参数化的类型
                if(type instanceof ParameterizedType){
                    //强转
                    ParameterizedType ptype = (ParameterizedType)type;
                    //得到参数化类型中的实际类型参数
                    Type[] types = ptype.getActualTypeArguments();
                    //取出第一个
                    Class domainClass = (Class)types[0];
                    //获取domainClass的类名
                    String resultType = domainClass.getName();
                    //给Mapper赋值
                    mapper.setResultType(resultType);
                }
                //组装key的信息
                //获取方法的名称
                String methodName = method.getName();
                String className = method.getDeclaringClass().getName();
                String key = className+"."+methodName;
                //给map赋值
                mappers.put(key,mapper);
            }
        }
        return mappers;
    }
*/
}

Configuration:

package mybatis.cfg;

import java.util.HashMap;
import java.util.Map;

/**
 * @author: Lee
 * @create: 2020-11-14 16:34
 * @description: 自定义mybatis映射配置文件SqlMapConfig.xml、UserDao.xml信息
 **/
public class Configuration {
    /**
     * 创建数据库操作对象的信息
     */
    private String driver;
    private String url;
    private String username;
    private String password;
    /**
     * 执行数据库SQL语句所需要的信息
     */
    private Map<String, Mapper> mappers = new HashMap<String, Mapper>();

    public Configuration() {
    }

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

    public Map<String, Mapper> getMappers() {
        return mappers;
    }

    public void setMappers(Map<String, Mapper> mappers) {
        this.mappers.putAll(mappers);
    }
}

Mapper:

package mybatis.cfg;

/**
 * @author: Lee
 * @create: 2020-11-14 16:40
 * @description: 用于封装(key)dao接口的全限定类名·方法名和(value)执行的SQL语句+查询返回结果类型的全限定类名
 **/
public class Mapper {
    private String queryString;//SQL
    private String resultType;//实体类的全限定类名
    
	public Mapper() {
	    }
	    
    public Mapper(String queryString, String resultType) {
        this.queryString = queryString;
        this.resultType = 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;
    }
}

SqlSessionFactory :

package mybatis.sqlsession;

/**
 * @author: Lee
 * @create: 2020-11-14 14:34
 * @description: SqlSessionFactory的接口
 **/
public interface SqlSessionFactory {
    /**
     * 创建新的操作数据库对象
     *
     * @return SqlSession
     */
    SqlSession openSession();
}

SqlSessionFactoryImpl :

package mybatis.sqlsession.impl;

import mybatis.cfg.Configuration;
import mybatis.sqlsession.SqlSession;
import mybatis.sqlsession.SqlSessionFactory;

/**
 * @author: Lee
 * @create: 2020-11-14 17:00
 * @description:
 **/
public class SqlSessionFactoryImpl implements SqlSessionFactory {
    /**
     * 此处工厂有映射配置文件对象:Configuration cfg
     * 即代表所有的SqlSession都有此对象。如下所示,创建新的操作数据库对象SqlSession对象将cfg传递进去
     */
    private Configuration cfg;

    public SqlSessionFactoryImpl(Configuration cfg) {
        this.cfg = cfg;
    }

    /**
     * 创建新的操作数据库对象
     *
     * @return
     */
    public SqlSession openSession() {
        return new SqlSessionImpl(cfg);
    }
}

SqlSession :

package mybatis.sqlsession;

/**
 * @author: Lee
 * @create: 2020-11-14 14:37
 * @description: 自定义mybatis中和数据库交互的核心类,可以创建dao接口的代理对象
 **/
public interface SqlSession {
    /**
     * 创建dao接口的代理对象
     *
     * @param daoInterfaceClass
     * @param <T>
     * @return
     */
    <T> T getMapper(Class<T> daoInterfaceClass);

    /**
     * 释放资源
     */
    void close();
}

SqlSessionImpl

package mybatis.sqlsession.impl;

import mybatis.cfg.Configuration;
import mybatis.sqlsession.SqlSession;
import mybatis.sqlsession.proxy.MapperProxy;
import mybatis.utils.DataSourceUtil;

import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * @author: Lee
 * @create: 2020-11-14 17:39
 * @description:
 **/
public class SqlSessionImpl implements SqlSession {
    private Configuration cfg;
    private Connection conn;

    public SqlSessionImpl(Configuration cfg) {
        this.cfg = cfg;
        conn = DataSourceUtil.getConnection(cfg);
    }

    /**
     * 创建dao接口的代理对象
     *
     * @param daoInterfaceClass
     * @param <T>
     * @return
     */
    public <T> T getMapper(Class<T> daoInterfaceClass) {
        /**
         * Proxy.newProxyInstance(代理谁就是谁的类加载器,代理的类实现的接口(本身是接口传入本身即可),代理方式)
         * 注:此处的代理方式需要获取映射信息,实现查询需求,所以需要将Configuration cfg对象的mappers传递过去
         * 获得
         */
        return (T) Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(), new Class[]{daoInterfaceClass}, new MapperProxy(cfg.getMappers(), conn));
    }

    /**
     * 释放资源
     */
    public void close() {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

MapperProxy :

package mybatis.sqlsession.proxy;

import mybatis.cfg.Mapper;
import mybatis.utils.Executor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Map;

/**
 * @author: Lee
 * @create: 2020-11-14 17:51
 * @description: 自定义mybatis的代理方式
 **/
public class MapperProxy implements InvocationHandler {
    private Map<String, Mapper> mappers;
    private Connection conn;

    public MapperProxy(Map<String, Mapper> mappers, Connection conn) {
        this.mappers = mappers;
        this.conn = conn;
    }

    /**
     * 用于对方法进行增强,我们的增强就是调用selectList方法(此处针对自定义mybatis的需求是查询所有user数据)
     *
     * @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 = mappers.get(key);
        //5.判断是否有mapper
        if (mapper == null) {
            throw new IllegalArgumentException("传入的参数有误");//Runtime异常
        }
        //6.调用工具类E
        return new Executor().selectList(mapper, conn);

    }
}

Executor :

package mybatis.utils;

import mybatis.cfg.Mapper;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 黑马程序员
 * @Company http://www.ithiema.com
 * 负责执行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();//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();
            }
        }
    }
}

DataSourceUtil :

package mybatis.utils;

import mybatis.cfg.Configuration;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * @author: Lee
 * @create: 2020-11-14 18:22
 * @description: 根据映射配置文件创建数据库连接
 **/
public class DataSourceUtil {
    /**
     * 用于获取一个连接对象
     *
     * @param cfg
     * @return
     */
    public static Connection getConnection(Configuration cfg) {
        try {
            Class.forName(cfg.getDriver());
            return DriverManager.getConnection(cfg.getUrl(), cfg.getUsername(), cfg.getPassword());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

MybatisTest :

import dao.UserDao;
import domain.User;
import mybatis.io.Resources;
import mybatis.sqlsession.SqlSession;
import mybatis.sqlsession.SqlSessionFactory;
import mybatis.sqlsession.SqlSessionFactoryBuilder;

import java.io.InputStream;
import java.util.List;

/**
 * @author: Lee
 * @create: 2020-11-13 19:54
 * @description:
 **/
public class MybatisTest {
    public static void main(String[] args) throws Exception {
        //1.读取配置文件
        InputStream is = Resources.class.getClassLoader().getResourceAsStream("SqlMapConfig.xml");
        //2.创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        //3.使用工厂生产SqlSession对象
        SqlSession session = factory.openSession();
        //4.使用SqlSession创建UserDao接口的代理对象(实现类)
        UserDao dao = session.getMapper(UserDao.class);
        //5.使用代理对象执行方法
        List<User> list = dao.findAll();
        for (User user : list) {
            System.out.println(user);
        }
        //6.释放资源
        session.close();
        is.close();
    }
}

基于注解
针对放开SqlMapConfig.xml  XMLConfigBuilder 注释
创建mybatis.annotation.Select注解
		package mybatis.annotation;
		
		import java.lang.annotation.ElementType;
		import java.lang.annotation.Retention;
		import java.lang.annotation.RetentionPolicy;
		import java.lang.annotation.Target;
		
		/**
		 * 查询注解
		 */
		@Retention(RetentionPolicy.RUNTIME)
		@Target(ElementType.METHOD)
		public @interface Select {
		    /**
		     * 接收sql语句
		     * @return
		     */
		    String value();
		}
添加dao接口的注解或者放开注释
好像不需要删除资源目录下的dao:可能因为自定义原因
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值