Mybatis自定义框架

完成手写MyMybatis自定义框架。重新温习了以下技术,加深自己对Mybatis框架的深层次理解。

使用到的技术有

 

 

整个自定义框架分成三部分功能

  1. 解析XML文件封装成实体类对象

    1. Mapper用于封装UserMapper.xml中元素

    2. Configuration用于解析并封装mybatis-config.xml中元素

  2. 通过JDK动态代理,生成UserMapper接口的代理对象

  3. SqlSession:运行XML中的SQL语句,JDBC访问数据库,通过反射封装查询的结果集

 

项目结构如下:

 

sql准备

CREATE TABLE USER (
  id INT(11) NOT NULL AUTO_INCREMENT,
  username VARCHAR(20) NOT NULL,
  birthday DATE DEFAULT NULL,
  sex CHAR(1) DEFAULT '男',
  address VARCHAR(50) DEFAULT NULL,
  PRIMARY KEY (id)
) ENGINE=INNODB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8
 

mybatis-config.xml代码

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <!--mybatis环境的配置-->
    <environments default="development">
        <!--通常我们只需要配置一个就可以了, id是环境的名字 -->
        <environment id="development">
            <!--事务管理器:由JDBC来管理-->
            <transactionManager type="JDBC"/>
            <!--数据源的配置:mybatis自带的连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/day06"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!--加载映射文件,放到src下即可-->
        <mapper resource="userMapper.xml"/>

    </mappers>
</configuration>

 

userMapper.xml代码实现

<?xml version="1.0" encoding="UTF-8" ?>
<!--<mapper namespace="org.mybatis.example.BlogMapper">-->
<mapper namespace="dao.UserMapper">
    <!--
        查询语句
        resultType:返回的实体类的类型,类全名
    -->
    <select id="findAllUsers" resultType="pojo.User">
        select * from user
    </select>
</mapper>

UserMapper.java代码实现:

package dao;

import pojo.User;

import java.util.List;

public interface UserMapper {
    /*
       查询所有用户
    */
    List<User> findAllUsers();
}

User实体类代码实现:

package pojo;

