日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Mybatis执行流程分析_自定义简易Mybatis框架

發(fā)布時(shí)間:2024/7/5 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Mybatis执行流程分析_自定义简易Mybatis框架 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

自定義簡(jiǎn)易Mybatis框架

Mybatis執(zhí)行流程分析

  • Mybatis代碼編寫(xiě)流程:
  • Mybatis配置文件加載過(guò)程:

需求分析及技術(shù)概述

  • 根據(jù)上述的功能結(jié)構(gòu)圖, 得出如下需求:
1. 需要具有配置文件加載模塊. 2. 支持使用構(gòu)建者進(jìn)行SessionFactory構(gòu)建. 3. 支持使用工廠模式構(gòu)建Session對(duì)象. 4. 可以使用代理模式, 構(gòu)建DAO接口實(shí)現(xiàn)類(lèi)對(duì)象. 5. 使用反射機(jī)制, 封裝ResultSet結(jié)果集. 6. 支持使用.xml文件編寫(xiě)SQL語(yǔ)句. 7. 支持使用注解方式進(jìn)行SQL查詢(xún).
  • 項(xiàng)目技術(shù)要點(diǎn)
1. 解析UserDao.xml; SqlMapConfig.xml需要使用dom4j, xpath技術(shù)2. 使用Configuration類(lèi)(類(lèi)似于Map結(jié)構(gòu))存儲(chǔ)SqlMapConfig中配置信息3. SqlMapConfig中包含連接池, 使用DataSource類(lèi)根據(jù)配置信息, 創(chuàng)建連接池4. 使用構(gòu)建者, 工廠, 代理模式開(kāi)發(fā)5. 使用Java反射機(jī)制, 自定義注解.

項(xiàng)目搭建(本項(xiàng)目只構(gòu)建Select * from tableName; 主要用于理解Mybatis執(zhí)行過(guò)程)

本項(xiàng)目使用maven進(jìn)行構(gòu)建, pom.xml文件需引入如下依賴(lài)包:

<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency> <!-- 使用dom4j與xpath解析xml--><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version></dependency> <!-- xpath技術(shù)--><dependency><groupId>jaxen</groupId><artifactId>jaxen</artifactId><version>1.1.6</version></dependency>

項(xiàng)目目錄結(jié)構(gòu):

本項(xiàng)目分為如下幾塊內(nèi)容:

配置文件加載(包括SqlMapConfig.xml; Mapper.xml, 注解). 配置數(shù)據(jù)源. Session工廠 SqlSession

第一部分內(nèi)容如下:

  • 配置文件加載類(lèi)實(shí)體編寫(xiě)(用于封裝SqlMapConfig.xml文件內(nèi)容):
