硬核手写简易mybatis框架

末蓝、 2023-10-13 17:53 1阅读 0赞

目录

简易框架功能介绍

创建模块导入相关pom依赖

资源工具类

sqlSessionFactoryBuilder工厂构造类

SqlSessionFactory设计和MappedStatement的编写

JDBCTransaction设计

JDBC事务管理器实现类

UNPOOLEDDataSource数据源类设计

SqlSessionFactory类完善

SqlSessionFactoryBuilder中的build方法编写

SqlSession编写

在SqlSessionFactory中添加openSession方法

编写SqlSession类中的insert方法

编写SqlSession类中的selectOne方法


简易框架功能介绍

搭建这个简易的框架是为了加深对mybatis的理解,功能不是全部实现的(也没有能力),所以这个简易的框架的功能只支持表字段都为varchar,pojo为String类型的,而且本框架只支持JDBC事务管理器,只支持非池化,功能的话只实现了插入,查询(单个数据不支持多个)。

创建模块导入相关pom依赖

使用dom4j去进行解析xml文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>org.example</groupId>
  7. <artifactId>mybatis-xml-dom4j</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <dependencies>
  10. <!--dom4j依赖-->
  11. <dependency>
  12. <groupId>org.dom4j</groupId>
  13. <artifactId>dom4j</artifactId>
  14. <version>2.1.3</version>
  15. </dependency>
  16. <!--jaxen依赖-->
  17. <dependency>
  18. <groupId>jaxen</groupId>
  19. <artifactId>jaxen</artifactId>
  20. <version>1.2.0</version>
  21. </dependency>
  22. <!--junit依赖-->
  23. <dependency>
  24. <groupId>junit</groupId>
  25. <artifactId>junit</artifactId>
  26. <version>4.13.2</version>
  27. <scope>test</scope>
  28. </dependency>
  29. </dependencies>
  30. <properties>
  31. <maven.compiler.source>17</maven.compiler.source>
  32. <maven.compiler.target>17</maven.compiler.target>
  33. </properties>
  34. </project>
资源工具类

这个类用于读取指定配置文件的输入流,即读取resourse目录下的文件

  1. /**
  2. * @author 风轻云淡
  3. */
  4. public class Resource {
  5. /**
  6. * 从类路径中获取配置文件输入输出流动
  7. */
  8. public static InputStream getResourcesAsStream(String path){
  9. return Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
  10. }
  11. }
sqlSessionFactoryBuilder工厂构造类

读取batis核心配置文件,工具SqlSessionFactory对象

build方法主要负责功能解析配置文件:

创建数据源对象

创建事物管理器对象

获取所有的SQL映射文件

  1. /**
  2. * @author 风轻云淡
  3. */
  4. public class SqlSessionFactoryBuilder {
  5. public SqlSessionFactoryBuilder() {
  6. }
  7. /**
  8. * 获取SqlSessionFactory对象
  9. * 读取batis核心配置文件,工具SqlSessionFactory对象
  10. * @param inputStream 指向核心配置文件的输入流动
  11. * @return
  12. */
  13. public SqlSessionFactory build(InputStream inputStream){
  14. /**
  15. * 主要负责功能解析配置文件
  16. * 创建数据源对象
  17. * 创建事物管理器对象
  18. * 获取所有的SQL映射文件
  19. * 封装到SqlSessionFactory对象中
  20. *
  21. */
  22. return null;
  23. }
  24. }
SqlSessionFactory设计和MappedStatement的编写

这个类应该包含:

一个属性为事务管理器,对应执行sql语句的MappedStatement对象

JDBCTransaction属性

Map属性

