手写mybatis框架

我们首先要了解JDBC和mybtis的执行流程
  • JDBC的执行流程
/**
 -  JDBC的执行流程,mybatis其实就是对ibatis的封装,而ibatis的底层封装了JDBC的操作
*/
public static void main(String[] args)  {
        try {
            // 1.加载JDBC的驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2.通过驱动管理器获取connection
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
            // 3.通过Connection获取statement
            Statement statement = connection.createStatement();
            // 4.statement相当于数据库操作对象,statement去执行sql语句
            statement.execute("select * from user;");
            // 5.获取数据库返回的结果集
            ResultSet resultSet = statement.getResultSet();
        }catch (Exception e){
            e.printStackTrace();
        }
}
  • mybatis的执行流程
/**
 *  mybatis的执行流程:
 *      1.根据配置文件myBatis-config.xml加载初始化参数
 *      2.通过configuration去build一个SqlSessionFactory
 *      3.获取一个SqlSession
 *      4.最后通过SqlSession获取一个接口的代理类,去执行SQL语句,返回执行结果
 */
public class TestMain {

    private SqlSession session;

    @Before
    public void before() throws Exception{
        InputStream is = Resources.getResourceAsStream("myBatis-config.xml");
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(new InputStreamReader(is));
        session = sessionFactory.openSession();
    }


    @After
    public void after(){
        try {
            session.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }


    @Test
    public void test(){
        DeptDao dao = session.getMapper(DeptDao.class);
        List list = dao.findAll();
        System.out.println(list.get(0));
        System.out.println(list.get(1));
    }
}

手写mybtis框架

加载配置文件,这里我们只加载mapper中的sql语句;我们根据DAO中的方法和XML的SQL标签的ID一一对应;
提供一个根据方法名去获取DAO方法对应的SQL和该SQL执行后返回的数据类型;
public class Configuration {

    private Connection connection;

    private String path;

    private String daoName;

    private Map sqlMap = new HashMap();

    private Map typeMap = new HashMap();

    public Map getSqlMap() {
        return sqlMap;
    }

    public Map getTypeMap() {
        return typeMap;
    }

    public synchronized Connection getConnection() {
        return connection;
    }

    public Configuration(String path){
        this.path = path;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
        }catch (Exception e){
            e.printStackTrace();
        }
        initConfiguration();
    }