import java.util.Date;

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    public User(){

    }
    public User(Integer id, String username, Date birthday, String sex, String address) {
        this.id = id;
        this.username = username;
        this.birthday = birthday;
        this.sex = sex;
        this.address = address;
    }

   //get、set、toString方法
}
Configuration.java代码实现
package framework;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.beans.PropertyVetoException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Configuration {
    //1.连接数据库的四大参数
    private String driver;
    private String url;
    private String username;
    private String password;
    //2.Map集合封装多个映射文件,key是String类型,value是Mapper
    private HashMap<String,Mapper> mapper = new HashMap<String,Mapper>();
    //3.提供数据源,即数据库连接池引用private DataSource dataSource;
    private ComboPooledDataSource dataSource;

    public Configuration(){
        try {
            //1. 从src路径加载mybatis-config.xml配置文件,创建输入流
            //2. 使用dom4j得到文档对象
            Document document = new SAXReader().read(Configuration.class.getClassLoader()
                                                    .getResourceAsStream("mybatis-config.xml"));
            //解析核心xml配置文件
            loadMybatisConfig(document);

            //2.创建数据源
            createDataSource();

            //3.加载UserMapper.xml
            loadMapper(document);


        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private void loadMapper(Document document) throws DocumentException {
        //1. 读取mapper中的resource属性值
        List<Node> nodes = document.selectNodes("//mapper");
        //遍历
        for (Node node : nodes) {
            //   1. 使用XPath读取所有mapper元素
            //   2. 遍历每个mapper元素
            //   3. 读取mapper的resource属性值
            Element element  = (Element) node;
            String mapperResource = element.attributeValue("resource");
            //2. 通过resource读取它对应的XML文件,得到namespace,id,resultType,sql的值
            //   1. 使用类对象,读取输入流下面resource
            //   2. 创建文档对象
            //   3. 读取根元素mapper
            //   4. 读取namespace属性
            //   5. 读取根元素下的一个select标签
            //   6. 得到id,resultType,sql内容
            Document mapperDom = new SAXReader().read(Configuration.class
                                            .getClassLoader().getResourceAsStream(mapperResource));
            Element rootElement = mapperDom.getRootElement();
            String namespace = rootElement.attributeValue("namespace");
            List<Element> selectList = rootElement.elements("select");
            for (Element selectElement : selectList) {
                String id = selectElement.attributeValue("id");
                String resultType = selectElement.attributeValue("resultType");
                String sql = selectElement.getTextTrim();

            //3. 封装成Mapper对象
                //   1. 创建一个自定义的Mapper对象,封装上面三个属性
                //   2. 再封装namespace属性
                //   3. 将封装好的mapper对象添加到this的mappers属性中,其中键是namespace+"."+id,值是自定义的mapper对象。

                Mapper mapper = new Mapper();
                mapper.setNamespace(namespace);
                mapper.setId(id);
                mapper.setResultType(resultType);
                mapper.setSql(sql);

                this.mapper.put(namespace+","+id,mapper);
            }
        }

    }

    private void createDataSource() throws PropertyVetoException {
        //使用c3p0连接池
        ComboPooledDataSource ds = new ComboPooledDataSource();
        //在代码中设置连接池的属性
        ds.setDriverClass(this.driver);
        ds.setJdbcUrl(this.url);
        ds.setPassword(this.password);
        ds.setUser(this.username);
        //创建好的数据源赋值给成员变量
        this.dataSource = ds;
    }

    /**解析xml文件*/
    private void loadMybatisConfig(Document document) {
        //得到properties元素
        List<Node> nodes = document.selectNodes("//property");
        //遍历
        for (Node node : nodes) {
            //强转
            Element element = (Element) node;
            //获取属性值对应的value
            String name = element.attributeValue("name");
            String value = element.attributeValue("value");
            //判断 将那个属性封装给那个对象中属性
            switch (name){
                case "driver":
                    this.driver = value;
                    break;
                case "url":
                    this.url = value;
                    break;
                case "username":
                    this.username = value;
                    break;
                case "password":
                    this.password = value;
                    break;
            }
        }
    }


    
//get、set、toString方法省略


}

 

Mapper.java代码实现:
package framework;

/**
 封装UserMapper.xml属性
 */
public class Mapper {
    //1. 创建包
    //2. 创建实体类:Mapper包含4个属性:namespace,id,resultType,sql
    //3. 重写toString()方法,方便后期测试看到封装的结果
    //4. 生成get和set方法
    //5. 一个Mapper对象代表一条要操作的查询语句对象
    private String namespace;
    private String id;
    private String resultType;
    private String sql;


//get、set、toString方法省略
}

 

SqlSession.java代码实现
package framework;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.lang.reflect.*;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/*
    sql会话类
 */
@SuppressWarnings("all")
public class SqlSession {
    //定义方法获取mapper
    /**
     创建UserMapper接口的代理对象
     @param clazz 接口类对象
     @return
     */
    public <T> T getMapper(Class<T> clazz) {//Class<T> clazz = UserMapper.class
        /*
        返回的是接口的代理对象
        参数1:类加载器
        参数2:所有实现的接口
        参数3:回调函数,用来处理每个方法
         */
        // 参数1:类加载器
        ClassLoader classLoader = SqlSession.class.getClassLoader();
        // 参数2:所有实现的接口
        Class[] interfaces = {clazz};
        //proxyMapper表示传递的接口的代理类对象
        T proxyMapper= (T) Proxy.newProxyInstance(classLoader,interfaces
                , new InvocationHandler() {
                    //参数1:生成的代理对象
                    //参数2:要调用的方法 例如UserMapper接口中的方法:findAllUsers
                    //参数3:方法的参数
                    //返回值:方法的返回值  List<User> findAllUsers();
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        //创建核心配置类对象
                        Configuration config = new Configuration();
                        //使用对象获取数据源
                        ComboPooledDataSource ds = config.getDataSource();
                        //从数据库连接池获取连接
                        Connection conn = ds.getConnection();

                        //获取mapper集合对象
                        HashMap<String, Mapper> map = config.getMapper();
                        //获取map集合的key
                        //获取namespace即全类名
                        String namespace = clazz.getName();
//                        System.out.println("namespace = " + namespace);
                        //获取id即接口的方法名
                        String id = method.getName();
//                        System.out.println("id = " + id);
                        //根据key获取map集合中的value
                        String key = namespace + "," + id;
                        Mapper mapper = map.get(key);

                        //获取mapper类的属性sql
                        String sql = mapper.getSql();
                        //获取结果集resultType
                        String resultType = mapper.getResultType();


                        //得到pojo类的对象 即User对象
                        Class objClass = Class.forName(resultType);

                        List list = queryForList(conn,sql,objClass);
                        return list;
                    }
                });

        return proxyMapper;
    }


    private List queryForList(Connection conn, String sql, Class clazz) throws Exception {
        List users = new ArrayList();
        //1.通过连接对象得到预编译的语句对象
        PreparedStatement ps = conn.prepareStatement(sql);
        //2.执行SQL语句,得到结果集
        ResultSet rs = ps.executeQuery();
        //3.遍历结果集,将每一行记录封装成一个User对象
        while (rs.next()) {
            Object user = clazz.getConstructor().newInstance();
            //得到User类中所有的成员变量
            /*
                private Integer id;
                private String username;
                private Date birthday;
                private String sex;
                private String address;
             */
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {//field 可以是:id username birthday sex address
                //得到成员变量的名字
                String name = field.getName();
                //暴力反射
                field.setAccessible(true);
                //遍历成员变量给每个成员变量赋值
                //从结果集中取出所有的数据
                //rs.getObject(name) 表示根据数据表的列名取出数据表中的列值 因为User类中的成员变量名必须和数据表列名一致
                //例如: name 的值是birthday 那么这里 rs.getObject(name)---》rs.getObject("birthday")获取数据表的生日1980-10-24
                Object table_value = rs.getObject(name);
                //void set(Object obj, Object value)给成员变量赋值,参数1:对象名 参数2:要赋的值
                field.set(user, table_value);
            }
            //4.将user对象添加到集合中
            users.add(user);
        }
        rs.close();
        ps.close();
        conn.close();
        return users;
    }
}

 

Test.java测试类的代码实现

package test;

import dao.UserMapper;
import framework.Configuration;
import framework.SqlSession;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.junit.Test;
import pojo.User;

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

public class test {
    @Test
    public void findAllUsers() {
        //创建会话对象
        SqlSession session = new SqlSession();
        //得到DAO接口对象:参数是接口,返回的是实现类。得到是它的代理对象
        UserMapper mapper = session.getMapper(UserMapper.class);
        //调用接口中的方法
        List<User> userList = mapper.findAllUsers();
        //遍历集合
        for (User user : userList) {
            System.out.println(user);
        }

    }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值