編寫實現下列功能的程序段,MyBatis Demo 編寫(1)基礎功能搭建

 2023-10-12 阅读 27 评论 0

摘要:簡介 在Mybatis3的源碼解析系列中,我們對其核心功能有了一定的了解,下面我們嘗試簡單寫一下Demo,讓其有簡單的Mybatis的一些核心功能,本篇是基礎功能的搭建 Dome 編寫 完整的工程已放到GitHub上:https://github.com/lw1243925457/MybatisD

簡介

在Mybatis3的源碼解析系列中,我們對其核心功能有了一定的了解,下面我們嘗試簡單寫一下Demo,讓其有簡單的Mybatis的一些核心功能,本篇是基礎功能的搭建

Dome 編寫

完整的工程已放到GitHub上:https://github.com/lw1243925457/MybatisDemo/tree/master/

本篇文章的代碼對應的Tag是: V1

本篇的目標是完成MapperProxy和運行不帶參數和無自定義返回類型的SQL

測試代碼如下:

public class SelfMybatisTest {@Testpublic void test() {try(SelfSqlSession session = buildSqlSessionFactory()) {PersonMapper personMapper = session.getMapper(PersonMapper.class);personMapper.createTable();personMapper.save();List<Object> personList = personMapper.list();for (Object person: personList) {System.out.println(person.toString());}}}public static SelfSqlSession buildSqlSessionFactory() {String JDBC_DRIVER = "org.h2.Driver";String DB_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1";String USER = "sa";String PASS = "";HikariConfig config = new HikariConfig();config.setJdbcUrl(DB_URL);config.setUsername(USER);config.setPassword(PASS);config.setDriverClassName(JDBC_DRIVER);config.addDataSourceProperty("cachePrepStmts", "true");config.addDataSourceProperty("prepStmtCacheSize", "250");config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");DataSource dataSource = new HikariDataSource(config);SelfConfiguration configuration = new SelfConfiguration(dataSource);
//        configuration.getTypeHandlerRegistry().register(String[].class, JdbcType.VARCHAR, StringArrayTypeHandler.class);configuration.addMapper(PersonMapper.class);return new SelfSqlSession(configuration);}
}

編寫實現下列功能的程序段?如上所示,我們集成其他DataSource,這里使用HikariCP,然后初始化Mapper,生成表、插入、查詢

最終的結果如下:

初始化我們的PersonMapper,最后輸出查詢的結果

add sql source: mapper.mapper.PersonMapper.list
add sql source: mapper.mapper.PersonMapper.save
add sql source: mapper.mapper.PersonMapper.createTableexecutor
executor
executor[1, 1]

基本達到了要求,下面開始講解核心代碼:

Mapper初始化

我們的思路目前是:

  • 1.SelfConfiguration 作為全局配置類,從中取得DataSource和Mapper相關的信息(目前是接口方法對應的SQL語句)
  • 2.SelfConfiguration 構造函數保存DataSource
  • 3.SelfConfiguration 添加Mapper時,保存其中方式對應的SQL語句信息到Map,方法的路徑作為唯一標識

SelfConfiguration 相關的代碼

public class SelfConfiguration {private final DataSource dataSource;private final Map<String, SqlSource> sqlCache = new HashMap<>();public SelfConfiguration(DataSource dataSource) {this.dataSource = dataSource;}/*** Mapper添加* 保存接口方法的SQL類型和方法* 方法路徑作為唯一的id* @param mapperClass mapper*/public void addMapper(Class<?> mapperClass) {final String classPath = mapperClass.getPackageName();final String className = mapperClass.getName();for (Method method: mapperClass.getMethods()) {final String id = StringUtils.joinWith("." ,classPath, className, method.getName());for (Annotation annotation: method.getAnnotations()) {if (annotation instanceof Select) {addSqlSource(id, ((Select) annotation).value(), SqlType.SELECT);continue;}if (annotation instanceof Insert) {addSqlSource(id, ((Insert) annotation).value(), SqlType.INSERT);}}}}private void addSqlSource(final String id, final String sql, final SqlType selectType) {System.out.println("add sql source: " + id);final SqlSource sqlSource = SqlSource.builder().type(selectType).sql(sql).build();sqlCache.put(id, sqlSource);}public DataSource getDataSource() {return dataSource;}public SqlSource getSqlSource(final String id) {if (sqlCache.containsKey(id)) {return sqlCache.get(id);}throw new RuntimeException("don't find mapper match: " + id);}
}

