生活随笔
收集整理的這篇文章主要介紹了
Mybatis执行流程分析_自定义简易Mybatis框架
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
自定義簡易Mybatis框架
Mybatis執行流程分析
- Mybatis代碼編寫流程:
- Mybatis配置文件加載過程:
需求分析及技術概述
1. 需要具有配置文件加載模塊.
2. 支持使用構建者進行SessionFactory構建.
3. 支持使用工廠模式構建Session對象.
4. 可以使用代理模式, 構建DAO接口實現類對象.
5. 使用反射機制, 封裝ResultSet結果集.
6. 支持使用.xml文件編寫SQL語句.
7. 支持使用注解方式進行SQL查詢.
1. 解析UserDao.xml; SqlMapConfig.xml需要使用dom4j, xpath技術2. 使用Configuration類(類似于Map結構)存儲SqlMapConfig中配置信息3. SqlMapConfig中包含連接池, 使用DataSource類根據配置信息, 創建連接池4. 使用構建者, 工廠, 代理模式開發5. 使用Java反射機制, 自定義注解.
項目搭建(本項目只構建Select * from tableName; 主要用于理解Mybatis執行過程)
本項目使用maven進行構建, pom.xml文件需引入如下依賴包:
<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技術
--><dependency><groupId>jaxen
</groupId
><artifactId>jaxen
</artifactId
><version>1.1.6</version
></dependency
>
項目目錄結構:
本項目分為如下幾塊內容:
配置文件加載(包括SqlMapConfig.xml; Mapper.xml, 注解).
配置數據源.
Session工廠
SqlSession
第一部分內容如下:
- 配置文件加載類實體編寫(用于封裝SqlMapConfig.xml文件內容):
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();
}
- 工具類準備(Xml文件解析, 數據源配置, Sql執行結果集封裝):
-
public class Resources {public static InputStream
getResourceAsStream(String filePath
) {return Resources
.class.getClassLoader().getResourceAsStream(filePath
);}
}
-
- Xml文件解析(將配置文件內容解析后, 并將其封裝到Configuration實體中)
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
);}}
}
-
- 結果集封裝(Sql執行后的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();}}}
}
第二部分內容:
- SqlSession工廠(此部分使用工廠模型, 構建者模型):
-
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
);}
}
第三部分內容:
public class Mapper {private String resultType
;private String queryString
;
public interface SqlSession {<T> T
getMapper(Class
<T> daoInterfaceClass
);void close();
}
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對象錯誤");}return new Executor().selectList(mapper
, connection
);}
}
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();}}}
}
測試類:
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();}
}
總結
斷點調試, 理解Mybatis執行過程, 根據Mybatis執行過程, 模擬實現. 遇到問題, 理清思路, debug調試.
總結
以上是生活随笔為你收集整理的Mybatis执行流程分析_自定义简易Mybatis框架的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。