    public void close(){
        try {
            if (connection != null){
                connection.close();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 初始化配置文件,此处我们只做简单的处理mapper文件,
     * 其它:
     *      plugins(配置拦截器)
     *      properties(一般存放数据链接和账号密码信息)
     *      environment(读取properties信息生成数据链接对象)
     *      transactionManager(事务管理,一般交给spring管理)
     *      dataSource:
     *                  dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。数据源类型有三种:UNPOOLED,POOLED,JNDI。
     *                  UNPOOLED是没有数据库连接池的,没执行一次操作,打开一次数据库,关闭一次数据库.效率较为低下
     *                  POOLED是存在数据库连接池的,没有操作数据库从数据库连接池中拿取连接
     *                  JNDI这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
     *      mappers:它是数据库mapper.xml
     *
     */
    public void initConfiguration() {
        try {
            //1.读取资源文件夹mapper,该文件夹的XML和DAO一一对应,XML中的sql标签的id和DAO中的方法一一对应,读取mapper文件是使用了dom4j技术
            URL url = Configuration.class.getClassLoader().getResource("");
            File fileFolder = new File(url.getPath() + path);
            String[] fileStrs = fileFolder.list();
            for (int i=0;fileStrs != null && fileStrs.length > i;i++){
                String fullDaoName = fileStrs[i];
                daoName = fullDaoName.substring(0,fullDaoName.indexOf("."));
                InputStream in = Resources.getResourceAsStream(path + "/" + fullDaoName);
                SAXReader saxReader = new SAXReader();
                Document document = saxReader.read(new InputStreamReader(in));
                setSqlMapAndTypeMethod(document.getRootElement(),(String) null);
            }
            //System.out.println(sqlMap);
            //System.out.println(typeMap);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 利用递归遍历xml文件,获取dao对应xml文件的sql和返回值类型
     *
     * @param rootElement
     * @param methodName
     */
    public void setSqlMapAndTypeMethod(Element rootElement,String methodName){

        List<Attribute> attributeList = rootElement.attributes();
        for (int i=0;attributeList != null && attributeList.size()>0 && attributeList.size() > i;i++){
            Attribute attribute = attributeList.get(i);
            String name = attribute.getName();
            String value = attribute.getValue();
            if("id".equals(name)){
                methodName = daoName + "." +value;
                sqlMap.put(methodName,rootElement.getStringValue());
            } else if ("resultType".equals(name)){
                typeMap.put(methodName,value);
            }
        }

        List<Element> elementList = rootElement.elements();
        for (int i=0;elementList != null && elementList.size() > 0 && elementList.size() > i;i++){
            Element element = elementList.get(i);
            setSqlMapAndTypeMethod(element,methodName);
        }
    }

    public static void main(String[] args) {
        Configuration configuration = new Configuration("mapper");
        configuration.initConfiguration();
    }
}
该类的作用是执行代理监听类的实现类,例如<input type="button" onclick="func()" />
onclick是代理监听类,func相当于ProxyFactory,而实现类就是StatementHandler;
public class ProxyFactory implements InvocationHandler {

    private Configuration configuration;

    private Class classFile;

    public ProxyFactory(Configuration configuration,Class classFile){
        this.configuration = configuration;
        this.classFile = classFile;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String classFullName = classFile.getName();
        int size = classFullName.lastIndexOf(".");
        String methodName = classFullName.substring(size + 1) +"."+ method.getName();
        StatementHandler statementHandler = new StatementHandler(configuration,methodName);
        return statementHandler.execute();
    }
}
该类相当于代理监听类的实现类,它主要是获取代理类中需要执行的方法,根据方法名在configuration中获取对应方法需要执行的sql语句
public class StatementHandler {

    private Configuration configuration;

    private Statement statement;

    private String method;

    public StatementHandler(Configuration configuration,String method){
        this.method = method;
        this.configuration = configuration;
    }

    public Object execute() throws Exception{
        Map sqlMap = configuration.getSqlMap();
        Connection connection = configuration.getConnection();
        statement = connection.createStatement();
        statement.execute(sqlMap.get(method).toString());
        ResultSet resultSet = statement.getResultSet();
        List list = new ArrayList();
        while (resultSet.next()){
            Class classFile = Class.forName((String) configuration.getTypeMap().get(method));
            Field fields[] = classFile.getDeclaredFields();
            Object classObj = classFile.newInstance();
            for (int i=0;i<fields.length;i++){
                Field field = fields[i];
                String name = field.getName();
                Class typeClass = field.getType();
                field.setAccessible(true);
                String resultValue = resultSet.getString(name);
                if (Integer.class == typeClass){
                    field.set(classObj,Integer.valueOf(resultValue));
                } else if (String.class == typeClass){
                    field.set(classObj,String.valueOf(resultValue));
                }
            }
            list.add(classObj);
        }
        close();
        return list;
    }

    public void close(){
        try {
            if (statement != null){
                statement.close();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
该类的最要作用是获取代理监听类,getMapper返回的是代理类
public class SqlSessionFactory {

    private Configuration configuration;

    public SqlSessionFactory(Configuration configuration){
        this.configuration = configuration;
    }

    public Object getMapper(Class classFile){
        ProxyFactory proxyFactory = new ProxyFactory(configuration,classFile);
        Class classArray[] = {classFile};
        return Proxy.newProxyInstance(classFile.getClassLoader(),classArray,proxyFactory);
    }
}
测试类
public class TestMain {
    private SqlSessionFactory sqlSessionFactory;

    private Configuration configuration;

    @Before
    public void before(){
        configuration = new Configuration("mapper");
        sqlSessionFactory = new SqlSessionFactory(configuration);
    }

    @After
    public void after(){
        configuration.close();
    }

    @Test
    public void test(){
        DeptDao deptDao = (DeptDao) sqlSessionFactory.getMapper(DeptDao.class);
        List list = deptDao.findAll();
        System.out.println(list.toString());
    }
}