手写Mybatis框架--通过动态代理简单实现查询功能

在这里插入图片描述

前言

Mybatis就是一个封装来jdbc持久层及框架,它让程序员只关注sql本身,而不需要去关注如连接的创建,statment的创建等操作,它会将输入参数,输出结果进行映射

**持久层:**数据保持在数据库或者硬盘一类可以保存很长时间的设备里面,不像放在内存中那样断电就消失了,也就是把数据存在持久化设备上。

问题1:在用mybatis框架时,只写了接口,没写实现类,拿到对象,对象如何来的?

问题2:在用mybatisn框架,那个类帮你实现数据库连接,执行sql,遍历结果集

正文

一、Mybatis框架执行流程

在这里插入图片描述

思路明确了,那么设计一下简化版的mybatis框架来完成一个查询。

二、简化Mybatis

项目结构图:
在这里插入图片描述

1.连接数据库

配置环境:两个jar包–>mysql-connector-java-5.1.32.jar和jaxen-1.1-beta-11.jar

思路:

​ 配置文件有两种,一个为主配置文件,一个为映射文件。

​ 主配置文件:配置了jdbc等环境信息。

​ 映射文件:配置了接口对应的sql语句映射。

主配置文件代码:

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <dataSource>
        <property name="driver" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/testdb"></property>

        <property name="username" value="root"></property>
        <property name="password" value="root"></property>

    </dataSource>
    <mappers>
        <mapper resource="UserMapper.xml"></mapper>
        <mapper resource="ItemMapper.xml"></mapper>

    </mappers>
</configuration>

映射sql文件代码

<?xml version="1.0" encoding="UTF-8" ?>
<select id="com.tedu.UserMapper.selectAll" resultType="com.tedu.User">
    select * from user
</select>

将xml文件解析出来:

package org.mybatis.configuration;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

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

public class XmlConfigParser {
    public static  Configuration configuration=new Configuration();

    public static void parser(InputStream inputStream)throws  Throwable{
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(inputStream);
        List<Element> list = document.selectNodes("//property");
        for (Element element:list){
            String valueOfname = element.attributeValue("name");
            String valueOfvalue = element.attributeValue("value");
            switch (valueOfname){
                case "driver":
                    configuration.setDriver(valueOfvalue);
                    break;
                case "url":
                    configuration.setUrl(valueOfvalue);
                    break;
                case "username":
                    configuration.setUsername(valueOfvalue);
                    break;
                case "password":
                    configuration.setPassword(valueOfvalue);
                    break;
            }

        }
        //解析mapper.xml
        HashMap<String, SqlMapper> hashMap = new HashMap<>();
        configuration.setSqlMappers(hashMap);

        List<Element> mapperList = document.selectNodes("//mapper");
        for (Element element:mapperList){
            String mapperFileName = element.attributeValue("resource");
            parserMapper(mapperFileName);
        }
        System.out.println(configuration.toString());
    }

    private static void parserMapper(String mapperFileName) throws Throwable{
        ClassLoader classLoader = XmlConfigParser.class.getClassLoader();
        InputStream inputStream = classLoader.getResourceAsStream(mapperFileName);

        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(inputStream);
        List<Element> list = document.selectNodes("//select");

        for (Element element:list){
            String id = element.attributeValue("id");
            String resultType = element.attributeValue("resultType");
            String sql = element.getText();

            SqlMapper sqlMapper = new SqlMapper(id, resultType, sql);
            configuration.getSqlMappers().put(id,sqlMapper);
        }
    }
}

2、动态代理

环境配置:dom4j-1.6.1.jar

思路:sqlSession肯定不会自己去执行,因为不能写死所以使用动态代理来使代理类去实现具体方法。

package org.mybatis.executor;

import java.lang.reflect.Proxy;

public class SqlSession {
    public Object getMapper(Class clazz) throws Throwable {
        ClassLoader classLoader = clazz.getClassLoader();
        Class[] interfaces={clazz};
        MapperProxy mapperProxy = new MapperProxy();
        Object proxyObject = Proxy.newProxyInstance(classLoader, interfaces, mapperProxy);
        return  proxyObject;
    }
}

