自定義持久層架構
文章目錄
-
- 自定義持久層架構
- 前言
- 一、JDBC問題分析
- 二、解決政策分析
- 二、架構設計
-
- 1.使用端
- 2. 架構端
- 三、實作
-
- 1.使用端
- 2.架構端
- 總結
前言
例如:為什麼連接配接資料庫不再使用JDBC,而改用諸如Mybatis的持久層架構,此篇通過自定義持久層架構分析對比了二者的差別,及持久層的實作原理
提示:以下是本篇文章正文内容,下面案例可供參考
一、JDBC問題分析
- 資料庫連接配接建立、釋放頻繁,造成系統資源浪費,進而影響系統性能;
- sql語句存在寫死,不易擴充和維護;
- 查詢結果集的封裝存在寫死,sql變化會導緻解析變化,不易維護;
二、解決政策分析
- 連接配接頻繁---->使用連接配接池
- sql語句寫死----->使用配置檔案
- 封裝結果集寫死 ------>使用反射、内省封裝pojo對象
二、架構設計
1.使用端
提供核心配置檔案
sqlMapConfig.xml 提供資料庫配置資訊,引入mapper.xml
mapper.xml 提供sql語句配置資訊
2. 架構端
-
加載配置檔案,以位元組流讀取到記憶體中
建立Resources類,getResourceAsStaream方法,傳回InputStream
-
建立Bean對象,存放解析出來的内容
Configuration:核心配置類,存放sqlMapConfig.xml 解析出來的内容;
MappedStatement:映射配置類,存放mapper.xml解析春來的内容;
Configuration 中引入MappedStatement,便于傳遞執行。
-
解析配置檔案
建立類SqlSessionFactoryBuilder
建立方法build
該方法做了兩件事:
第一,dom4j解析位元組輸入流,封裝成Configuration對象中;
第二,建立工廠類SqlSessionFactory對象。
-
建立SQLSessionFactory接口及實作類DefaultSqlSessionFactory
方法openSession,該方法擷取SqlSession接口的執行個體類對象
-
建立SqlSession接口及實作類DefaultSession
建立CRUD方法:selectList()、selectOne()、insert()、update()、delete()等,方法中調用Executor接口的實力類對象的方法
優化:在SqlSession中添加getMapper方法,通過代理實作(代理對象在調用任何方法,都會執行getMapper中的invoke方法)
-
建立Executor接口及實作類SimpleExecutor
該類的crud方法中執行的就是jdbc的代碼:注冊驅動獲連接配接、擷取sql、擷取預處理對象、設定參數、執行sql、封裝結果集
三、實作
1.使用端
sqlMapConfig.xml 代碼如下:
<configuration>
<!-- 資料庫配置資訊 -->
<dataSource>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/lagou?useUnicode=true&characterEncoding=utf8"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</dataSource>
<!--存放mapper.xml全路徑-->
<mapper resource="UserMapper.xml"></mapper>
</configuration>
sqlMapConfig.xml 代碼如下:
<mapper namespace="com.lagou.dao.IUserDao">
<!-- sql唯一辨別:namespace.id組成:statementId-->
<select id="findAll" resultType="com.lagou.pojo.User">
select * from user
</select>
<select id="findByCondition" resultType="com.lagou.pojo.User" paramterType="com.lagou.pojo.User">
select * from user where id=#{id} and username=#{username}
</select>
<insert id="addUser" paramterType="com.lagou.pojo.User">
insert into user values(#{id},#{username})
</insert>
<update id="updateUser" paramterType="com.lagou.pojo.User">
update user set username = #{username} where id = #{id}
</update>
<delete id="deleteUser" paramterType="com.lagou.pojo.User">
delete from user where id = #{id}
</delete>
</mapper>
2.架構端
Resources.java代碼如下:
public static InputStream getResourceAsStaream(String path){
InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path);
return resourceAsStream;
SqlSessionFactoryBuilder.java代碼如下:
public SqlSessionFactory build(InputStream in) throws PropertyVetoException, DocumentException {
//第一:使用dom4j解析配合檔案,将解析出來的配置檔案封裝到Configuration中
XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder();
Configuration configuration = xmlConfigBuilder.parseConfig(in);
//第二:建立SqlsessionFactory對象:工廠類:生産sqlSession:會話對象
DefaultSqlSessionFactory defaultSqlSessionFactory = new DefaultSqlSessionFactory(configuration);
return defaultSqlSessionFactory;
}
XMLConfigBuilder.java代碼如下:
/**
*
* 該方法就是将配置檔案進行解析,封裝到Configuration
*/
public Configuration parseConfig(InputStream in) throws DocumentException, PropertyVetoException {
Document document = new SAXReader().read(in);
//根标簽<configuration>
Element rootElement = document.getRootElement();
List<Element> list = rootElement.selectNodes("//property");
Properties properties = new Properties();
for (Element element : list) {
String name = element.attributeValue("name");
String value = element.attributeValue("value");
properties.setProperty(name,value);
}
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));
comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));
comboPooledDataSource.setUser(properties.getProperty("username"));
comboPooledDataSource.setPassword(properties.getProperty("password"));
configuration.setDataSource(comboPooledDataSource);
//mapper.xml解析:拿到路徑--位元組輸入流---解析
List<Element> mapperList = rootElement.selectNodes("//mapper");
for (Element element : mapperList) {
String mapperPath = element.attributeValue("resource");
InputStream resourceAsStaream = Resources.getResourceAsStaream(mapperPath);
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
xmlMapperBuilder.parse(resourceAsStaream);
}
return configuration;
}
xmlMapperBuilder.java代碼如下:
public void parse(InputStream inputStream) throws DocumentException {
Document document = new SAXReader().read(inputStream);
Element rootElement = document.getRootElement();
String namesapce = rootElement.attributeValue("namespace");
List<Element> selectlist = rootElement.selectNodes("//select");
List<Element> insertlist = rootElement.selectNodes("//insert");
List<Element> updatelist = rootElement.selectNodes("//update");
List<Element> deletelist = rootElement.selectNodes("//delete");
//組裝方法類型與标簽集合的對應關系
Map map = new HashMap<String,Object>();
map.put("select",selectlist);
map.put("insert",insertlist);
map.put("update",updatelist);
map.put("delete",deletelist);
Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator();
while(it.hasNext()){
Map.Entry<String, Object> entry = it.next();
dealConfiguration(configuration,(List<Element>)entry.getValue(),(String)entry.getKey(),namesapce);
}
}
/**
把配置檔案增删改查類型的标簽配置到MappedStatement,進而封裝到Configuration
*/
private void dealConfiguration(Configuration configuration,List<Element> list,String methodType,String namesapce){
for (Element element : list) {
String id = element.attributeValue("id");
String resultType = element.attributeValue("resultType");
String paramterType = element.attributeValue("paramterType");
String sqlText = element.getTextTrim();
MappedStatement mappedStatement = new MappedStatement();
mappedStatement.setId(id);
mappedStatement.setResultType(resultType);
mappedStatement.setParamterType(paramterType);
mappedStatement.setSql(sqlText);
mappedStatement.setMethodType(methodType);
String key = namesapce + "." + id;
configuration.getMappedStatementMap().put(key,mappedStatement);
}
}
DefaultSqlSession.java代碼如下:
@Override
public <E> List<E> selectList(String statementid, Object... params) throws Exception{
//完成對SimpleExecutor裡query方法的調用
SimpleExecutor simpleExecutor = new SimpleExecutor();
MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementid);
List<Object> list = simpleExecutor.query(configuration, mappedStatement, params);
return (List<E>) list;
}
DefaultSqlSession.java代碼如下:
@Override
public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
//1.注冊驅動擷取連接配接
Connection connection = configuration.getDataSource().getConnection();
//2.擷取SQL語句: select * from user where id=#{id} and username=#{username}
String sql = mappedStatement.getSql();
//解析sql語句: select * from user where id=? and username=? 轉換過程中,對#{}裡面的值進行存儲
BoundSql boundSql = getBoundSql(sql);
//3. 擷取預處理對象:paramterStatement
PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
//4. 設定參數
//擷取參數全路徑
String paramterType = mappedStatement.getParamterType();
Class<?> paramterClass = getClassType(paramterType);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
String content = parameterMapping.getContent();
//反射
Field declaredField = paramterClass.getDeclaredField(content);
//暴力通路
declaredField.setAccessible(true);
Object o = declaredField.get(params[0]);
preparedStatement.setObject(i+1,o);
}
//5. 執行sql
ResultSet resultSet = preparedStatement.executeQuery();
String resultType = mappedStatement.getResultType();
Class<?> resultTypeClass = getClassType(resultType);
ArrayList<Object> objects = new ArrayList<>();
//6. 封裝傳回結果集
while(resultSet.next()){
Object o = resultTypeClass.newInstance();
//中繼資料,因為中繼資料帶有列名
ResultSetMetaData metaData = resultSet.getMetaData();
for (int i = 1; i <= metaData.getColumnCount(); i++) {
//字段名
String columnName = metaData.getColumnName(i);
//字段值
Object value = resultSet.getObject(columnName);
//使用反射或内省,根據資料庫表和實體的對應關系完成封裝
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName,resultTypeClass);
Method writeMethod = propertyDescriptor.getWriteMethod();
writeMethod.invoke(o,value);
}
objects.add(o);
}
return (List<E>) objects;
}
總結
以上就是自定義持久層架構的思路及内容,其中對sql語句解析的類時mybatis中自帶的類。