MappedStatement:

  1. /**
  2. * @author 风轻云淡
  3. */
  4. public class MapperStatement {
  5. private String sqlId;
  6. private String resultType;
  7. private String sql;
  8. private String parameterType;
  9. private String sqlType;
  10. public MapperStatement(String sqlId, String resultType, String sql, String parameterType, String sqlType) {
  11. this.sqlId = sqlId;
  12. this.resultType = resultType;
  13. this.sql = sql;
  14. this.parameterType = parameterType;
  15. this.sqlType = sqlType;
  16. }
  17. @Override
  18. public String toString() {
  19. return "MapperStatement{" +
  20. "sqlId='" + sqlId + '\'' +
  21. ", resultType='" + resultType + '\'' +
  22. ", sql='" + sql + '\'' +
  23. ", parameterType='" + parameterType + '\'' +
  24. ", sqlType='" + sqlType + '\'' +
  25. '}';
  26. }
  27. public String getSqlId() {
  28. return sqlId;
  29. }
  30. public void setSqlId(String sqlId) {
  31. this.sqlId = sqlId;
  32. }
  33. public String getResultType() {
  34. return resultType;
  35. }
  36. public void setResultType(String resultType) {
  37. this.resultType = resultType;
  38. }
  39. public String getSql() {
  40. return sql;
  41. }
  42. public void setSql(String sql) {
  43. this.sql = sql;
  44. }
  45. public String getParameterType() {
  46. return parameterType;
  47. }
  48. public void setParameterType(String parameterType) {
  49. this.parameterType = parameterType;
  50. }
  51. public String getSqlType() {
  52. return sqlType;
  53. }
  54. public void setSqlType(String sqlType) {
  55. this.sqlType = sqlType;
  56. }
  57. }
JDBCTransaction设计

我们知道mybatis的事务管理器类型有只能填MANAGED或者JDBC,在本框架中我们只实现最简单的JDBC事务管理器

● transactionManager:配置事务管理器
○ type属性:指定事务管理器具体使用什么方式,可选值包括两个
■ JDBC:使用JDBC原生的事务管理机制。底层原理:事务开启conn.setAutoCommit(false); …处理业务…事务提交conn.commit();
■ MANAGED:交给其它容器来管理事务,比如WebLogic、JBOSS等。如果没有管理事务的容器,则没有事务。没有事务的含义:只要执行一条DML语句,则提交一次。

74a66b08ac614cd8a77fb29bfb9df193.png

事务管理器最好是定义一个接口,然后每一个具体的事务管理器都实现这个接口。

  1. /**
  2. * @author 风轻云淡
  3. */
  4. public class JDBCTransaction implements TransactionManager{
  5. /**
  6. * 数据库连接对象
  7. */
  8. private Connection connection;
  9. /**
  10. * 数据源对象
  11. */
  12. private DataSource dataSource;
  13. /**
  14. * 自动提交标记
  15. * true:自动提交
  16. * false:不自动提交
  17. */
  18. private boolean autoCommit;
  19. /**
  20. * 构造事务管理器对象
  21. */
  22. public JDBCTransaction( DataSource dataSource,Connection connection) {
  23. this.connection = connection;
  24. this.dataSource = dataSource;
  25. }
  26. @Override
  27. public void commit() {
  28. try {
  29. connection.commit();
  30. }catch (Exception e){
  31. e.printStackTrace();
  32. }
  33. }
  34. @Override
  35. public void rollback() {
  36. try {
  37. connection.rollback();
  38. } catch (SQLException throwables) {
  39. throwables.printStackTrace();
  40. }
  41. }
  42. @Override
  43. public void close() {
  44. try {
  45. connection.close();
  46. } catch (SQLException throwables) {
  47. throwables.printStackTrace();
  48. }
  49. }
  50. @Override
  51. public void openConnection() {
  52. try {
  53. Connection connection = dataSource.getConnection();
  54. this.connection.setAutoCommit(this.autoCommit);
  55. } catch (SQLException throwables) {
  56. throwables.printStackTrace();
  57. }
  58. }
  59. @Override
  60. public Connection getConnection() {
  61. return connection;
  62. }
  63. }
JDBC事务管理器实现类
  1. /**
  2. * @author 风轻云淡
  3. */
  4. public class JDBCTransaction implements TransactionManager{
  5. @Override
  6. public void commit() {
  7. }
  8. @Override
  9. public void rollback() {
  10. }
  11. @Override
  12. public void close() {
  13. }
  14. @Override
  15. public void openConnection() {
  16. }
  17. @Override
  18. public Connection getConnection() {
  19. return null;
  20. }
  21. }
UNPOOLEDDataSource数据源类设计

