Spring笔记(06) Mybatis底层源码实现

本篇通过模仿Mybatis实现过程,探索底层源码,更好的熟悉Mybatis。本文大部分摘抄案例上手 Spring 全家桶,版权属于他们 ,等会文末会提供地址链接。

1、创建一个类,实现 InvocationHandler 接口,该类就具备了创建动态代理对象的功能,定义两个核心方法。

自定义 getInstance 方法:入参为目标对象,通过 Proxy.newProxyInstance 方法创建代理对象,并返回。

/**
     * 入参为目标对象
     * @param clazz
     * @return
     */
    public Object getInstance(Class clazz){
        Object newProxyInstance = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this);
        return newProxyInstance;
    }

实现接口的 invoke 方法,通过反射机制完成业务逻辑代码。

@Override
public Object invoke(Object proxy, Method method, Object[] args)
  throws Throwable {
  // TODO Auto-generated method stub
  return null;
}

invoke 方法是核心代码,在该方法中实现具体的业务需求,接下来我们来看如何实现。

既然是对数据库进行操作,则一定需要数据库连接对象,数据库相关信息配置在 config.xml 中。因此 invoke 方法第一步,就是要解析 config.xml,创建数据库连接对象,使用 C3P0 数据库连接池。

/**
     * 解析数据源
     * @return
     * @throws DocumentException
     */
    public static Map<String,String> getC3P0Properties() throws DocumentException {
        Map<String, String> map = new HashMap<>();
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read("/Users/guozh/Workspace/Java/mybaitis-demo/src/main/resources/config.xml");
        //根节点
        Element rootElement = document.getRootElement();
        Iterator it01 = rootElement.elementIterator();
        while(it01.hasNext()){
            Element ele01 = (Element) it01.next();
            if(ele01.getName().equals("environments")){
                Iterator it02 = ele01.elementIterator();
                while(it02.hasNext()){
                    Element el02 = (Element) it02.next();
                    if(el02.getName().equals("environment")){
                        Iterator it03 = el02.elementIterator();
                        while(it03.hasNext()){
                            Element el03 = (Element) it03.next();
                            if(el03.getName().equals("dataSource")){
                                Iterator it04 = el03.elementIterator();
                                while(it04.hasNext()){
                                    Element el04 = (Element) it04.next();
                                    map.put(el04.attributeValue("name"),el04.attributeValue("value"));
                                }
                            }
                        }
                    }
                }
            }
        }
        return map;
    }
/**
 * 获取 C3P0 信息,创建数据源对象
 * @return
 */
public Connection createDataSources(){
    try {
        Map<String,String> map = getC3P0Properties();
        ComboPooledDataSource datasource = new ComboPooledDataSource();
        datasource.setDriverClass(map.get("driver"));
        datasource.setJdbcUrl(map.get("url"));
        datasource.setUser(map.get("username"));
        datasource.setPassword(map.get("password"));
        datasource.setInitialPoolSize(20);
        datasource.setMaxPoolSize(40);
        datasource.setMinPoolSize(2);
        datasource.setAcquireIncrement(5);
        Connection conn = datasource.getConnection();
        return conn;
    } catch (DocumentException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    } catch (PropertyVetoException e) {
        e.printStackTrace();
    }
    return null;
}

有了数据库连接,接下来就需要获取待执行的 SQL 语句,SQL 的定义全部写在 StudentDAO.xml 中。

继续解析 XML,执行 SQL 语句,SQL 执行完毕,查询结果会保存在 ResultSet 中,还需要将 ResultSet 对象中的数据进行解析,封装到 POJO 中返回,这一功能同样需要两步操作。

  • 反射机制创建 Student 对象。
  • 通过反射动态执行类中所有属性的 setter 方法,完成赋值。

这样就将 ResultSet 中的数据封装到 POJO 中了。

//获取 sql 语句
String sql = element.getText();
//获取参数类型
String parameterType = element.attributeValue("parameterType");
//创建 pstmt
PreparedStatement pstmt = createPstmt(sql,parameterType,conn,args);
ResultSet rs = pstmt.executeQuery();
if(rs.next()){
    //读取返回数据类型
    String resultType = element.attributeValue("resultType");   
    //反射创建对象
    Class clazz = Class.forName(resultType);
    obj = clazz.newInstance();
    //获取 ResultSet 数据
    ResultSetMetaData rsmd = rs.getMetaData();
    //遍历实体类属性集合,依次将结果集中的值赋给属性
    Field[] fields = clazz.getDeclaredFields();
    for(int i = 0; i < fields.length; i++){
        Object value = setFieldValueByResultSet(fields[i],rsmd,rs);
        //通过属性名找到对应的 setter 方法
        String name = fields[i].getName();
        name = name.substring(0, 1).toUpperCase() + name.substring(1);
        String MethodName = "set"+name;
        Method methodObj = clazz.getMethod(MethodName,fields[i].getType());
        //调用 setter 方法完成赋值
        methodObj.invoke(obj, value);
        }
}

以上是核心思路,甚至代码不全,但不妨碍学习。

以上代码笔记内容来自付费专栏:案例上手 Spring 全家桶

PS:并没有透露关键内容,纯粹是零碎笔记。如果侵犯版权,请联系我。

本文由老郭种树原创,转载请注明:https://guozh.net/spring-mybatis/

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注