架構分析
-
-
得到數據源對象
-
加載其它的實體類映射文件:UserMapper.xml,使用DOM4J
-
-
Mapper類只是一個實體類:POJO,用來封裝數據
-
SqlSession類:
-
生成了UserMapper接口的代理對象,JDK代理。
-
訪問數據庫:JDBC
-
封裝查詢的結果集,使用反射
-
添加所需的所有依賴,做好准備工作。
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>
步驟
-
創建包com.it.mybatis
-
創建實體類:Mapper包含4個屬性:namespace,id,resultType,sql
-
重寫toString()方法,方便后期測試看到封裝的結果
-
生成get和set方法
-
一個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; } }
-
設計Configuration的基本屬性
-
產生get和set方法
-
產生toString()方法
sqlMapConfig.xml文件
-
創建loadSqlMapConfig()方法,它的作用:
-
解析sqlMapConfig.xml配置文件,給Configuration中的屬性賦值
-
解析UserMapper.xml配置文件,給Mapper中的屬性賦值
-
-
-
作用:使用dom4j解析sqlMapConfig.xml文件,給數據庫有關的屬性賦值
-
-
從類路徑加載/sqlMapConfig.xml配置文件,創建輸入流
-
使用dom4j得到文檔對象
-
使用XPath讀取所有property元素
-
遍歷每個property元素,讀取它的name和value屬性值
-
判斷name的字符串,如果與類中的屬性名相同,則賦值到相應屬性中
-
4.Configuration解析實體類映射文件
解析UserMapper.xml並且封裝到Mapper類中
-
-
創建新的方法loadMapper(Document document),將當前的文檔對象傳遞給方法
-
讀取<mapper>中的resource屬性值
-
通過resource讀取它對應的XML文件
-
得到namespace,id,resultType,sql的值,封裝成Mapper對象
-
在loadSqlMapConfig()中調用此方法
-
loadMapper(Document document)方法開發步驟
作用:進一步解析其它的XML文件,給mappers屬性賦值
-
讀取mapper中的resource屬性值
-
使用XPath讀取所有mapper元素
-
遍歷每個mapper元素
-
讀取mapper的resource屬性值
-
-
通過resource讀取它對應的XML文件,得到namespace,id,resultType,sql的值
-
使用類對象,讀取輸入流下面resource,注:要加上/
-
創建文檔對象
-
讀取根元素mapper
-
讀取namespace屬性
-
讀取根元素下的一個select標簽
-
得到id,resultType,sql內容
-
-
封裝成Mapper對象
-
創建一個自定義的Mapper對象,封裝上面三個屬性
-
再封裝namespace屬性
-
將封裝好的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; } }
步驟
得到SQL語句和返回類型
-
得到Configuration中Map集合
-
實例化Configuration對象
-
通過Configuration得到Mapper對象的集合
-
-
得到Map中的鍵:類全名.方法名
-
通過方法對象->得到聲明的接口->得到名稱:即類全名 com.it.dao.UserMapper
-
獲取當前執行的方法名稱:findAllUsers
-
通過類全名+方法名得到鍵
-
-
得到Mapper中相應的屬性
-
通過類全名+"."+方法名,從mappers中得到映射的mapper對象
-
從mapper中獲取查詢的sql語句
-
從mapper中獲取返回值類型resultType
-
通過反射將上面的resultType字符串轉成類對象,供后面的方法使用
-
得到Connection對象訪問數據庫
-
通過Configuration得到數據源,通過數據源得到連接對象
-
調用List queryForList(Connection connection, String sql, Class clazz)方法
-
參數:連接對象,SQL語句,結果集的類型。 直接創建一個List集合,添加3個User對象到集合中,暫時不訪問數據庫。
- 返回封裝好的集合
-

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; } }