Mybatis学习1:从零手写Mybatis精简版框架

前言

  参考博客:https://zhuanlan.zhihu.com/p/67086285

   不废话,直接上源码

框架目录

 

 

源码如下  

Function类
package com.hs.tyj.config;


/**
 * @description:用于映射sql.xml中的sql方法
 * @date:2019/6/20
 * @author:tangyj
 * @remark:
 * */
public class Function {

    private String sqlType;
    private String funcName;
    private String sql;
    private Object resultType;
    private String parameterType;

    public String getSqlType() {
        return sqlType;
    }

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

    public String getFuncName() {
        return funcName;
    }

    public void setFuncName(String funcName) {
        this.funcName = funcName;
    }

    public String getSql() {
        return sql;
    }

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

    public Object getResultType() {
        return resultType;
    }

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

    public String getParameterType() {
        return parameterType;
    }

    public void setParameterType(String parameterType) {
        this.parameterType = parameterType;
    }
}

  MapperBean类

package com.hs.tyj.config;

import java.util.List;

/**
 * @description:用于映射sql.xml
 * @date:2019/6/20
 * @author:tangyj
 * @remark:
 * */
public class MapperBean {

    private String interfaceName;
    private List<Function> list;

    public String getInterfaceName() {
        return interfaceName;
    }

    public void setInterfaceName(String interfaceName) {
        this.interfaceName = interfaceName;
    }

    public List<Function> getList() {
        return list;
    }

    public void setList(List<Function> list) {
        this.list = list;
    }
}

  User类

package com.hs.tyj.entity;

public class User {

    private Integer id;
    private String userName;
    private String password;

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

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

  UserMapper类

package com.hs.tyj.mapper;

import com.hs.tyj.entity.User;

import java.util.List;

public interface UserMapper {

    public User getUserById(Integer id);

}

  Excutor类

package com.hs.tyj.sqlSession;

public interface Excutor {

    public <T> T query(String stattement, Object parameter);

}

  MyConfiguration类

package com.hs.tyj.sqlSession;


import com.hs.tyj.config.Function;
import com.hs.tyj.config.MapperBean;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;


/**
 * @description:Configuration类,1-读取数据库相关配置-2-提供数据库链接方法-3-解析sql.Mapper文件
 * @date:2019/6/20
 * @author:tangyj
 * @remark:
 * */
public class MyConfiguration {

    private static ClassLoader loader = ClassLoader.getSystemClassLoader();


    /**
     * @description:读取xml信息并处理
     * @param:[resource]
     * @return:com.mysql.jdbc.Connection
     * @date:2019/6/19
     * @author:tangyj
     * @remark:
     * */
    public Connection bulid(String resource){
        try {
            InputStream resourceAsStream = loader.getResourceAsStream(resource);
            SAXReader reader = new SAXReader();//SAXReader creates a DOM4J tree from SAX parsing events.
            Document document = reader.read(resourceAsStream);
            Element rootElement = document.getRootElement();
            return evalDataSource(rootElement);
        } catch (Exception e) {
            e.printStackTrace();
            throw  new RuntimeException("error accured while eval xml " + resource);
        }
    }