package org.mybatis.executor;

import org.mybatis.configuration.Configuration;
import org.mybatis.configuration.SqlMapper;
import org.mybatis.configuration.XmlConfigParser;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;

public class MapperProxy implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("invoke method="+method.getName() );
        //1,得到id
        String methodName=method.getName();
        String interfaceName = method.getDeclaringClass().getName();
        String id=interfaceName+"."+methodName;
        //2,从xmlConfigParser中得到configuration
        Configuration configuration = XmlConfigParser.configuration;
        //3,连接数据库
        //3.1加载驱动
        Class.forName(configuration.getDriver());
        //3.2 建立连接
        Connection connection = DriverManager.getConnection(configuration.getUrl(),
                configuration.getUsername(),
                configuration.getPassword());
        //4,执行sql
        SqlMapper sqlMapper = configuration.getSqlMappers().get(id);
        String sql = sqlMapper.getSql();
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //5,得到结果集
        ResultSet resultSet = preparedStatement.executeQuery();
        //6,遍历结果集
        int row=0;
        ArrayList<Object> list = new ArrayList<>();
        while (resultSet.next()){
            row++;
            //6.1创建实体类对象
            String resultType = sqlMapper.getResultType();
            Class clazz = Class.forName(resultType);
            Object entity = clazz.newInstance();
            //6.2给对象属性赋值
            /*
            id,username
            1,root
            entity{id=null,username=null}
             */
           // 6.2.1 取列的信息
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            //6.2.1 遍历列
            for(int i=1;i<=columnCount;i++){
                //取列名
                String columnName = metaData.getColumnName(i);
                System.out.print(columnName+"|");
                //取列的值
                Object dbData = resultSet.getObject(columnName);
                //根据列名创建field(entity的属性)
                Field field = clazz.getDeclaredField(columnName);

                //给属性赋值
                field.setAccessible(true);//private-->public
                //entity.field=dbData
                field.set(entity,dbData);
            }
            System.out.println("");

            //6.3把对象放在集合中
            list.add(entity);

        }
        //7,返回list
        return list;
    }
}

3、executor执行

准备:先创建两个实体类,一个是对应接口实体类,一个是xml文件sql实体类

sql实体类:

package com.tedu;
//实体类
public class User {
    Integer id;
    String username;

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                '}';
    }
}

对应接口实体类:

package com.tedu;

import java.util.List;

public interface UserMapper {
    public List<User> selectAll();
}

4、测试类
package org.mybatis;

import com.tedu.Item;
import com.tedu.ItemMapper;
import com.tedu.User;
import com.tedu.UserMapper;
import org.mybatis.configuration.SqlMapper;
import org.mybatis.configuration.XmlConfigParser;
import org.mybatis.executor.SqlSession;

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

public class MybatisTest {
    public static void main(String[] args) throws  Throwable{
        ClassLoader classLoader = MybatisTest.class.getClassLoader();
        InputStream inputStream =
                classLoader.getResourceAsStream("mybatis-config.xml");

        XmlConfigParser.parser(inputStream);

        SqlSession sqlSession = new SqlSession();
        Object proxyObject = sqlSession.getMapper(UserMapper.class);
        UserMapper userMapper= (UserMapper) proxyObject;
        List<User> list = userMapper.selectAll();
        System.out.println(list);

        Object itemProxy = sqlSession.getMapper(ItemMapper.class);
        ItemMapper itemMapper= (ItemMapper) itemProxy;
        List<Item> items = itemMapper.selectAll();
        System.out.println(items);

    }
}

总结:

  • 连接数据库的初始化工作
    ect;
    List list = userMapper.selectAll();
    System.out.println(list);

      Object itemProxy = sqlSession.getMapper(ItemMapper.class);
      ItemMapper itemMapper= (ItemMapper) itemProxy;
      List<Item> items = itemMapper.selectAll();
      System.out.println(items);
    

    }
    }






## 总结:

- 连接数据库的初始化工作
- 建立xml配置文件的关系,使得我们调用方法就可以自动执行xml文件中的SQL语句
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值