生活随笔
收集整理的這篇文章主要介紹了
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).
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)容):
public class Configuration {private String driver
;private String username
;private String url
;private String password
;private Map
<String, Mapper> mappers
= new HashMap<>(16);
}
@Retention(RetentionPolicy
.RUNTIME
)
@Target(ElementType
.METHOD
)
public @
interface Select {String
value();
}
- 工具類(lèi)準(zhǔn)備(Xml文件解析, 數(shù)據(jù)源配置, Sql執(zhí)行結(jié)果集封裝):
-
public class Resources {public static InputStream
getResourceAsStream(String filePath
) {return Resources
.class.getClassLoader().getResourceAsStream(filePath
);}
}
-
- Xml文件解析(將配置文件內(nèi)容解析后, 并將其封裝到Configuration實(shí)體中)
public class XMLConfigBuilder {public static Configuration
loadConfiguration(InputStream config
){try{Configuration cfg
= new Configuration();SAXReader reader
= new SAXReader();Document document
= reader
.read(config
);Element root
= document
.getRootElement();List
<Element> propertyElements
= root
.selectNodes("//property");for(Element propertyElement
: propertyElements
){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
)){String password
= propertyElement
.attributeValue("value");cfg
.setPassword(password
);}}List
<Element> mapperElements
= root
.selectNodes("//mappers/mapper");for(Element mapperElement
: mapperElements
){Attribute attribute
= mapperElement
.attribute("resource");if(attribute
!= null
){String mapperPath
= attribute
.getValue();Map
<String,Mapper> mappers
= loadMapperConfiguration(mapperPath
);cfg
.setMappers(mappers
);}else{String daoClassPath
= mapperElement
.attributeValue("class");Map
<String, Mapper> mappers
= loadMapperAnnotation(daoClassPath
);cfg
.setMappers(mappers
);}}return cfg
;}catch(Exception e
){throw new RuntimeException(e
);}finally{try {config
.close();}catch(Exception e
){e
.printStackTrace();}}}private static Map
<String,Mapper> loadMapperConfiguration(String mapperPath
)throws IOException
{InputStream in
= null
;try{Map
<String,Mapper> mappers
= new HashMap<String,Mapper>(16);in
= Resources
.getResourceAsStream(mapperPath
);SAXReader reader
= new SAXReader();Document document
= reader
.read(in
);Element root
= document
.getRootElement();String namespace
= root
.attributeValue("namespace");List
<Element> selectElements
= root
.selectNodes("//select");for(Element selectElement
: selectElements
){String id
= selectElement
.attributeValue("id");String resultType
= selectElement
.attributeValue("resultType");String queryString
= selectElement
.getText();String key
= namespace
+"."+id
;Mapper mapper
= new Mapper();mapper
.setQueryString(queryString
);mapper
.setResultType(resultType
);mappers
.put(key
,mapper
);}return mappers
;}catch(Exception e
){throw new RuntimeException(e
);}finally{in
.close();}}private static Map
<String,Mapper> loadMapperAnnotation(String daoClassPath
)throws Exception
{Map
<String,Mapper> mappers
= new HashMap<String, Mapper>();Class
daoClass = Class
.forName(daoClassPath
);Method
[] methods
= daoClass
.getMethods();for(Method method
: methods
){boolean isAnnotated
= method
.isAnnotationPresent(Select
.class);if(isAnnotated
){Mapper mapper
= new Mapper();Select selectAnno
= method
.getAnnotation(Select
.class);String queryString
= selectAnno
.value();mapper
.setQueryString(queryString
);Type type
= method
.getGenericReturnType();if(type
instanceof ParameterizedType){ParameterizedType ptype
= (ParameterizedType
)type
;Type
[] types
= ptype
.getActualTypeArguments();Class
domainClass = (Class
)types
[0];String resultType
= domainClass
.getName();mapper
.setResultType(resultType
);}String methodName
= method
.getName();String className
= method
.getDeclaringClass().getName();String key
= className
+"."+methodName
;mappers
.put(key
,mapper
);}}return mappers
;}
}
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封裝)
public class Executor {public <E> List
<E> selectList(Mapper mapper
, Connection conn
) {PreparedStatement pstm
= null
;ResultSet rs
= null
;try {String queryString
= mapper
.getQueryString();String resultType
= mapper
.getResultType();Class
domainClass = Class
.forName(resultType
);pstm
= conn
.prepareStatement(queryString
);rs
= pstm
.executeQuery();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
++) {String columnName
= rsmd
.getColumnName(i
);Object columnValue
= rs
.getObject(columnName
);PropertyDescriptor pd
= new PropertyDescriptor(columnName
,domainClass
);Method writeMethod
= pd
.getWriteMethod();writeMethod
.invoke(obj
,columnValue
);}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)建者模型):
-
public interface SqlSessionFactory {SqlSession
openSession();
}
public class SqlSessionFactoryBuilder {public SqlSessionFactory
build(InputStream inputStream
) {Configuration configuration
= XMLConfigBuilder
.loadConfiguration(inputStream
);return new DefaultSqlSessionFactory(configuration
);}
}
public class DefaultSqlSessionFactory implements SqlSessionFactory {private Configuration configuration
;public DefaultSqlSessionFactory(Configuration configuration
) {this.configuration
= configuration
;}@Overridepublic SqlSession
openSession() {return new DefaultSqlSession(configuration
);}
}
第三部分內(nèi)容:
public class Mapper {private String resultType
;private String queryString
;
public interface SqlSession {<T> T
getMapper(Class
<T> daoInterfaceClass
);void close();
}
-
- MapperProxy(生成Dao接口代理類(lèi)):
public class MapperProxy implements InvocationHandler {private Map
<String, Mapper> mappers
;private Connection connection
;public MapperProxy(Map
<String, Mapper> mappers
, Connection connection
) {this.mappers
= mappers
;this.connection
= connection
;}@Overridepublic Object
invoke(Object proxy
, Method method
, Object
[] args
) throws Throwable
{String methodName
= method
.getName();String className
= method
.getDeclaringClass().getName();String key
= className
+ "." + methodName
;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):
public class DefaultSqlSession implements SqlSession {private Configuration configuration
;private Connection connection
;public DefaultSqlSession(Configuration configuration
) {this.configuration
= configuration
;this.connection
= DataSource
.getConnection(configuration
);}@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):
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();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ò),歡迎將生活随笔推薦給好友。