    /**
     * @description:解析mapper.xml文件
     * @param:[path]
     * @return:com.hs.tyj.config.MapperBean
     * @date:2019/6/19
     * @author:tangyj
     * @remark:
     * */
    @SuppressWarnings("rawtypes")
    public MapperBean readMapper(String path){
        MapperBean mapper = new MapperBean();

        try {
            InputStream resourceAsStream = loader.getResourceAsStream(path);
            SAXReader reader = new SAXReader();
            Document document = reader.read(resourceAsStream);
            Element rootElement = document.getRootElement();

            mapper.setInterfaceName(rootElement.attributeValue("nameSpace").trim());//把mapper节点的nameSpace值存为接口名
            List<Function> list = new ArrayList<Function>();
            for(Iterator rootIter = rootElement.elementIterator();rootIter.hasNext();){//遍历跟节点下的所有子节点
                Function func = new Function();
                Element e = (Element)rootIter.next();
                String sqlType = e.getName().trim();
                String funcName = e.attributeValue("id").trim();
                String sql = e.getText().trim();
                String resultType = e.attributeValue("resultType").trim();

                func.setSqlType(sqlType);
                func.setFuncName(funcName);
                Object newInstance = null;

                try {
                    newInstance = Class.forName(resultType).newInstance();
                } catch (InstantiationException e1) {
                    e1.printStackTrace();
                } catch (IllegalAccessException e1) {
                    e1.printStackTrace();
                } catch (ClassNotFoundException e1) {
                    e1.printStackTrace();
                }

                func.setResultType(newInstance);
                func.setSql(sql);
                list.add(func);
            }
            mapper.setList(list);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return mapper;
    }

    //本类私有方法
    //1-解析数据库配置
    private Connection evalDataSource(Element node) throws ClassNotFoundException {
        if(!node.getName().equals("database")){
            throw  new RuntimeException("root should be <database>");
        }
        String driverClassName = null;
        String url = null;
        String userName = null;
        String password = null;

        //1-获取属性节点
        List property = node.elements("property");
        for(Object item : property){
            Element i = (Element)item;
            String value = getValue(i);
            String name = i.attributeValue("name");
            if(name == null || value == null){
                throw  new RuntimeException("[database]:<property> should contain name and value");
            }
            //2-赋值
            if(name.equals("driverClassName")){
                driverClassName = value;
            }else if(name.equals("url")){
                url = value;
            }else if(name.equals("username")){
                userName = value;
            }else if(name.equals("password")){
                password = value;
            }else{
                throw new RuntimeException("[database]:<property> unknow name");
            }
        }
        Class.forName(driverClassName);
        Connection connection = null;

        try {
            connection = DriverManager.getConnection(url, userName, password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;

    }
    //2-获取property的属性的值,如果有value值,则读取,则设置
    private String getValue(Element node){
        return node.hasContent()? node.getText() : node.attributeValue("value");
    }
}

   MyExcutor类

package com.hs.tyj.sqlSession;

import com.hs.tyj.entity.User;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;


/**
 * @description:Excutor实现类;提供数据库查询等方法
 * @date:2019/6/20
 * @author:tangyj
 * @remark:
 * */
public class MyExcutor implements  Excutor{

    private MyConfiguration xmlConfiguration = new MyConfiguration();


    //1-获取单条数据
    public <T> T query(String sql, Object parameter) {
        Connection connection = getConnecttion();
        ResultSet set = null;
        PreparedStatement pre = null;

        try {
            pre = connection.prepareStatement(sql);
            //设置参数
            pre.setString(1,parameter.toString());
            set = pre.executeQuery();

            User u = new User();
            //遍历结果集
            while(set.next()){
                u.setId(set.getInt(1));
                u.setUserName(set.getString(2));
                u.setPassword(set.getString(3));
            }
            return (T)u;
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            try {
                if(set != null){
                    set.close();
                }
                if(pre != null){
                    pre.close();
                }
                if(connection != null){
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }

        }
        return null;
    }

    //本类私有方法
    private Connection getConnecttion(){

        try {
            Connection connection = xmlConfiguration.bulid("config.xml");
            return connection;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}

   MyMapperProxy类

package com.hs.tyj.sqlSession;

import com.hs.tyj.config.Function;
import com.hs.tyj.config.MapperBean;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;


/**
 * @description:Mapper接口代理,主要功能:读取sql.xml文件,通过动态代理防范,实现UserMapper接口
 * @date:2019/6/20
 * @author:tangyj
 * @remark:
 * */
public class MyMapperProxy implements InvocationHandler{

    private MySqlSession mySqlSession;

    private MyConfiguration myConfiguration;

    public MyMapperProxy(MySqlSession mySqlSession, MyConfiguration myConfiguration) {
        this.mySqlSession = mySqlSession;
        this.myConfiguration = myConfiguration;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MapperBean readMapper = myConfiguration.readMapper("UserMapper.xml");//读取sql.xml,并将将接口名称和方法体封装到MapperBean;
        //是否是文件对应的接口
        if(!method.getDeclaringClass().getName().equals(readMapper.getInterfaceName())){
            return  null;
        }

        List<Function> list = readMapper.getList();//读取方法体
        if(null != list || 0 != list.size()){
            for(Function func : list){
                //id是否和接口方法名一样
                if(method.getName().equals(func.getFuncName())){
                    return mySqlSession.selectOne(func.getSql(),String.valueOf(args[0]));//执行selectOne方法
                }
            }
        }
        return null;
    }
}

  MySqlSession类

package com.hs.tyj.sqlSession;

import java.lang.reflect.Proxy;
import java.util.List;


/**
 * @description:sqlSession类,SqlSession提供select/insert/update/delete方法
 * @date:2019/6/20
 * @author:tangyj
 * @remark:
 * */
public class MySqlSession {

    private Excutor excutor = new MyExcutor();

    private MyConfiguration myConfiguration = new MyConfiguration();

    //1-获取单条数据
    public <T> T selectOne(String statement, Object parameter){
        return excutor.query(statement,parameter);
    }

    public <T> T getMapper(Class<T> clas){
        //动态代理
        return (T) Proxy.newProxyInstance(clas.getClassLoader(),new Class[]{clas},new MyMapperProxy(this,myConfiguration));
    }

}

  TestMybatis类

package com.hs.tyj;

import com.hs.tyj.entity.User;
import com.hs.tyj.mapper.UserMapper;
import com.hs.tyj.sqlSession.MySqlSession;

import java.util.List;

public class TestMybatis {

    public static void main(String[] args){
        MySqlSession mySqlSession = new MySqlSession();
        UserMapper mapper = mySqlSession.getMapper(UserMapper.class);
        User user = mapper.getUserById(1);
        System.out.println(user);
    }
}

  config.xml

<?xml version="1.0" encoding="UTF-8"?>
<database>
    <property name="driverClassName" >com.mysql.jdbc.Driver</property>
    <property name="url" >jdbc:mysql://XXXXX:3306/saichang?useUnicode=true&amp;characterEncoding=utf8</property>
    <property name="username" >saichang</property>
    <property name="password" >saichang1234</property>
</database>

  UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<mapper nameSpace="com.hs.tyj.mapper.UserMapper">

    <select id="getUserById" resultType="com.hs.tyj.entity.User">
        select * from user where id = ?
    </select>
</mapper>

  pom文件

<?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>com.hs</groupId>
    <artifactId>tyj-mybatis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <java.version>1.8</java.version>
    </properties>


    <dependencies>

        <!-- https://mvnrepository.com/artifact/dom4j/dom4j -->
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.29</version>
        </dependency>

    </dependencies>
</project>

 

 

 

数据设计

 

 

 日志数据

User{id=1, userName='我不吃番茄', password='123456'}

 

posted on 2019-06-20 20:56  我不吃番茄  阅读(869)  评论(0编辑  收藏  举报