自定义实现mybatis框架的简单实现-搭建XML开发

发布于 2020-07-25  1164 次阅读


mybatis框架作为一个流行的和数据库打交道的持久层框架,在我们实际开发中运用广泛,其框架的设计思想值得我们这些新手学习。笔者近几天开始学习mybatis框架,便自己参照mybatis框架的源码以及其它一些相关文献,自定义实现一个简单的mybatis框架。下面便开始进入正题:
1.我们在使用mybatis框架的时候,一般使用以下几个步骤:

            //读取配置文件
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            //实例化SqlSessionFactoryBuilder
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
          //构建SqlSessionFactory
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
           //产生一个SqlSession
            SqlSession sqlSession = sqlSessionFactory.openSession();
            //利用SqlSession创建UserDAao的代理对象
            UserDao userDao = sqlSession.getMapper(UserDao.class);
            //利用代理对象执行方法
            List<User> userList = userDao.getAll();
            //关闭资源
            sqlSession.close();

此次就参考mybatis的使用步骤,我们不导入mybatis的依赖包,逐个自定义实现该功能所需要的类。
2.刚开始如图所示:
在这里插入图片描述
这些必要的类报错,我们需要自己手动定义创建。
2.1首先创建Resources类。并且创建一个static的getResourceAsStream(String filePath)方法。
2.2其次创建SqlSessionFactoryBuilder类,并且创建一个build(InputStream inputStream)方法
2.3再创建SqlSessionFactory接口(根据mybatis的源码可以知道这个是接口),然后添加一个openSession()方法。
2.4最后创建SqlSession接口(根据mybatis的源码可以知道这个是接口)。然后创建一个getMapper(Class daoClass)和close()方法。
在这里插入图片描述
虽然这些类和方法还没有真正实现,但是此时发现已经不报错了。
3.在resources资源根目录中创建配置文件mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!--mybatis的主配置文件-->
<configuration>
    <environments default="development">
        <environment id="development">
            <!-- 配置事务类型  -->
            <transactionManager type="JDBC"/>
            <!--   配置连接信息         -->
            <dataSource type="POOLED">
                <!--       配置加载MySQL的驱动         -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <!--      MySQL连接的数据库地址,笔者这里是在本地localhost上测试,并且数据库的名称为mybatisdemo     -->
                <property name="url" value="jdbc:mysql://localhost:3306/mybatisdemo?serverTimezone=UTC"/>
                <!--      MySQL连接的用户名         -->
                <property name="username" value="root"/>
                <!--      MySQL的连接密码        -->
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <!--   指定配置文件的的位置,映射文件指的是每个dao的位置-->
    <mappers>
        <!--  指定Userdao的位置,使用XML配置时使用resource标签-->
        <!--使用注解配置的时候使用class标签,使用UserDao的全限定类名   -->
        <mapper resource="dao/UserDao.xml"></mapper>
    </mappers>
</configuration>

4.首先实现Resources类的getResourceAsStream方法,其具体代码为:

 /**
     * @Description :根据传入的资源文件名获取对应的输入流
     * @Date 15:17 2020/7/25 0025
     * @Param * @param filePath :资源文件名,比如 demo.txt
     * @return java.io.InputStream
     **/
    public static InputStream getResourceAsStream(String filePath) {
        return Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath);
    }

5.接着实现SqlSessionFactoryBuilder的build方法,其具体代码为:

  /**
     * @Description :根据配置文件输入流加载配置信息并封装到一个对象中去
     * @Date 1:36 2020/7/26 0026
     * @Param * @param inputStream :配置文件输入流
     * @return mybatis.SqlSessionFactory
     **/
    public SqlSessionFactory build(InputStream inputStream) {
        //调用工具类加载配置文件并将配置信息封装到一个Configuration类中
        Configurantion configurantion = MybatisUtil.LoaderConfiguration(inputStream);
        //利用SqlSessionFactory接口的一个实现类
        return new DefaultSqlSessionFactory(configurantion);
    }

在这个类中我们发现需要对配置信息进行封装,将数据库连接中的driver,url,userName,password等信息封装到一个对象Configuration中去:

package mybatis;
import java.util.HashMap;
import java.util.Map;

/**
 * Classname:Configurantion
 *
 * @description:数据库连接配置信息类
 * @author: 陌意随影
 * @Date: 2020-07-25 15:40
 * @Version: 1.0
 **/
public class Configurantion {
    //连接驱动
    private  String driver;
    //连接用户名
    private  String username;
    //密码
    private  String password;
    //连接URL
    private String url;
    //dao接口的关键配置信息map
    private Map<String,Mapper> mapperMap = new HashMap<>();

    public Map<String, Mapper> getMapperMap() {
        return mapperMap;
    }

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

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    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 String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    @Override
    public String toString() {
        return "Configurantion{" +
                "driver='" + driver + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", url='" + url + '\'' +
                '}';
    }
}

然后我们继续创建一个工具类MybatisUtil,并添加一个static方法LoaderConfiguration用于加载配置信息并封装到Configuration对象中然后返回:

 /**
     * @Description :通过资源文件流加载配置信息
     * @Date 15:45 2020/7/25 0025
     * @Param * @param inputStream :
     * @return mybatis.Configurantion
     **/
    public static Map<String, Mapper> XMLLoader(String mapperPath) throws DocumentException {
        InputStream resourceAsStream = Resources.getResourceAsStream(mapperPath);
        //获取读取流对象
        SAXReader reader = new SAXReader();
        //获取document对象
        Document document = reader.read(resourceAsStream);
        //获取根节点,根节点就是mapper节点
        Element rootElement = document.getRootElement();
        //获取mapper的namespace的属性值
        String namespace = rootElement.attributeValue("namespace");
        //获取mapper下的每个select节点
        List nodes = rootElement.selectNodes("//select");
        Map<String, Mapper> mapperMap = new HashMap<>();
        if (nodes!=null && nodes.size() != 0){
            Map<String, Mapper> mappers = getMapperMap(namespace, nodes,Mapper.SELECT);
            mapperMap.putAll(mappers);
        }
        //获取mapper下的每个insert节点
         nodes = rootElement.selectNodes("//insert");
        if (nodes!=null && nodes.size() != 0){
            Map<String, Mapper> mappers = getMapperMap(namespace, nodes,Mapper.INSERT);
            mapperMap.putAll(mappers);
        }
        //获取mapper下的每个delete节点
         nodes = rootElement.selectNodes("//delete");
        if (nodes!=null && nodes.size() != 0){
            Map<String, Mapper> mappers = getMapperMap(namespace, nodes,Mapper.DELETE);
            mapperMap.putAll(mappers);
        }
        //获取mapper下的每个update节点
        nodes = rootElement.selectNodes("//update");
        if (nodes!=null && nodes.size() != 0){
            Map<String, Mapper> mappers = getMapperMap(namespace, nodes,Mapper.UPDATE);
            mapperMap.putAll(mappers);
        }

        return mapperMap;
    }
    /**
     * @Description :根据namespace,操作类型节点以及操作类型获取封装的Map
     * @Date 20:19 2020/7/25 0025
     * @Param * @param namespace: dao接口的全限定类名
     * @param nodes :操作类型的节点集合
     * @param sqlType :操作类型
     * @return java.util.Map<java.lang.String,mybatis.Mapper>
     **/
    private static Map<String, Mapper> getMapperMap(String namespace, List nodes,int sqlType) {
        Map<String,Mapper> mapperMap = new HashMap<>();
        for (Object o:nodes){
            Element element = (Element) o;
            //获取select的id属性值,相当于dao接口的方法名
            String daoMethodName = element.attributeValue("id");
            //获取select的resultType的属性值,相当于dao接口的返回值类型
            String resultType = element.attributeValue("resultType");
            //组建一个dao全限定类名和方法名组成的key,比如  dao.UserDao.getAll,标识dao包下的USerDao接口中的getAll方法
            String key = namespace+"."+daoMethodName;
            //获取select标签的sql语句
            String sql = element.getText();
            Mapper mapper = new Mapper();
            //设置返回值类型
            mapper.setResultTypePath(resultType);
            //设置sql语句
            mapper.setSql(sql);
            //将mapper存入Map中
            mapperMap.put(key,mapper);
            //设置sql操作类型
            mapper.setSqlType(sqlType);
        }
        return mapperMap;
    }

    /**
   * @Description :加载XML的配置信息
   * @Date 15:45 2020/7/25 0025
   * @Param * @param inputStream :资源文件流
   * @return mybatis.Configurantion
   **/
    public static Configurantion LoaderConfiguration(InputStream inputStream) {
        //获取读取流对象
        SAXReader reader = new SAXReader();
        Configurantion configurantion = new Configurantion();
        try {
            //根据资源文件流获取document对象
            Document document = reader.read(inputStream);
            //获取文档的根节点
            Element rootElm = document.getRootElement();
            //获取默认的数据库配置
            List list = rootElm.selectNodes("environments");
            if (list == null || list.size() ==0){
                throw  new RuntimeException("尚未配置数据库连接环境信息!");
            }
            //获取environments的id
            Element environmentsEle = (Element) list.get(0);
            String defaultEnvironmentId = environmentsEle.attributeValue("default");
            //获取所有的已经配置的数据库环境
            List environmentNodes = environmentsEle.elements();
            if (environmentNodes == null || environmentNodes.size() ==0){
                throw  new RuntimeException("尚未配置数据库连接环境信息!");
            }
            //获取与默认的environments的id相同的environment节点
            List propertyList = null;
            for (Object o: environmentNodes){
                Element environmentEle = (Element) o;
                //找到默认的数据库连接环境
                if (defaultEnvironmentId.equals(environmentEle.attributeValue("id"))){
                   //获取数据库的链接信息,driver,username,password,url
                    //获取该环境下的property标签
                     propertyList = environmentEle.selectNodes("//property");
                     //跳出循环
                    break;
                }
            }
            if(propertyList  == null){
                throw  new RuntimeException("尚未配置数据库连接环境信息!");
            }
            for (Object p:propertyList  ) {
                Element propertyEle = (Element) p;
                //获取property标签的name属性值
                String name = propertyEle.attributeValue("name");
                if ("url".equals(name)){
                    //设置URL的值
                    configurantion.setUrl(propertyEle.attributeValue("value"));
                }
                if ("driver".equals(name)){
                    //设置driver的值
                    configurantion.setDriver(propertyEle.attributeValue("value"));
                }
                if ("username".equals(name)){
                    //设置username的值
                    configurantion.setUsername(propertyEle.attributeValue("value"));
                }
                if ("password".equals(name)){
                    //设置password的值
                    configurantion.setPassword(propertyEle.attributeValue("value"));
                }
            }
            //定位到mybatis配置文件中的 mappers 中的mapper标签的所有节点
            List mapperNodes = rootElm.selectNodes("mappers/mapper");
            if (mapperNodes == null || mapperNodes.size() == 0){
                throw  new RuntimeException("您未配置mapper的信息!");
            }
            //逐个遍历mapper节点然后获取其中resource的值
            for (Object e:mapperNodes){
                Element element = (Element) e;
                String  daoPath = element.attributeValue("resource");
                if (daoPath != null){
                    //是XML配置
                    Map<String, Mapper> mapperMap = XMLLoader(daoPath);
                    configurantion.setMapperMap(mapperMap);
                }else{
                    //是注解配置
                }
            }

        } catch (DocumentException e) {
            e.printStackTrace();
        }

        return  configurantion;
    }

