自定義mybatis


架構分析

  1. Configuration類:

    1. 得到數據源對象

    2. 加載其它的實體類映射文件:UserMapper.xml,使用DOM4J

  2. Mapper類只是一個實體類:POJO,用來封裝數據

  3. SqlSession類:

    1. 生成了UserMapper接口的代理對象,JDK代理。

    2. 訪問數據庫:JDBC

    3. 封裝查詢的結果集,使用反射

使用到的技術

 

添加所需的所有依賴,做好准備工作。

pom.xml文件

 <properties>
        <!--dom4j版本-->
        <dom4j.vesrion>1.6.1</dom4j.vesrion>
        <!--dom4j依賴包版本-->
        <jaxen.version>1.1.6</jaxen.version>
        <!--mysql驅動版本-->
        <mysql.version>5.1.30</mysql.version>
        <!--c3p0版本-->
        <c3p0.version>0.9.2.1</c3p0.version>
        <!-- junit版本 -->
        <junit.version>4.12</junit.version>
    </properties>

    <dependencies>
        <!--dom4j依賴-->
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>${dom4j.vesrion}</version>
        </dependency>
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>${jaxen.version}</version>
        </dependency>
        <!-- mysql數據庫依賴 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <!--c3p0依賴-->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>${c3p0.version}</version>
        </dependency>
        <!-- junit依賴 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
    </dependencies>
pox.xml

自定義mybatis:Mapper封裝數據類

分析映射配置:UserMapper.xml 

 

步驟

  1. 創建包com.it.mybatis

  2. 創建實體類:Mapper包含4個屬性:namespace,id,resultType,sql

  3. 重寫toString()方法,方便后期測試看到封裝的結果

  4. 生成get和set方法

  5. 一個Mapper對象代表一條要操作的查詢語句對象

代碼

package com.it.mybatis;

/**
 用來封裝映射文件的實體類
 */
public class Mapper {

    private String namespace;  //接口類全名
    private String id;   //接口中方法名
    private String resultType;  //封裝的數據類型
    private String sql;   //要執行的SQL語句

    @Override
    public String toString() {
        return "Mapper{" +
                "namespace='" + namespace + '\'' +
                ", id='" + id + '\'' +
                ", resultType='" + resultType + '\'' +
                ", sql='" + sql + '\'' +
                '}';
    }

    public String getNamespace() {
        return namespace;
    }