unpool即在本框架中不使用数据库连接池技术实现

  1. /**
  2. * @author 风轻云淡
  3. */
  4. public class UNPOOLEDDataSource implements javax.sql.DataSource{
  5. private String url;
  6. private String username;
  7. private String password;
  8. public UNPOOLEDDataSource(String driver,String url,String username,String password){
  9. try {
  10. Class.forName(driver);
  11. }catch (Exception e){
  12. e.printStackTrace();
  13. }
  14. this.url=url;
  15. this.username=username;
  16. this.password=password;
  17. }
  18. @Override
  19. public Connection getConnection() throws SQLException {
  20. return DriverManager.getConnection(url,username,password);
  21. }
  22. @Override
  23. public Connection getConnection(String username, String password) throws SQLException {
  24. return null;
  25. }
  26. @Override
  27. public PrintWriter getLogWriter() throws SQLException {
  28. return null;
  29. }
  30. @Override
  31. public void setLogWriter(PrintWriter out) throws SQLException {
  32. }
  33. @Override
  34. public void setLoginTimeout(int seconds) throws SQLException {
  35. }
  36. @Override
  37. public int getLoginTimeout() throws SQLException {
  38. return 0;
  39. }
  40. @Override
  41. public Logger getParentLogger() throws SQLFeatureNotSupportedException {
  42. return null;
  43. }
  44. @Override
  45. public <T> T unwrap(Class<T> iface) throws SQLException {
  46. return null;
  47. }
  48. @Override
  49. public boolean isWrapperFor(Class<?> iface) throws SQLException {
  50. return false;
  51. }
  52. }
SqlSessionFactory类完善
  1. /**
  2. * @author 风轻云淡
  3. */
  4. public class SqlSessionFactory {
  5. private TransactionManager transactionManager;
  6. private Map<String,MapperStatement> mapperStatements;
  7. public SqlSessionFactory(TransactionManager transactionManager, Map<String, MapperStatement> mapperStatements) {
  8. this.transactionManager = transactionManager;
  9. this.mapperStatements = mapperStatements;
  10. }
  11. public TransactionManager getTransactionManager() {
  12. return transactionManager;
  13. }
  14. public void setTransactionManager(TransactionManager transactionManager) {
  15. this.transactionManager = transactionManager;
  16. }
  17. public Map<String, MapperStatement> getMapperStatements() {
  18. return mapperStatements;
  19. }
  20. public void setMapperStatements(Map<String, MapperStatement> mapperStatements) {
  21. this.mapperStatements = mapperStatements;
  22. }
  23. }