在进行解析配置文件时,使用了dom4j技术进行解析xml文件。在进行解析的过程中,考虑到mappers的配置有xml配置和注解配置,所以需要分开来,此次首先考虑的是继续xml的配置。为了我们实现功能,我们需要从dao.xml配置文件中获取接口的全限定方法名以及返类型的全限定类名,然后封装到一个mapper中,比如UserDao中的getAll()方法,那么其全限定方法名为“dao.UserDao.getAll”,表示包dao下的接口UserDao中的getAll方法,“entity.User”表示entity包下的User类,每一个方法对应一条Sql语句,同时对应一个mapper,所以一个Configuration中需要有多个mapper,可以使用Map来存取,它的key就是接口的全限定方法名,value就是对应的mapper,其Mapper类的具体代码为:

package mybatis;

/**
 * Classname:Mapper
 *
 * @description:封装sql语句和返回值类型全限定类名
 * @author: 陌意随影
 * @Date: 2020-07-25 17:04
 * @Version: 1.0
 **/
public class Mapper {
    //返回值了类型的类限定名
    private String resultTypePath;
    //sql语句
    private String sql;
    //sql操作类型
    private  int sqlType;
    /**查询类型*/
    public  static int SELECT = 1;
    /**插入类型*/
    public  static int INSERT = 2;
    /**删除类型*/
    public  static int DELETE = 3;
    /**更新类型*/
    public  static int UPDATE = 4;
    public String getResultTypePath() {
        return resultTypePath;
    }
    public int getSqlType() {
        return sqlType;
    }

    public void setSqlType(int sqlType) {
        this.sqlType = sqlType;
    }
    public void setResultTypePath(String resultTypePath) {
        this.resultTypePath = resultTypePath;
    }

