手写极简mybatis

手写极简Mybatis


  1. 加载已经配置的mybatis-config.xml,从里边加载数据源和mapper.xml的存放路径
  2. 并将mapper.xml里面的通过全类名(也就是namespace)作为key以及包装的sql作为value,最后存于一个map中
  3. 然后建立一个Session,通过代理和map中的sql生成代理对象从而实现对数据库的增删查改

大致方法执行过程

 public static void main(String[] args) throws SQLException {
        SessionFactory sessionFactory = new SessionFactory("config/mybatis-config.xml");
        Session session = sessionFactory.openSession();

        UserDao mapper = session.getMapper(UserDao.class);
        session.begin();
        mapper.saveUser(new User(1,"11","111"));
        session.commit();
    }

1.配置文件准备

mybatis.config.xml

<mybatis>
    <dataSource>druid</dataSource>
    <mapper>config/UserMapper.xml</mapper>
</mybatis>

druid.properties

druid.username=root
druid.password=123
druid.url=jdbc:mysql://127.0.0.1:3306/ssm?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
druid.driverClassName=com.mysql.jdbc.Driver

2.创建数据源

由于嫌麻烦所以只设置了一个数据源

public static DataSource createDataSource(String type) {
        DataSource dataSource = null;

        Properties properties = new Properties();
        if (DATASOURCE.equals(type)) {
            try {
                properties.load(DataSourceFactory.class.getClassLoader()
                        .getResourceAsStream("config/hikari.properties"));
            } catch (Exception e) {
                e.printStackTrace();
            }

            DruidDataSource druidDataSource = new DruidDataSource();
            druidDataSource.configFromPropety(properties);
            dataSource = druidDataSource;
        }
        return dataSource;
    }

3.创建会话工厂

SessionFactory.java

package com.mybatis.core;

import com.mybatis.DataSourceFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;

/**
 * @author 2358189586
 */
public class SessionFactory {

    private DataSource dataSource;
    private Map<String, Map<String, DaoWrapper>> env = new HashMap<>(8);

    public SessionFactory(String config) {
        loadXml(config);
    }


    // 打开一个会话
    public Session openSession() {
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return new Session(connection, env);
    }

    // 加载资源环境
    public void loadXml(String config) {
        try {
            SAXReader reader = new SAXReader();
            Document configDom = reader.read(Session.class.getClassLoader().getResourceAsStream(config));
            Element configRoot = configDom.getRootElement();
            String dataSourceType = configRoot.element("dataSource").getTextTrim();
            dataSource = DataSourceFactory.createDataSource(dataSourceType);
            List mapperElements = configRoot.elements("mapper");
            List<String> mapperPaths = new ArrayList<>();
            for (Object element : mapperElements) {
                Element mapper = (Element) element;
                mapperPaths.add(mapper.getTextTrim());
            }
            for (String mapperPath : mapperPaths) {
                Map<String, DaoWrapper> wrapper = new HashMap<>(2);
                Document document = reader.read(Session.class.getClassLoader().getResourceAsStream(mapperPath));
                Element root = ((org.dom4j.Document) document).getRootElement();
                String namespace = root.attribute("namespace").getValue();
                Iterator iterator = root.elementIterator();
                while (iterator.hasNext()) {
                    Element el = (Element) iterator.next();
                    String type = el.getName();
                    String id = el.attribute("id").getValue();
                    String resultType = el.attribute("resultType").getValue();
                    String paramType = el.attribute("paramType").getValue();
                    String sqlStr = el.getTextTrim();
                    wrapper.put(id, new DaoWrapper(type, resultType, paramType, sqlStr));
                }
                env.put(namespace, wrapper);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }
}

Session.java

package com.mybatis.core;

import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 2358189586
 */
public class Session {
    /**
     * 每个会话持有一个链接
     */
    private Connection connection;
    /**
     * 当前会话的上下文
     */
    private Map<String, Map<String, DaoWrapper>> env = new HashMap<>(8);

    public Session(Connection connection, Map<String, Map<String,
            DaoWrapper>> env) {
        this.connection = connection;
        this.env = env;
    }

    /**
     * 拿到一个包装
     * 类
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T getMapper(Class<T> clazz) {
        T t = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(),
                new Class[]{clazz},
                new SQLHandler(connection, clazz, env.get(clazz.getName())));
        return t;
    }

    /**
     * // 开始会话
     */
    public void begin() {
        try {
            connection.setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * // 提交
     */
    public void commit() {
        try {
            connection.commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * // 回滚
     */
    public void rollback() {
        try {
            connection.rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

对于每条sql的封装

DaoWrapper

package com.mybatis.core;

/**
 * @author 2358189586
 */
public class DaoWrapper {
    /**
     * 类型,insert|update|delete
     */
    private String type;
    /**
     * 返回值的类型
     */
    private String resultType;
    /**
     * 参数的类型
     */
    private String paramType;
    /**
     * sql语句
     */
    private String sql;


    public DaoWrapper(){}

    public DaoWrapper(String type, String resultType, String paramType, String sql) {
        this.type = type;
        this.resultType = resultType;
        this.paramType = paramType;
        this.sql = sql;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getResultType() {
        return resultType;
    }

    public void setResultType(String resultType) {
        this.resultType = resultType;
    }

    public String getParamType() {
        return paramType;
    }

    public void setParamType(String paramType) {
        this.paramType = paramType;
    }

    public String getSql() {
        return sql;
    }

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

    @Override
    public String toString() {
        return "DaoWrapper{" +
                "type='" + type + '\'' +
                ", resultType='" + resultType + '\'' +
                ", paramType='" + paramType + '\'' +
                ", sql='" + sql + '\'' +
                '}';
    }
}

4.代理对象的生成

SQLHandler.java

package com.mybatis.core;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author 2358189586
 */
public class SQLHandler implements InvocationHandler {
    /**
     * 需传入一个链接
     */
    private Connection connection;
    /**
     * 需传入一个dao的类型
     */
    private Class clazz;
    /**
     * 需传入一个独立的环境
     */
    private Map<String, DaoWrapper> env;

    public SQLHandler(Connection connection, Class clazz, Map<String,
            DaoWrapper> env) {
        this.connection = connection;
        this.clazz = clazz;
        this.env = env;
    }

    /**
     * 生成代理对象
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws
            Throwable {
       // 拿到包装
        DaoWrapper wrapper = env.get(method.getName());
        PreparedStatement statement =
                connection.prepareStatement(wrapper.getSql());
        // 对每一种sql语句进行独立的操作
        if ("insert".equals(wrapper.getType())) {
            String paramType = wrapper.getParamType();
            // 暂定传入一个对象
            Class<?> clazz = args[0].getClass();
            Field[] fields = clazz.getDeclaredFields();


            for (int i = 0; i < fields.length; i++) {
                fields[i].setAccessible(true);
                statement.setObject(i + 1, fields[i].get(args[0]));
            }
            return statement.executeUpdate();
        } else if ("delete".equals(wrapper.getType())) {
            for (int i = 0; i < args.length; i++) {
                statement.setObject(i + 1, args[i]);
            }
            return statement.executeUpdate();
        } else if ("select".equals(wrapper.getType())) {
            for (int i = 0; i < args.length; i++) {
                statement.setObject(i + 1, args[i]);
            }
            ResultSet result = statement.executeQuery();
            List list = new ArrayList();
            while (result.next()) {
                Class<?> clazz = Class.forName(wrapper.getResultType());
                Object object = clazz.newInstance();
                Field[] fields = clazz.getDeclaredFields();
                for (int i = 0; i < fields.length; i++) {
                    fields[i].setAccessible(true);
                    fields[i].set(object,
                            result.getObject(fields[i].getName()));
                }
                list.add(object);
            }
            return list;
        }
        return null;
    }
}

END:本文的实体类和Dao接口请自行生成

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值