MapperProxy生成

importjava,MapperProxy 從 SelfSqlSession 中進行獲取,MapperProxy中調用Executor的方法即可,比較簡單

SelfSqlSession 主要是返回MapperProxy

public class SelfSqlSession implements Closeable {private final SelfConfiguration config;public SelfSqlSession(SelfConfiguration configuration) {this.config = configuration;}@Overridepublic void close() {}public <T> T getMapper(Class<?> mapperClass) {final MapperProxy<T> proxy = new MapperProxy(config);return (T) Proxy.newProxyInstance(mapperClass.getClassLoader(), new Class[] {mapperClass}, proxy);}
}

MapperProxy 簡單調用下 Executor

public class MapperProxy<T> implements InvocationHandler {private final SelfConfiguration config;public MapperProxy(final SelfConfiguration configuration) {this.config = configuration;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return Executor.executor(config, proxy, method, args);}
}

SQL語句執行

主要邏輯如下:

  • SQL的編排主要還是在Executor中
  • 語句的解析和執行在StatementHandler中,返回返回結果
  • 結果的解析在ResultHandler中

Executor 代碼如下,獲取數據庫連接,StatementHandler執行,ResultHandler處理結果返回

public class Executor {private static final StatementHandler statementHandler = new StatementHandler();private static final ResultHandler resultHandler = new ResultHandler();public static Object executor(SelfConfiguration config, Object proxy, Method method, Object[] args) throws SQLException {System.out.println("executor");try (Connection conn = config.getDataSource().getConnection()) {ResultSet resultSet = statementHandler.prepare(conn, proxy, method, args, config);return resultHandler.parse(resultSet);}}
}

StetementHandler主要是從Config從獲取需要執行的SQL信息

編寫一個?目前比較簡單,根據SQL類型,直接執行即可

public class StatementHandler {public ResultSet prepare(Connection conn, Object proxy, Method method, Object[] args, SelfConfiguration config) throws SQLException {final String classPath = method.getDeclaringClass().getPackageName();final String className = method.getDeclaringClass().getName();final String methodName = method.getName();final String id = StringUtils.joinWith(".", classPath, className, methodName);final SqlSource sqlSource = config.getSqlSource(id);if (sqlSource.getType().equals(SqlType.SELECT)) {return select(conn, sqlSource);}if (sqlSource.getType().equals(SqlType.INSERT)) {return insert(conn, sqlSource);}throw new RuntimeException("don't support this sql type");}private ResultSet insert(Connection conn, SqlSource sqlSource) throws SQLException {final Statement statement = conn.createStatement();statement.execute(sqlSource.getSql());return null;}private ResultSet select(Connection conn, SqlSource sqlSource) throws SQLException {final Statement statement = conn.createStatement();return statement.executeQuery(sqlSource.getSql());}
}

ResultHandler 目前就是循環讀取結果,然后返回

public class ResultHandler {public List<Object> parse(ResultSet res) throws SQLException {if (res == null) {return null;}final List<Object> list = new ArrayList<>(res.getFetchSize());final ResultSetMetaData metaData = res.getMetaData();while (res.next()) {final int count =metaData.getColumnCount();final List<Object> val = new ArrayList<>(count);for (int i=1; i <= count; i++) {final String name = metaData.getColumnName(i);final Object value = res.getObject(name);val.add(value);}list.add(val);}return list;}
}

總結

本篇中搭建了MyBatis Demo的基礎部分,除了結果返回有點不好看,其他有了點日常MyBatis的味道了,結果處理后面也會完善上去

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/5/135529.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息