SqlSessionFactoryBuilder中的build方法编写
  1. /**
  2. * @author 风轻云淡
  3. */
  4. public class SqlSessionFactoryBuilder {
  5. public SqlSessionFactoryBuilder() {
  6. }
  7. /**
  8. * 获取SqlSessionFactory对象
  9. * 读取batis核心配置文件,工具SqlSessionFactory对象
  10. * @param inputStream 指向核心配置文件的输入流动
  11. * @return
  12. */
  13. public SqlSessionFactory build(InputStream inputStream) throws DocumentException {
  14. /**
  15. * 主要负责功能解析配置文件
  16. * 创建数据源对象
  17. * 创建事物管理器对象
  18. * 获取所有的SQL映射文件
  19. * 封装到SqlSessionFactory对象中
  20. *
  21. */
  22. Document document = new SAXReader().read(inputStream);
  23. Element environmentsElt = (Element) document.selectSingleNode("/configuration/environments");
  24. String defaultEnv = environmentsElt.attributeValue("default");
  25. //解析文件得出数据源对象
  26. Element dataSourceElt = environmentsElt.element("dataSource");
  27. DataSource dataSource= getDataSource(dataSourceElt);
  28. //解析文件得出事务管理器对象
  29. Element transactionManagerElt = environmentsElt.element("transactionManager");
  30. TransactionManager transactionManager =gettransactionManager(transactionManagerElt,dataSource);
  31. //解析文件得出sql映射对象
  32. Element mapperElt=environmentsElt.element("mappers");
  33. Map<String,MapperStatement> mapperStatementMap =getMapperStatements(mapperElt);
  34. SqlSessionFactory sqlSessionFactory = new SqlSessionFactory(transactionManager, mapperStatementMap);
  35. return sqlSessionFactory;
  36. }
  37. private Map<String, MapperStatement> getMapperStatements(Element mapperElt) {
  38. Map<String,MapperStatement> mapperStatements=new HashMap<>();
  39. try {
  40. String resource = mapperElt.attributeValue("resource");
  41. SAXReader saxReader = new SAXReader();
  42. Document document = saxReader.read(Resource.getResourcesAsStream(resource));
  43. Element mapper = (Element) document.selectSingleNode("/mapper");
  44. String namespace = mapper.attributeValue("namespace");
  45. mapper.elements().forEach(item->{
  46. String sqlId = item.attributeValue("id");
  47. String sql = item.getTextTrim();
  48. String parameterType = item.attributeValue("parameterType");
  49. String resultType = item.attributeValue("resultType");
  50. String sqlType = item.getName().toLowerCase();
  51. //封装对象
  52. MapperStatement mapperStatement = new MapperStatement(sqlId, resultType, sql, parameterType, sqlType);
  53. mapperStatements.put(namespace+"."+sqlId,mapperStatement);
  54. });
  55. }catch (Exception e){
  56. e.printStackTrace();
  57. }
  58. return mapperStatements;
  59. }
  60. private TransactionManager gettransactionManager(Element transactionManagerElt, DataSource dataSource) {
  61. String type = transactionManagerElt.attributeValue("type").toUpperCase();
  62. TransactionManager transactionManager=null;
  63. if("JDBC".equals(type)){
  64. transactionManager=new JDBCTransaction(dataSource,false);
  65. }else{
  66. try {
  67. throw new Exception("本框架只支持JDBC事务管理");
  68. } catch (Exception e) {
  69. e.printStackTrace();
  70. }
  71. };
  72. return transactionManager;
  73. }
  74. private DataSource getDataSource(Element dataSourceElt) {
  75. //获取所有数据源配置
  76. Map<String ,String> dataSourceMap=new HashMap<>();
  77. dataSourceElt.elements().forEach(item->{
  78. dataSourceMap.put(item.attributeValue("name"),item.attributeValue("value"));
  79. });
  80. String dataSourceType = dataSourceElt.attributeValue("type").toUpperCase();
  81. DataSource dataSource=null;
  82. if("UNPOOLED".equals(dataSourceType)){
  83. dataSource=new UNPOOLEDDataSource(dataSourceMap.get("driver"),
  84. dataSourceMap.get("url"),
  85. dataSourceMap.get("username"),
  86. dataSourceMap.get("password"));
  87. }else{
  88. try {
  89. throw new Exception("本框架只实现了UNPOOLED");
  90. } catch (Exception e) {
  91. e.printStackTrace();
  92. }
  93. }
  94. return dataSource;
  95. }
  96. }
SqlSession编写
  1. public class SqlSession {
  2. TransactionManager transactionManager;
  3. Map<String, MapperStatement> mapperStatements;
  4. public SqlSession(TransactionManager transactionManager, Map<String, MapperStatement> mapperStatements) {
  5. this.transactionManager = transactionManager;
  6. this.mapperStatements = mapperStatements;
  7. }
  8. public void commit(){
  9. try {
  10. transactionManager.getConnection().commit();
  11. } catch (SQLException e) {
  12. throw new RuntimeException(e);
  13. }
  14. }
  15. public void rollback(){
  16. try {
  17. transactionManager.getConnection().rollback();
  18. } catch (SQLException e) {
  19. throw new RuntimeException(e);
  20. }
  21. }
  22. public void close(){
  23. try {
  24. transactionManager.getConnection().close();
  25. } catch (SQLException e) {
  26. throw new RuntimeException(e);
  27. }
  28. }
  29. }
在SqlSessionFactory中添加openSession方法
  1. public SqlSession openSession(){
  2. transactionManager.openConnection();
  3. SqlSession sqlSession = new SqlSession(transactionManager, mappedStatements);
  4. return sqlSession;
  5. }
编写SqlSession类中的insert方法
  1. /**
  2. * 插入数据
  3. *
  4. * @param sqlId 要执行的sqlId
  5. * @param obj 插入的数据
  6. * @return
  7. */
  8. public int insert(String sqlId, Object obj) {
  9. MapperStatement godMappedStatement = mapperStatements.get(sqlId);
  10. Connection connection = transactionManager.getConnection();
  11. // 获取sql语句
  12. // insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
  13. String godbatisSql = godMappedStatement.getSql();
  14. // insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values(null,?,?,?,?,?)
  15. String sql = godbatisSql.replaceAll("#\\{[a-zA-Z0-9_\\$]*}", "?");
  16. // 重点一步
  17. Map<Integer, String> map = new HashMap<>();
  18. int index = 1;
  19. while (godbatisSql.indexOf("#") >= 0) {
  20. int beginIndex = godbatisSql.indexOf("#") + 2;
  21. int endIndex = godbatisSql.indexOf("}");
  22. map.put(index++, godbatisSql.substring(beginIndex, endIndex).trim());
  23. godbatisSql = godbatisSql.substring(endIndex + 1);
  24. }
  25. final PreparedStatement ps;
  26. try {
  27. ps = connection.prepareStatement(sql);
  28. // 给?赋值
  29. map.forEach((k, v) -> {
  30. try {
  31. // 获取java实体类的get方法名
  32. String getMethodName = "get" + v.toUpperCase().charAt(0) + v.substring(1);
  33. Method getMethod = obj.getClass().getDeclaredMethod(getMethodName);
  34. ps.setString(k, getMethod.invoke(obj).toString());
  35. } catch (Exception e) {
  36. throw new RuntimeException(e);
  37. }
  38. });
  39. int count = ps.executeUpdate();
  40. ps.close();
  41. return count;
  42. } catch (Exception e) {
  43. throw new RuntimeException(e);
  44. }
  45. }
编写SqlSession类中的selectOne方法
  1. /**
  2. * 查询一个对象
  3. * @param sqlId
  4. * @param parameterObj
  5. * @return
  6. */
  7. public Object selectOne(String sqlId, Object parameterObj){
  8. MapperStatement godMappedStatement = mapperStatements.get(sqlId);
  9. Connection connection = transactionManager.getConnection();
  10. // 获取sql语句
  11. String godbatisSql = godMappedStatement.getSql();
  12. String sql = godbatisSql.replaceAll("#\\{[a-zA-Z0-9_\\$]*}", "?");
  13. // 执行sql
  14. PreparedStatement ps = null;
  15. ResultSet rs = null;
  16. Object obj = null;
  17. try {
  18. ps = connection.prepareStatement(sql);
  19. ps.setString(1, parameterObj.toString());
  20. rs = ps.executeQuery();
  21. if (rs.next()) {
  22. // 将结果集封装对象,通过反射
  23. String resultType = godMappedStatement.getResultType();
  24. Class<?> aClass = Class.forName(resultType);
  25. Constructor<?> con = aClass.getDeclaredConstructor();
  26. obj = con.newInstance();
  27. // 给对象obj属性赋值
  28. ResultSetMetaData rsmd = rs.getMetaData();
  29. int columnCount = rsmd.getColumnCount();
  30. for (int i = 1; i <= columnCount; i++) {
  31. String columnName = rsmd.getColumnName(i);
  32. String setMethodName = "set" + columnName.toUpperCase().charAt(0) + columnName.substring(1);
  33. Method setMethod = aClass.getDeclaredMethod(setMethodName, aClass.getDeclaredField(columnName).getType());
  34. setMethod.invoke(obj, rs.getString(columnName));
  35. }
  36. }
  37. } catch (Exception e) {
  38. throw new RuntimeException(e);
  39. } finally {
  40. if (rs != null) {
  41. try {
  42. rs.close();
  43. } catch (SQLException e) {
  44. throw new RuntimeException(e);
  45. }
  46. }
  47. try {
  48. ps.close();
  49. } catch (SQLException e) {
  50. throw new RuntimeException(e);
  51. }
  52. }
  53. return obj;
  54. }

发表评论

表情:
评论列表 (有 0 条评论,1人围观)

还没有评论,来说两句吧...

相关阅读

    相关 简易Mybatis

    手写简易的Mybatis -------------------- 此篇文章用来记录今天花个五个小时写出来的简易版mybatis,主要实现了基于注解方式的增删查改,...