    public String getSql() {
        return sql;
    }

    public void setSql(String sql) {
        this.sql = sql;
    }

    @Override
    public String toString() {
        return "Mapper{" +
                "resultTypePath='" + resultTypePath + '\'' +
                ", sql='" + sql + '\'' +
                ", sqlType=" + sqlType +
                '}';
    }
}

然后将封装好的Mapper存入到Configuration中去。我们获取了Configuration后,通过创建一个接口SqlSessionFactory的一个实现类DefaultSqlSessionFactory进行返回一个SqlSessionFactory。
5.紧接着实现SqlSessionFactory接口中的openSession方法,实际上是在SqlSessionFactory的一个实现类DefaultSqlSessionFactory中进行实现,其代码为:

/**
 * Classname:DefaultSqlSessionFactory
 *
 * @description:构建SQLSession的默认工厂
 * @author: 陌意随影
 * @Date: 2020-07-25 17:52
 * @Version: 1.0
 **/
public class DefaultSqlSessionFactory  implements SqlSessionFactory {
     private Configurantion configurantion ;
     public DefaultSqlSessionFactory(Configurantion configurantion){
         this.configurantion = configurantion;
     }
    @Override
    public SqlSession openSession() {
        return new DefaultSqlSession(configurantion);
    }
}

我们发现实际上在调用sqlSessionFactory.openSession();时,实际上是调用new DefaultSqlSession(configurantion)后获取一个SqlSession接口的实现类。
6.实现 sqlSession.getMapper(UserDao.class),该过程中使用了动态代理的方法。其代码为:

 @Override
    public <T> T getMapper(Class<T> daoClass) {
        Object proxyInstance = Proxy.newProxyInstance(daoClass.getClassLoader(), new Class[]{daoClass}, new MapperProxy(configurantion.getMapperMap(), connection));
        return (T) proxyInstance;
    }

在该方法过程中,使用了动态代理进行加强需要被代理的对象。
动态代理底层实现
动态代理具体步骤:
6.1通过实现 InvocationHandler 接口创建自己的调用处理器;
6.2通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
6.3通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
6.4通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
newProxyInstance​(ClassLoader loader,(被代理对象的类加载器)
Class[] interfaces,(被代理对像接口的字节码数组)
InvocationHandler h(由代理实例的调用处理程序实现的接口。)

为了方便,添加InvocationHandler 接口的一个实现类MapperProxy,其代码为:

package mybatis;

import utils.DefaultResultSetHandler;
import utils.MyBatisJDBCUtil;

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

/**
 * Classname:MapperProxy
 *
 * @description:Mapper的代理对象类
 * @author: 陌意随影
 * @Date: 2020-07-25 18:06
 * @Version: 1.0
 **/
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;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //获取方法名
        String methodName = method.getName();
        //获取方法所在的类的名称
        String className = method.getDeclaringClass().getName();
        //组合成一个key
        String key = className+"."+methodName;
        //获取mapperMap中的的对应mapper
        Mapper mapper = mapperMap.get(key);
        //判断是否有对应的mapper
        if (mapper == null){
            throw  new RuntimeException("传的参数有误!");
        }

        //执行查询语句返回结果
        List<Object> objectList = MyBatisJDBCUtil.query(mapper, connection, new DefaultResultSetHandler(),args);
        //获取返回类型
        Class<?> returnType = method.getReturnType();
        //获取集合的字节码用于判断返回类型是否是集合的子类
        Class<Collection> collectionClass = Collection.class;
        if (objectList!= null && objectList.size() != 0){
            if (collectionClass.isAssignableFrom(returnType)){
                //如果返回类型是集合则返回一个集合
                return objectList;
            }else{
                //返回类型是一个对象则直接返回一个对象
                return  objectList.get(0);
            }
        }else{
            return  null;
        }



    }
}

Object invoke​(Object proxy,Method method, Object[] args) throws Throwable处理代理实例上的方法调用并返回结果。 当在与之关联的代理实例上调用方法时,将在调用处理程序中调用此方法。
参数
proxy - 调用该方法的代理实例
method - 对应于在代理实例上调用的接口方法的方法实例。 方法对象的声明类将是方法声明的接口,它可以是代理类继承方法的代理接口的超级接口。
args - 包含代理实例上方法调用中传递的参数值的对象数组,如果接口方法不带参数, null 。 原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean
在进行对被代理对象的接口方法进行加强的时候,需要用到与数据库连接后进行操作的工具类MyBatisJDBCUtil,其具体代码为:

package utils;

import mybatis.Configurantion;
import mybatis.Mapper;

import java.sql.*;
import java.util.List;

/**
 * Classname:MyBatisJDBCUtil
 *
 * @description:获取sql连接的工具类
 * @author: 陌意随影
 * @Date: 2020-07-25 18:17
 * @Version: 1.0
 **/
public class MyBatisJDBCUtil {
    public static Connection getConnection(Configurantion configurantion) {
        try {
            //加载驱动
            Class.forName(configurantion.getDriver());
            try {
                //获取连接
                Connection connection = DriverManager.getConnection(configurantion.getUrl(), configurantion.getUsername(), configurantion.getPassword());
                return  connection;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return  null;
    }
  public  static <T> List<T> query(Mapper mapper, Connection connection, ResultSetHandler resultSetHandler, Object[] param) throws Exception {
      //设置禁止自动提交
        connection.setAutoCommit(false);
      PreparedStatement preparedStatement =  null;
      List<T> t = null;
      try {
          //获取预处理对象
          preparedStatement =  connection.prepareStatement(mapper.getSql());
          //设置占位参数
          if (param != null && param.length != 0){
              for (int i = 0; i < param.length;i++){
                  preparedStatement.setObject(i+1,param[i]);
              }
          }
          //获取结果集
          ResultSet resultSet = preparedStatement.executeQuery();
          //对结果集进行处理
          t = resultSetHandler.resultSet(resultSet,mapper.getResultTypePath());
      }catch (Exception e){
          e.printStackTrace();
      }finally {
          //设置允许自动提交
          connection.setAutoCommit(true);
          //返回处理结果
          return  t;
      }



  }
    public static int update(String sql,Connection connection,String ...param) throws Exception {
        //设置禁止自动提交
        connection.setAutoCommit(false);
        PreparedStatement preparedStatement =  null;
        int  result = -1;
        try {
            //获取预处理对象
            preparedStatement =  connection.prepareStatement(sql);
            //设置占位参数
            if (param != null && param.length != 0){
                for (int i = 0; i < param.length;i++){
                    preparedStatement.setObject(i+1,param[i]);
                }
            }
            //获取结果集
           result = preparedStatement.executeUpdate();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //设置允许自动提交
            connection.setAutoCommit(true);
            //返回处理结果
            return  result;
        }



    }

}

在工具类MyBatisJDBCUtil中,使用到了ResultSetHandler接口用于处理数据库查询后获取的结果集Resultset,ResultSetHandler的一个子类实现DefaultResultSetHandler,其具体的代码为:

package utils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Classname:DefaultResultSetHandler
 * @description:处理结果集
 * @author: 陌意随影
 * @Date: 2020-07-25 21:12
 * @Version: 1.0
 **/
public class DefaultResultSetHandler<T> implements ResultSetHandler {
    /**
     * @param resultTypePath :需要封装到的实体类
     * @return java.util.List<T>
     * @Description :处理并封装结果集对象到对应的实体类中去
     * @Date 22:58 2020/7/25 0025
     * @Param * @param resultSet :包含数据库中实体类表信息的结果集
     **/
    @Override
    public List<T> resultSet(ResultSet resultSet, String resultTypePath) throws Exception {
        //通过反射获取返回类型的字节码
        Class<T> obj = (Class<T>) Class.forName(resultTypePath);
        if (obj == null) {
            throw new IllegalAccessException("封装类型有误!");
        }
        //获取构造器
        Constructor<T> constructor = obj.getConstructor();
        //获取所有的方法
        Method[] objMethods = obj.getMethods();
        List<T> list = new ArrayList<>();
        while (resultSet.next()) {
            //实例化一个实体类
            T t = constructor.newInstance();
            //获取实体类的set方法
            for (Method method : objMethods) {
                //获取方法的名字
                String methodName = method.getName();
                //找到set方法
                if (methodName.startsWith("set")) {
                    //获取set方法对应的属性名称
                    String fieldName = methodName.substring(3);
                    //获取set方法的参数类型,set方法只有一个参数
                    Class<?> parameterType = method.getParameterTypes()[0];
                    //获取set方法参数类型
                    String parameterTypeName = parameterType.getName();
                    //根据属性名从result中的获取对应数据库中的值并填充到实体对象中国
                    if ("java.lang.String".equals(parameterTypeName)) {
                        method.invoke(t, resultSet.getString(fieldName));
                    } else if ("java.lang.Integer".equals(parameterTypeName)) {
                        method.invoke(t, resultSet.getInt(fieldName));
                    } else if ("java.lang.Double".equals(parameterTypeName)) {
                        method.invoke(t, resultSet.getDouble(fieldName));
                    } else if ("java.lang.Byte".equals(parameterTypeName)) {
                        method.invoke(t, resultSet.getByte(fieldName));
                    } else if ("java.lang.Float".equals(parameterTypeName)) {
                        method.invoke(t, resultSet.getFloat(fieldName));
                    } else if ("java.lang.Boolean".equals(parameterTypeName)) {
                        method.invoke(t, resultSet.getBoolean(fieldName));
                    } else if ("java.util.Date".equals(parameterTypeName)) {
                        //将日期转化为指定格式的字符串
                        String dateToStr = DateUtil.dateToStr(resultSet.getDate(fieldName));
                        //将指定的字符串转换为日期
                        Date date = DateUtil.strToDate(dateToStr);
                        method.invoke(t, date);
                    }
                }

            }
            list.add(t);
        }
        return list;
    }
}

7.我们要自己写一个dao接口,用于进行各种操作,其代码为:

package dao;

import entity.User;

import java.util.List;

/**
 * Classname:mybatisdemo
 * @description:User的dao接口
 * @author: 陌意随影
 * @Date: 2020-07-24 10:35
 */
public interface UserDao {
    /**
     * @date: 2020/7/24 0024 10:41
     * @description:获取所有的用户信息
     * @return: 返回包含所有的用户的list
     */
    List<User> getAll();
    /**
     * @Description :根据id获取制定对象
     * @Date 0:32 2020/7/26 0026
     * @Param * @param id :
     * @return entity.User
     **/
   User getOne(int id);

}

通过 UserDao userDao = sqlSession.getMapper(UserDao.class);我们可以获取一个经过动态代理模式加强后的UserDao接口类,当调用UserDao的方法时,会相应自动调用MapperProxy中的invoke方法,从而UserDao的方法会相应得到加强,即使不用自己手动UserDao接口的实现类,也能够完成我们想要的功能。
8.在resources中创建一个包dao,然后在包dao中创建一个UserDao.xml,其代码为:

<?xml version="1.0" encoding="UTF-8" ?>

<!--指定UserDao接口的全限定类名-->
<mapper namespace="dao.UserDao">
    <!-- id即为UserDao接口的getAll()函数的方法名-->
    <select id="getAll" resultType="entity.User">
    select * from User
  </select>
    <select id ="getOne" resultType="entity.User">
        select *from User where id=?;
    </select>
</mapper>

9.到此自定义的mybatis框架基本完成,下面进行测试:

package dao;
import entity.User;
import mybatis.Resources;
import mybatis.SqlSession;
import mybatis.SqlSessionFactory;
import mybatis.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.Test;
import java.io.InputStream;
import java.util.List;
/**
 * Classname:UserDaoTest
 *
 * @description:测试Userdao
 * @author: 陌意随影
 * @Date: 2020-07-24 10:49
 * @Version: 1.0
 **/
public class UserDaoTest {
    @Test
    public void testGetUserAll(){
        try {
            //读取配置文件
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            //实例化SqlSessionFactoryBuilder
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
          //构建SqlSessionFactory
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
           //产生一个SqlSession
            SqlSession sqlSession = sqlSessionFactory.openSession();
            //利用SqlSession创建UserDAao的代理对象
            UserDao userDao = sqlSession.getMapper(UserDao.class);
            //利用代理对象执行方法
            List<User> userList = userDao.getAll();

            for (User u:userList ) {
                System.out.println(u);
            }
            User user = userDao.getOne(1);
            //关闭资源
            sqlSession.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
在这里插入图片描述

10.项目的目录为:
在这里插入图片描述
在这里插入图片描述
11.本项目的数据库MySQL文件User表为:

CREATE TABLE `user` (
  `id` int(4) NOT NULL AUTO_INCREMENT,
  `name` varchar(16) NOT NULL,
  `password` varchar(16) NOT NULL,
  `age` int(3) DEFAULT NULL,
  `createtime` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

12本项目源码已经打包,有需要请直接点击下载(下载解压后直接导入idea即可):http://moyisuiying.com/wp-content/uploads/2020/07/mybatis-self.rar


繁华落尽,雪花漫天飞舞。