    public void setNamespace(String namespace) {
        this.namespace = namespace;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getResultType() {
        return resultType;
    }

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

    public String getSql() {
        return sql;
    }

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

自定義mybatis:設計Configuration的基本屬性

  1. 設計Configuration的基本屬性

  2. 產生get和set方法

  3. 產生toString()方法

分析

sqlMapConfig.xml文件

 

  1. 創建loadSqlMapConfig()方法,它的作用:

    1. 解析sqlMapConfig.xml配置文件,給Configuration中的屬性賦值

    2. 解析UserMapper.xml配置文件,給Mapper中的屬性賦值

  2. 在構造方法中調用方法: loadSqlMapConfig()

  3. 作用:使用dom4j解析sqlMapConfig.xml文件,給數據庫有關的屬性賦值

    1. 從類路徑加載/sqlMapConfig.xml配置文件,創建輸入流

    2. 使用dom4j得到文檔對象

    3. 使用XPath讀取所有property元素

    4. 遍歷每個property元素,讀取它的name和value屬性值

    5. 判斷name的字符串,如果與類中的屬性名相同,則賦值到相應屬性中

  4.Configuration解析實體類映射文件

         解析UserMapper.xml並且封裝到Mapper類中

    1. 創建新的方法loadMapper(Document document),將當前的文檔對象傳遞給方法

    2. 讀取<mapper>中的resource屬性值

    3. 通過resource讀取它對應的XML文件

    4. 得到namespace,id,resultType,sql的值,封裝成Mapper對象

    5. 在loadSqlMapConfig()中調用此方法

 

 

loadMapper(Document document)方法開發步驟

作用:進一步解析其它的XML文件,給mappers屬性賦值

  1. 讀取mapper中的resource屬性值

    1. 使用XPath讀取所有mapper元素

    2. 遍歷每個mapper元素

    3. 讀取mapper的resource屬性值

  2. 通過resource讀取它對應的XML文件,得到namespace,id,resultType,sql的值

    1. 使用類對象,讀取輸入流下面resource,注:要加上/

    2. 創建文檔對象

    3. 讀取根元素mapper

    4. 讀取namespace屬性

    5. 讀取根元素下的一個select標簽

    6. 得到id,resultType,sql內容

  3. 封裝成Mapper對象

    1. 創建一個自定義的Mapper對象,封裝上面三個屬性

    2. 再封裝namespace屬性

    3. 將封裝好的mapper對象添加到this的mappers屬性中,其中鍵是namespace+"."+id,值是自定義的mapper對象。

代碼

package com.it.mybatis;

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

import javax.sql.DataSource;
import java.beans.PropertyVetoException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 1. 封裝sqlMapConfig.xml配置信息
 2. 得到數據源
 3. 加載UserMapper.xml配置信息
 */
public class Configuration {

    //數據源的四個屬性
    private String username;
    private String password;
    private String url;
    private String driver;

    //封裝其它的映射文件中屬性
    private Map<String, Mapper> mappers = new HashMap<>();

    private DataSource dataSource;  //數據源

    //在構造方法中調用
    public Configuration() {
        try {
            loadSqlMapConfig();  //加載配置文件
            createDataSource();  //創建數據源
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /*
        解析sqlMapConfig.xml文件,封裝上面的數據源的四個屬性
         */
    private void loadSqlMapConfig() throws Exception {
        //得到輸入流
        InputStream inputStream = Configuration.class.getResourceAsStream("/sqlMapConfig.xml");
        //得到文檔對象
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(inputStream);
        //讀取property
        List<Element> list = document.selectNodes("//property");
        for (Element property : list) {
            //讀取name屬性
            String name = property.attributeValue("name");
            //讀取value屬性
            String value = property.attributeValue("value");
            //判斷是哪個屬性
            switch (name) {
                case "username":
                    this.username = value;
                    break;
                case "password":
                    this.password = value;
                    break;
                case "driver":
                    this.driver = value;
                    break;
                case "url":
                    this.url = value;
                    break;
            }
        }
        //讀取UserMapper.xml文件
        loadMapper(document);
    }

    /**
    解析其它的實體類映射文件
    @param document 上面已經得到的文檔對象
     */
    private void loadMapper(Document document) throws Exception {
        //讀取mapper中resource屬性
        List<Element> list = document.selectNodes("//mapper");
        for (Element mapperElement : list) {
            //讀取mapper中resource屬性
            String resource = mapperElement.attributeValue("resource");
            //再次讀取新的XML文件
            InputStream in = Configuration.class.getResourceAsStream("/" + resource);
            //創建文檔對象
            Document doc = new SAXReader().read(in);
            //得到根元素
            Element rootElement = doc.getRootElement();
            String namespace = rootElement.attributeValue("namespace");
            //得到mapper下select元素
            Element select = rootElement.element("select");
            //得到id屬性
            String id = select.attributeValue("id");
            String resultType = select.attributeValue("resultType");
            String sql = select.getTextTrim();
            //創建Mapper對象
            Mapper mapper = new Mapper();
            mapper.setId(id);
            mapper.setNamespace(namespace);
            mapper.setSql(sql);
            mapper.setResultType(resultType);
            //鍵=namespace + "." + "id";
            String key = namespace + "." + id;
            //將創建好的mapper對象加到Map集合中
            mappers.put(key,mapper);
        }
    }

    /**
     創建數據源
     */
    private void createDataSource() throws PropertyVetoException {
        //使用c3p0的數據源
        ComboPooledDataSource ds = new ComboPooledDataSource();
        //設置數據庫訪問屬性
        ds.setUser(username);
        ds.setPassword(password);
        ds.setJdbcUrl(url);
        ds.setDriverClass(driver);
        this.dataSource = ds;
    }

    @Override
    public String toString() {
        return "Configuration{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", url='" + url + '\'' +
                ", driver='" + driver + '\'' +
                ", mappers=" + mappers +
                ", dataSource=" + dataSource +
                '}';
    }

    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 String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public Map<String, Mapper> getMappers() {
        return mappers;
    }

    public void setMappers(Map<String, Mapper> mappers) {
        this.mappers = mappers;
    }

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}
Configuration

 

核心組件SqlSession:編寫getMapper方法

 

步驟

得到SQL語句和返回類型

  1. 得到Configuration中Map集合

    1. 實例化Configuration對象

    2. 通過Configuration得到Mapper對象的集合

  2. 得到Map中的鍵:類全名.方法名

    1. 通過方法對象->得到聲明的接口->得到名稱:即類全名 com.it.dao.UserMapper

    2. 獲取當前執行的方法名稱:findAllUsers

    3. 通過類全名+方法名得到鍵

  3. 得到Mapper中相應的屬性

    1. 通過類全名+"."+方法名,從mappers中得到映射的mapper對象

    2. 從mapper中獲取查詢的sql語句

    3. 從mapper中獲取返回值類型resultType

    4. 通過反射將上面的resultType字符串轉成類對象,供后面的方法使用

得到Connection對象訪問數據庫

  1. 通過Configuration得到數據源,通過數據源得到連接對象

  2. 調用List queryForList(Connection connection, String sql, Class clazz)方法

    1. 參數:連接對象,SQL語句,結果集的類型。 直接創建一個List集合,添加3個User對象到集合中,暫時不訪問數據庫。

    2. 返回封裝好的集合

 

package com.it.mybatis;

import com.itheima.entity.User;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 核心類
 1. 生成了UserMapper接口的代理對象,JDK代理。
 2. 訪問數據庫:JDBC
 3. 封裝查詢的結果集,使用反射。
 */
public class SqlSession {

    public <T> T getMapper(Class<T> clazz) {
        /*
        參數1:類加載器
        參數2:接口數組
        參數3:每個方法調用一次
         */
        return (T) Proxy.newProxyInstance(
                SqlSession.class.getClassLoader(),
                new Class[]{clazz},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                       //1. 通過鍵得到Mapper對象
                        //創建Configuration對象
                        Configuration configuration = new Configuration();
                        //得到集合
                        Map<String, Mapper> mappers = configuration.getMappers();
                        String id = method.getName();  //方法名
                        //getDeclaringClass得到method所在類全名
                        String namespace = method.getDeclaringClass().getName();
                        //如何得到鍵
                        String key = namespace + "." + id;
                        //通過鍵得到值
                        Mapper mapper = mappers.get(key);

                        //2. 從Mapper對象中得到SQL語句執行,並且封裝成對象返回
                        String sql = mapper.getSql();
                        //resultType = com.itheima.entity.User
                        String resultType = mapper.getResultType();
                        //將字符串轉成Class
                        Class type = Class.forName(resultType);

                        //3.查詢數據庫,必須要連接對象
                        //連接對象從數據源中得到
                        DataSource dataSource = configuration.getDataSource();
                        Connection connection = dataSource.getConnection();

                        //使用JDBC訪問數據庫得到封裝好的結果
                        List list = queryForList(connection, sql, type);
                        return list;
                    }
                });
    }

    /**
     通過JDBC來訪問數據庫
     @param connection 連接對象
     @param sql 語句
     @param type 返回類型
     @return
     */
    private List queryForList(Connection connection, String sql, Class type) throws Exception {
        List users = new ArrayList<>();

        //1. 通過Connection創建語句對象:PreparedStatement
        PreparedStatement ps = connection.prepareStatement(sql);

        //2. 通過語句對象執行SQL語句
        ResultSet rs = ps.executeQuery();

        //3. 執行完畢以后得到結果集ResultSet
        while(rs.next()) {
            //4. 將ResultSet進行遍歷,封裝成實體類對象,添加到集合中
            //創建對象
            Object user = type.getConstructor().newInstance();
            //得到類中所有的屬性
            Field[] fields = type.getDeclaredFields();
            for (Field field : fields) {
                //得到屬性名
                String name = field.getName();
                //得到相應的值
                Object value = rs.getObject(name);
                //暴力反射
                field.setAccessible(true);
                //給每個屬性賦值
                field.set(user, value);
            }
            users.add(user);
        }

        //5.釋放資源
        rs.close();
        ps.close();
        connection.close();

        return users;
    }

}
SqlSession

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM