自己實現mybatis的基本功能

         最近在學習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);
    }
}
```java
### 4. 創建連接,與數據庫交互
   1. 首先是需要一個創建連接的工廠,用戶創建連接
```java
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();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章