/*** 自定義Mybatis的配置文件* @author regotto*/ public class Configuration {private String driver;private String username;private String url;private String password;private Map<String, Mapper> mappers = new HashMap<>(16);//....屬性get, set方法 }
  • 注解定義
/*** select語(yǔ)句的注解, 運(yùn)行期有效, 用于修飾方法* @author regotto*/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Select {/*** 配置SQL語(yǔ)句* @return*/String value(); }
  • 工具類(lèi)準(zhǔn)備(Xml文件解析, 數(shù)據(jù)源配置, Sql執(zhí)行結(jié)果集封裝):
    • 獲取XML配置文件的輸入流:
public class Resources {public static InputStream getResourceAsStream(String filePath) {return Resources.class.getClassLoader().getResourceAsStream(filePath);} }
    • Xml文件解析(將配置文件內(nèi)容解析后, 并將其封裝到Configuration實(shí)體中)
public class XMLConfigBuilder {/*** 解析主配置文件,把里面的內(nèi)容填充到DefaultSqlSession所需要的地方* 使用的技術(shù): dom4j+xpath*/public static Configuration loadConfiguration(InputStream config){try{//定義封裝連接信息的配置對(duì)象(mybatis的配置對(duì)象)Configuration cfg = new Configuration();//1.獲取SAXReader對(duì)象SAXReader reader = new SAXReader();//2.根據(jù)字節(jié)輸入流獲取Document對(duì)象Document document = reader.read(config);//3.獲取根節(jié)點(diǎn)Element root = document.getRootElement();//4.使用xpath中選擇指定節(jié)點(diǎn)的方式,獲取所有property節(jié)點(diǎn)List<Element> propertyElements = root.selectNodes("//property");//5.遍歷節(jié)點(diǎn)for(Element propertyElement : propertyElements){//判斷節(jié)點(diǎn)是連接數(shù)據(jù)庫(kù)的哪部分信息//取出name屬性的值String name = propertyElement.attributeValue("name");if("driver".equals(name)){String driver = propertyElement.attributeValue("value");cfg.setDriver(driver);}if("url".equals(name)){String url = propertyElement.attributeValue("value");cfg.setUrl(url);}if("username".equals(name)){String username = propertyElement.attributeValue("value");cfg.setUsername(username);}if("password".equals(name)){//表示密碼//獲取property標(biāo)簽value屬性的值String password = propertyElement.attributeValue("value");cfg.setPassword(password);}}//取出mappers中的所有mapper標(biāo)簽,判斷他們使用了resource還是class屬性List<Element> mapperElements = root.selectNodes("//mappers/mapper");//遍歷集合for(Element mapperElement : mapperElements){//判斷mapperElement使用的是哪個(gè)屬性Attribute attribute = mapperElement.attribute("resource");//在mapper中只有兩種屬性: resource, classif(attribute != null){//表示有resource屬性,用的是XML, 取出屬性的值String mapperPath = attribute.getValue();//獲取屬性的值"com/itheima/dao/IUserDao.xml"//把映射配置文件的內(nèi)容獲取出來(lái),封裝成一個(gè)mapMap<String,Mapper> mappers = loadMapperConfiguration(mapperPath);//給configuration中的mappers賦值cfg.setMappers(mappers);}else{//表示沒(méi)有resource屬性,用的是注解 獲取class屬性的值String daoClassPath = mapperElement.attributeValue("class");//根據(jù)daoClassPath獲取封裝的必要信息Map<String, Mapper> mappers = loadMapperAnnotation(daoClassPath);//給configuration中的mappers賦值cfg.setMappers(mappers);}}//返回Configurationreturn cfg;}catch(Exception e){throw new RuntimeException(e);}finally{try {config.close();}catch(Exception e){e.printStackTrace();}}}/*** 根據(jù)傳入的參數(shù),解析XML,并且封裝到Map中* @param mapperPath 映射配置文件的位置* @return map中包含了獲取的唯一標(biāo)識(shí)(key是由dao的全限定類(lèi)名和方法名組成)* 以及執(zhí)行所需的必要信息(value是一個(gè)Mapper對(duì)象,里面存放的是執(zhí)行的SQL語(yǔ)句和要封裝的實(shí)體類(lèi)全限定類(lèi)名(ResultSet的類(lèi)型))*/private static Map<String,Mapper> loadMapperConfiguration(String mapperPath)throws IOException {InputStream in = null;try{//定義返回值對(duì)象Map<String,Mapper> mappers = new HashMap<String,Mapper>(16);//1.根據(jù)路徑獲取字節(jié)輸入流in = Resources.getResourceAsStream(mapperPath);//2.根據(jù)字節(jié)輸入流獲取Document對(duì)象SAXReader reader = new SAXReader();Document document = reader.read(in);//3.獲取根節(jié)點(diǎn)Element root = document.getRootElement();//4.獲取根節(jié)點(diǎn)的namespace屬性取值String namespace = root.attributeValue("namespace");//是組成map中key的部分//5.獲取所有的select節(jié)點(diǎn)List<Element> selectElements = root.selectNodes("//select");//6.遍歷select節(jié)點(diǎn)集合for(Element selectElement : selectElements){//取出id屬性的值 組成map中key的部分String id = selectElement.attributeValue("id");//取出resultType屬性的值 組成map中value的部分String resultType = selectElement.attributeValue("resultType");//取出文本內(nèi)容 組成map中value的部分String queryString = selectElement.getText();//創(chuàng)建KeyString key = namespace+"."+id;//創(chuàng)建ValueMapper mapper = new Mapper();mapper.setQueryString(queryString);mapper.setResultType(resultType);//把key和value存入mappers中mappers.put(key,mapper);}return mappers;}catch(Exception e){throw new RuntimeException(e);}finally{in.close();}}/*** 根據(jù)傳入的參數(shù),得到dao中所有被select注解標(biāo)注的方法。* 根據(jù)方法名稱(chēng)和類(lèi)名,以及方法上注解value屬性的值,組成Mapper的必要信息* @param daoClassPath* @return*/private static Map<String,Mapper> loadMapperAnnotation(String daoClassPath)throws Exception{//定義返回值對(duì)象Map<String,Mapper> mappers = new HashMap<String, Mapper>();//1.得到dao接口的字節(jié)碼對(duì)象Class daoClass = Class.forName(daoClassPath);//2.得到dao接口中的方法數(shù)組Method[] methods = daoClass.getMethods();//3.遍歷Method數(shù)組for(Method method : methods){//取出每一個(gè)方法,判斷是否有select注解boolean isAnnotated = method.isAnnotationPresent(Select.class);if(isAnnotated){//創(chuàng)建Mapper對(duì)象Mapper mapper = new Mapper();//取出注解的value屬性值Select selectAnno = method.getAnnotation(Select.class);String queryString = selectAnno.value();mapper.setQueryString(queryString);//獲取當(dāng)前方法的返回值且返回值必須具有泛型(Generic泛型的意思)// List<User>Type type = method.getGenericReturnType();//判斷type是不是參數(shù)化的類(lèi)型if(type instanceof ParameterizedType){//強(qiáng)轉(zhuǎn)ParameterizedType ptype = (ParameterizedType)type;//得到參數(shù)化類(lèi)型中的實(shí)際類(lèi)型參數(shù), 因?yàn)橄馦ap<K,V>這種存在多個(gè)泛型, 所以Type是數(shù)組Type[] types = ptype.getActualTypeArguments();//取出第一個(gè), 我們的List<User>只有一個(gè), 所以取出第一個(gè)Class domainClass = (Class)types[0];//獲取domainClass的類(lèi)名String resultType = domainClass.getName();//給Mapper賦值mapper.setResultType(resultType);}//組裝key的信息//獲取方法的名稱(chēng)String methodName = method.getName();String className = method.getDeclaringClass().getName();String key = className+"."+methodName;//給map賦值mappers.put(key,mapper);}}return mappers;} }
    • 數(shù)據(jù)源配置
//獲得一個(gè)Connection對(duì)象 public class DataSource {public static Connection getConnection(Configuration configuration) {try {Class.forName(configuration.getDriver());return DriverManager.getConnection(configuration.getUrl(),configuration.getUsername(),configuration.getPassword());} catch (Exception e) {throw new RuntimeException(e);}} }
    • 結(jié)果集封裝(Sql執(zhí)行后的ResultSet封裝)
/*** @author regotto* 負(fù)責(zé)執(zhí)行SQL語(yǔ)句,并且封裝結(jié)果集*/ public class Executor {public <E> List<E> selectList(Mapper mapper, Connection conn) {PreparedStatement pstm = null;ResultSet rs = null;try {//1.取出mapper中的數(shù)據(jù)// select * from userString queryString = mapper.getQueryString();//com.regotto.domain.UserString resultType = mapper.getResultType();Class domainClass = Class.forName(resultType);//2.獲取PreparedStatement對(duì)象pstm = conn.prepareStatement(queryString);//3.執(zhí)行SQL語(yǔ)句,獲取結(jié)果集rs = pstm.executeQuery();//4.封裝結(jié)果集List<E> list = new ArrayList<E>();//定義返回值while(rs.next()) {//實(shí)例化要封裝的實(shí)體類(lèi)對(duì)象E obj = (E)domainClass.newInstance();//取出結(jié)果集的元信息:ResultSetMetaDataResultSetMetaData rsmd = rs.getMetaData();//取出總列數(shù)int columnCount = rsmd.getColumnCount();//遍歷總列數(shù)for (int i = 1; i <= columnCount; i++) {//獲取每列的名稱(chēng),列名的序號(hào)是從1開(kāi)始的String columnName = rsmd.getColumnName(i);//根據(jù)得到列名,獲取每列的值Object columnValue = rs.getObject(columnName);//給obj賦值:使用Java內(nèi)省機(jī)制(借助PropertyDescriptor實(shí)現(xiàn)屬性的封裝, 底層還是反射機(jī)制)//獲取對(duì)應(yīng)domainClass這個(gè)類(lèi)中的columnName的所有操作//要求:實(shí)體類(lèi)的屬性和數(shù)據(jù)庫(kù)表的列名保持一種PropertyDescriptor pd = new PropertyDescriptor(columnName,domainClass);//獲取它的寫(xiě)入方法(即setXXX(...))Method writeMethod = pd.getWriteMethod();//把獲取的列的值,給對(duì)象賦值(即obj.setXXX(columnValue))writeMethod.invoke(obj,columnValue);}//把賦好值的對(duì)象加入到集合中list.add(obj);}return list;} catch (Exception e) {throw new RuntimeException(e);} finally {release(pstm,rs);}}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();}}} }

第二部分內(nèi)容:

  • SqlSession工廠(此部分使用工廠模型, 構(gòu)建者模型):
    • SqlSession工廠接口:
/*** SqlSessionFactory, 制定Session* @author regotto*/ public interface SqlSessionFactory {/*** 返回一個(gè)SqlSession對(duì)象* @return sqlSession*/SqlSession openSession(); }
    • 工廠建造器:
/*** 根據(jù)配置文件創(chuàng)建SqlSessionFactory* @author regotto*/ public class SqlSessionFactoryBuilder {public SqlSessionFactory build(InputStream inputStream) {Configuration configuration = XMLConfigBuilder.loadConfiguration(inputStream);return new DefaultSqlSessionFactory(configuration);} }
    • 默認(rèn)Session工廠:
/*** 默認(rèn)的SqlSessionFactory實(shí)現(xiàn)* @author regotto*/ public class DefaultSqlSessionFactory implements SqlSessionFactory {private Configuration configuration;public DefaultSqlSessionFactory(Configuration configuration) {this.configuration = configuration;}/*** 創(chuàng)建一個(gè)默認(rèn)的Session* @return*/@Overridepublic SqlSession openSession() {return new DefaultSqlSession(configuration);} }

第三部分內(nèi)容:

  • SqlSession
    • Mapper實(shí)體:
/***封裝XXXDao.xml中的resultType和SQL語(yǔ)句* @author regotto*/ public class Mapper {private String resultType;private String queryString;//get, set方法...
    • SqlSession接口:
public interface SqlSession {/*** 通過(guò)tClass獲得代理對(duì)象* @param daoInterfaceClass* @param <T>* @return 代理對(duì)象*/<T> T getMapper(Class<T> daoInterfaceClass);/*** 關(guān)閉Session資源*/void close(); }
    • MapperProxy(生成Dao接口代理類(lèi)):
/*** 代理對(duì)象* @author regotto*/ public class MapperProxy implements InvocationHandler {/*** String內(nèi)存儲(chǔ)*/private Map<String, Mapper> mappers;private Connection connection;public MapperProxy(Map<String, Mapper> mappers, Connection connection) {this.mappers = mappers;this.connection = connection;}/*** 方法增強(qiáng), 調(diào)用selectList* 在此處執(zhí)行SQL, 對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作* @param proxy 代理對(duì)象* @param method 執(zhí)行方法* @param args 執(zhí)行方法的參數(shù)* @return Object 返回代理對(duì)象* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName = method.getName();//獲取方法所在的類(lèi)名String className = method.getDeclaringClass().getName();String key = className + "." + methodName;//查看XXXDao.xml中是否有代理對(duì)象對(duì)應(yīng)的類(lèi)和方法Mapper mapper = mappers.get(key);if (mapper == null) {throw new IllegalArgumentException("傳入的Class對(duì)象錯(cuò)誤");}return new Executor().selectList(mapper, connection);} }
    • 默認(rèn)SqlSession實(shí)現(xiàn):
/*** 默認(rèn)的Session* @author regotto*/ public class DefaultSqlSession implements SqlSession {/*** 封裝SqlMapConfig配置信息* 包含DataSource, Mappers(封裝XXXDao.xml的namespace, sql標(biāo)簽的id, returnType, sql語(yǔ)句)*/private Configuration configuration;/*** 數(shù)據(jù)庫(kù)連接對(duì)象*/private Connection connection;public DefaultSqlSession(Configuration configuration) {this.configuration = configuration;this.connection = DataSource.getConnection(configuration);}/*** 創(chuàng)建代理對(duì)象* @param daoInterfaceClass 需要?jiǎng)?chuàng)建代理對(duì)象的class* @param <T> 針對(duì)所有的class* @return 代理對(duì)象*/@Overridepublic <T> T getMapper(Class<T> daoInterfaceClass) {return (T) Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(),new Class[]{daoInterfaceClass},new MapperProxy(configuration.getMappers(), connection));}/*** 釋放資源*/@Overridepublic void close() {if (connection != null) {try {connection.close();} catch (SQLException e) {e.printStackTrace();}}} }

測(cè)試類(lèi):

/*** @author regotto*/ public class MybatisTest {public static void main(String[] args) throws Exception {InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");SqlSessionFactoryBuilder sessionFactoryBuilder = new SqlSessionFactoryBuilder();SqlSessionFactory sessionFactory = sessionFactoryBuilder.build(inputStream);SqlSession sqlSession = sessionFactory.openSession();//獲得代理對(duì)象UserDao userDao = sqlSession.getMapper(UserDao.class);List<User> users = userDao.findAll();for (User u: users) {System.out.println(u);}sqlSession.close();inputStream.close();} }

總結(jié)

斷點(diǎn)調(diào)試, 理解Mybatis執(zhí)行過(guò)程, 根據(jù)Mybatis執(zhí)行過(guò)程, 模擬實(shí)現(xiàn). 遇到問(wèn)題, 理清思路, debug調(diào)試.

總結(jié)

以上是生活随笔為你收集整理的Mybatis执行流程分析_自定义简易Mybatis框架的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。