首页 > 其他 > 详细

自己实现mybatis相关功能

时间:2020-04-21 21:03:08      阅读:76      评论:0      收藏:0      [点我收藏+]

???? ??? 最近在学习mybatis相关的内容,对mybatis功能的强大还有使用方便感受很强,也很想去了解他的实现原理,根据他的原理自己实现了一个简单版的,在这个过程中也使用到了工厂设计模式、动态代理等等相关的知识,也顺带复习和学习,有不对之处,还请各位大佬多多指导。

1. 使用的xml的方式

具体配置如下:
技术分享图片

2. 整体流程如下

public class MybatisTest {

    // 入门
    public static void main(String[] args) throws Exception {
        // 1、 读取配置文件
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 2、 创建sqlSessionFactory
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        // 3、 使用工程生产对象
        SqlSession session = factory.openSession();
        // 5、 使用代理对象执行方法
        UserDao userDao = session.getMapper(UserDao.class);
        List<User> users = userDao.findAll();
        // 6、 释放资源
        for (User user : users) {
            System.out.println(user.toString());
        }
        in.close();
    }
}

3. 读取配置信息

InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");

这个Resouces是自定义的一个类,用类加载器来读取

public class Resources {

    /**
     * 根据输入参数,获取一个字节流
     * @param filePath
     * @return
     */
    public static InputStream getResourceAsStream(String filePath) {

        return Resources.class.getClassLoader().getResourceAsStream(filePath);
    }
}

4. 创建连接,与数据库交互

  1. 首先是需要一个创建连接的工厂,用户创建连接
public interface SqlSessionFactory {
    SqlSession openSession();
}

这是一个接口类型的,我们需要一个实现类

public class DefaultSqlSessionFactory implements SqlSessionFactory {
    private Configuration cfg;
    public DefaultSqlSessionFactory(Configuration cfg) {
        this.cfg = cfg;
    }
    /**
     * 用于创建新的数据操作对象
     * @return
     */
    public SqlSession openSession() {
        return new DefaultSqlSession(cfg);
    }
}

我们通过SqlSessionFactoryBuilder来创建sqlSessiongFactory

public class SqlSessionFactoryBuilder {

    /**
     * 根据字节输入流来
     * @param in
     * @return
     */
    public static SqlSessionFactory build(InputStream in) {
        Configuration cfg = XMLConfigBuilder.loadConfiguration(in);

        return new DefaultSqlSessionFactory(cfg);
    }
}

创建工厂时使用到的Configuration,是我们定义的一个类,里面保存着数据连接相关的属性信息

public class Configuration {
    // 驱动
    private String driver;
    // url
    private String url;
    // username
    private String username;
    // password
    private String password;
    // 存储dao中相关操作的信息
    private Map<String, Mapper> mappers;

    public String getDriver() {
        return driver;
    }
    public void setDriver(String driver) {
        this.driver = driver;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    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;
    }
    public Map<String, Mapper> getMappers() {
        return mappers;
    }
    public void setMappers(Map<String, Mapper> mappers) {
        this.mappers.putAll(mappers);
    }
}

XMLConfigBuilder这是一个工具类,用于实现对xml的解析,使用dom4j+xpath方式,也可以通过其他方式,这边就不展示了。
2. 创建好sqlSessionFactory之后,我们就需要创建连接

public interface SqlSession {

    /**
     * 根据参数创建代理对象
     * @param daoInterfaceClass dao接口的字节码
     * @param <T>
     * @return
     */
    <T> T getMapper(Class<T> daoInterfaceClass);

    /**
     * 释放资源
     */
    void close();
}

SqlSession中主要包括两个方法,getMapper是根据参数创建代理对象,close用于释放资源,有一个默认实现类

public class DefaultSqlSession implements SqlSession {
    private Configuration cfg;
    private Connection connection;

    public DefaultSqlSession(Configuration cfg) {
        this.cfg = cfg;
        connection = DataSourceUtil.getConnection(cfg);
    }
    public <T> T getMapper(Class<T> daoInterfaceClass) {
        return (T) Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(), new Class[]{daoInterfaceClass}, new MapperProxy(cfg.getMappers(), connection));
    }
    public void close() {
        if (connection != null) {
            try {
                connection.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }
}

里面主要有两个属性,一个是加载的数据库配置信息,还有是Connection属性。
Connection在我们构造的时候通过DataSourceUtil工具类实现

public class DataSourceUtil {
    public static Connection getConnection(Configuration cfg) {
        try {
            Class.forName(cfg.getDriver());
            return DriverManager.getConnection(cfg.getUrl(), cfg.getUsername(), cfg.getPassword());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

在SqlSession中创建代理对象进行方法增强时,我们实现的MapperProxy

public class MapperProxy implements InvocationHandler {

    // Map的key是全限定类名+方法名
    private Map<String, Mapper> mappers;

    private Connection          conn;

    public MapperProxy(Map<String, Mapper> mappers, Connection conn) {
        this.mappers = mappers;
        this.conn = conn;
    }

    /**
     * 用此方法进行增强,我们的增强就是调用selectList方法
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 1、获取方法名
        String methodName = method.getName();
        // 2、获取方法所在类的名称
        String className = method.getDeclaringClass().getName();
        // 3、组合key
        String key = className + "." + methodName;
        // 4、获取mapper中的key的value
        Mapper mapper = mappers.get(key);
        // 5、是否有mapper
        if (mapper == null) {
            throw new IllegalArgumentException("传入的参数有误");
        }
        // 6、调用工具类,查询所有
        return new Executor().selectList(mapper, conn);
    }
}

Executor是一个工具类,里面封装类selectList方法

public class Executor {
    public <E> List<E> selectList(Mapper mapper, Connection conn) {
        PreparedStatement   pstm = null;
        ResultSet           rs = null;
        try {

            // 1、取出mapper中的数据
            String queryString = mapper.getQueryString();
            String resultType = mapper.getResultType();
            Class domainClass = Class.forName(resultType);
            // 2、获取PreparedStatement对象
            pstm = conn.prepareStatement(queryString);
            // 3、执行sql语句,获取结果集
            rs = pstm.executeQuery();
            // 4、封装结果集
            List<E> list = new ArrayList<E>();
            while (rs.next()) {
                E obj = (E) domainClass.newInstance();
                ResultSetMetaData rsmd = rs.getMetaData();
                int columnCount = rsmd.getColumnCount();
                for (int i = 1; i <= columnCount; i++) {
                    // 获取每列的列名,序号从1开始
                    String columnName = rsmd.getColumnName(i);
                    // 根据列名,获取值
                    Object columnValue = rs.getObject(columnName);
                    // 给obj赋值
                    PropertyDescriptor pd = new PropertyDescriptor(columnName, domainClass);
                    // 获取写入方法
                    Method writeMethod = pd.getWriteMethod();
                    // 赋值对象
                    writeMethod.invoke(obj, columnValue);
                }
                list.add(obj);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            release(pstm, rs);
        }
        return null;
    }
    private void release(PreparedStatement pstm, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (pstm != null) {
            try {
                pstm.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

5. 接下来就是用代理对象执行方法,封装返回类型,并释放资源

// 5、 使用代理对象执行方法
        UserDao userDao = session.getMapper(UserDao.class);
        List<User> users = userDao.findAll();
        // 6、 释放资源
        for (User user : users) {
            System.out.println(user.toString());
        }

        in.close();

自己实现mybatis相关功能

原文:https://www.cnblogs.com/liufei-yes/p/12747566.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!