硬核手写简易mybatis框架

简介: 硬核手写简易mybatis框架

简易框架功能介绍

搭建这个简易的框架是为了加深对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. 
7. <groupId>org.example</groupId>
8. <artifactId>mybatis-xml-dom4j</artifactId>
9. <version>1.0-SNAPSHOT</version>
10. <dependencies>
11. <!--dom4j依赖-->
12. <dependency>
13. <groupId>org.dom4j</groupId>
14. <artifactId>dom4j</artifactId>
15. <version>2.1.3</version>
16. </dependency>
17. <!--jaxen依赖-->
18. <dependency>
19. <groupId>jaxen</groupId>
20. <artifactId>jaxen</artifactId>
21. <version>1.2.0</version>
22. </dependency>
23. <!--junit依赖-->
24. <dependency>
25. <groupId>junit</groupId>
26. <artifactId>junit</artifactId>
27. <version>4.13.2</version>
28. <scope>test</scope>
29. </dependency>
30. </dependencies>
31. 
32. <properties>
33. <maven.compiler.source>17</maven.compiler.source>
34. <maven.compiler.target>17</maven.compiler.target>
35. </properties>
36. 
37. </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. 
9. /**
10.      * 获取SqlSessionFactory对象
11.      * 读取batis核心配置文件,工具SqlSessionFactory对象
12.      * @param inputStream 指向核心配置文件的输入流动
13.      * @return
14.      */
15. public SqlSessionFactory build(InputStream inputStream){
16. /**
17.          * 主要负责功能解析配置文件
18.          *          创建数据源对象
19.          *          创建事物管理器对象
20.          *          获取所有的SQL映射文件
21.          *          封装到SqlSessionFactory对象中
22.          *
23.          */
24. return null;
25.     }
26. }

SqlSessionFactory设计和MappedStatement的编写

这个类应该包含:

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

JDBCTransaction属性

Map<String, MappedStatement>属性

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. 
11. public MapperStatement(String sqlId, String resultType, String sql, String parameterType, String sqlType) {
12. this.sqlId = sqlId;
13. this.resultType = resultType;
14. this.sql = sql;
15. this.parameterType = parameterType;
16. this.sqlType = sqlType;
17.     }
18. 
19. @Override
20. public String toString() {
21. return "MapperStatement{" +
22. "sqlId='" + sqlId + '\'' +
23. ", resultType='" + resultType + '\'' +
24. ", sql='" + sql + '\'' +
25. ", parameterType='" + parameterType + '\'' +
26. ", sqlType='" + sqlType + '\'' +
27. '}';
28.     }
29. 
30. public String getSqlId() {
31. return sqlId;
32.     }
33. 
34. public void setSqlId(String sqlId) {
35. this.sqlId = sqlId;
36.     }
37. 
38. public String getResultType() {
39. return resultType;
40.     }
41. 
42. public void setResultType(String resultType) {
43. this.resultType = resultType;
44.     }
45. 
46. public String getSql() {
47. return sql;
48.     }
49. 
50. public void setSql(String sql) {
51. this.sql = sql;
52.     }
53. 
54. public String getParameterType() {
55. return parameterType;
56.     }
57. 
58. public void setParameterType(String parameterType) {
59. this.parameterType = parameterType;
60.     }
61. 
62. public String getSqlType() {
63. return sqlType;
64.     }
65. 
66. public void setSqlType(String sqlType) {
67. this.sqlType = sqlType;
68.     }
69. 
70. 
71. }

JDBCTransaction设计

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

● transactionManager:配置事务管理器

 ○ type属性:指定事务管理器具体使用什么方式,可选值包括两个

   ■ JDBC:使用JDBC原生的事务管理机制。底层原理:事务开启conn.setAutoCommit(false); ...处理业务...事务提交conn.commit();

   ■ MANAGED:交给其它容器来管理事务,比如WebLogic、JBOSS等。如果没有管理事务的容器,则没有事务。没有事务的含义:只要执行一条DML语句,则提交一次。

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

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

JDBC事务管理器实现类

1. /**
2.  * @author 风轻云淡
3.  */
4. public class JDBCTransaction implements TransactionManager{
5. @Override
6. public void commit() {
7. 
8.     }
9. 
10. @Override
11. public void rollback() {
12. 
13.     }
14. 
15. @Override
16. public void close() {
17. 
18.     }
19. 
20. @Override
21. public void openConnection() {
22. 
23.     }
24. 
25. @Override
26. public Connection getConnection() {
27. return null;
28.     }
29. }

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. 
9. public UNPOOLEDDataSource(String driver,String url,String username,String password){
10. try {
11.             Class.forName(driver);
12. 
13.         }catch (Exception e){
14.             e.printStackTrace();
15.         }
16. this.url=url;
17. this.username=username;
18. this.password=password;
19.     }
20. 
21. 
22. @Override
23. public Connection getConnection() throws SQLException {
24. return DriverManager.getConnection(url,username,password);
25.     }
26. 
27. @Override
28. public Connection getConnection(String username, String password) throws SQLException {
29. return null;
30.     }
31. 
32. @Override
33. public PrintWriter getLogWriter() throws SQLException {
34. return null;
35.     }
36. 
37. @Override
38. public void setLogWriter(PrintWriter out) throws SQLException {
39. 
40.     }
41. 
42. @Override
43. public void setLoginTimeout(int seconds) throws SQLException {
44. 
45.     }
46. 
47. @Override
48. public int getLoginTimeout() throws SQLException {
49. return 0;
50.     }
51. 
52. @Override
53. public Logger getParentLogger() throws SQLFeatureNotSupportedException {
54. return null;
55.     }
56. 
57. @Override
58. public <T> T unwrap(Class<T> iface) throws SQLException {
59. return null;
60.     }
61. 
62. @Override
63. public boolean isWrapperFor(Class<?> iface) throws SQLException {
64. return false;
65.     }
66. }

SqlSessionFactory类完善

1. /**
2.  * @author 风轻云淡
3.  */
4. public class SqlSessionFactory {
5. private  TransactionManager transactionManager;
6. private Map<String,MapperStatement> mapperStatements;
7. 
8. public SqlSessionFactory(TransactionManager transactionManager, Map<String, MapperStatement> mapperStatements) {
9. this.transactionManager = transactionManager;
10. this.mapperStatements = mapperStatements;
11.     }
12. 
13. public TransactionManager getTransactionManager() {
14. return transactionManager;
15.     }
16. 
17. public void setTransactionManager(TransactionManager transactionManager) {
18. this.transactionManager = transactionManager;
19.     }
20. 
21. public Map<String, MapperStatement> getMapperStatements() {
22. return mapperStatements;
23.     }
24. 
25. public void setMapperStatements(Map<String, MapperStatement> mapperStatements) {
26. this.mapperStatements = mapperStatements;
27.     }
28. }

SqlSessionFactoryBuilder中的build方法编写

1. /**
2.  * @author 风轻云淡
3.  */
4. public class SqlSessionFactoryBuilder {
5. public SqlSessionFactoryBuilder() {
6. 
7.     }
8. 
9. /**
10.      * 获取SqlSessionFactory对象
11.      * 读取batis核心配置文件,工具SqlSessionFactory对象
12.      * @param inputStream 指向核心配置文件的输入流动
13.      * @return
14.      */
15. public SqlSessionFactory build(InputStream inputStream) throws DocumentException {
16. /**
17.          * 主要负责功能解析配置文件
18.          *          创建数据源对象
19.          *          创建事物管理器对象
20.          *          获取所有的SQL映射文件
21.          *          封装到SqlSessionFactory对象中
22.          *
23.          */
24. Document document = new SAXReader().read(inputStream);
25. Element environmentsElt = (Element) document.selectSingleNode("/configuration/environments");
26. String defaultEnv = environmentsElt.attributeValue("default");
27. //解析文件得出数据源对象
28. Element dataSourceElt = environmentsElt.element("dataSource");
29.         DataSource dataSource= getDataSource(dataSourceElt);
30. //解析文件得出事务管理器对象
31. Element transactionManagerElt = environmentsElt.element("transactionManager");
32. TransactionManager transactionManager =gettransactionManager(transactionManagerElt,dataSource);
33. //解析文件得出sql映射对象
34.         Element mapperElt=environmentsElt.element("mappers");
35.         Map<String,MapperStatement> mapperStatementMap =getMapperStatements(mapperElt);
36. SqlSessionFactory sqlSessionFactory = new SqlSessionFactory(transactionManager, mapperStatementMap);
37. return sqlSessionFactory;
38.     }
39. 
40. private Map<String, MapperStatement> getMapperStatements(Element mapperElt) {
41.         Map<String,MapperStatement> mapperStatements=new HashMap<>();
42. try {
43. String resource = mapperElt.attributeValue("resource");
44. SAXReader saxReader = new SAXReader();
45. Document document = saxReader.read(Resource.getResourcesAsStream(resource));
46. Element mapper = (Element) document.selectSingleNode("/mapper");
47. String namespace = mapper.attributeValue("namespace");
48.             mapper.elements().forEach(item->{
49. String sqlId = item.attributeValue("id");
50. String sql = item.getTextTrim();
51. String parameterType = item.attributeValue("parameterType");
52. String resultType = item.attributeValue("resultType");
53. String sqlType = item.getName().toLowerCase();
54. //封装对象
55. MapperStatement mapperStatement = new MapperStatement(sqlId, resultType, sql, parameterType, sqlType);
56.                 mapperStatements.put(namespace+"."+sqlId,mapperStatement);
57. 
58.             });
59. 
60.         }catch (Exception e){
61.             e.printStackTrace();
62.         }
63. return mapperStatements;
64.     }
65. 
66. private TransactionManager gettransactionManager(Element transactionManagerElt, DataSource dataSource) {
67. String type = transactionManagerElt.attributeValue("type").toUpperCase();
68.         TransactionManager transactionManager=null;
69. if("JDBC".equals(type)){
70.             transactionManager=new JDBCTransaction(dataSource,false);
71.         }else{
72. try {
73. throw  new Exception("本框架只支持JDBC事务管理");
74.             } catch (Exception e) {
75.                 e.printStackTrace();
76.             }
77.         };
78. return transactionManager;
79.     }
80. 
81. private DataSource getDataSource(Element dataSourceElt)  {
82. //获取所有数据源配置
83.         Map<String ,String> dataSourceMap=new HashMap<>();
84.         dataSourceElt.elements().forEach(item->{
85.             dataSourceMap.put(item.attributeValue("name"),item.attributeValue("value"));
86.         });
87. String dataSourceType = dataSourceElt.attributeValue("type").toUpperCase();
88.         DataSource dataSource=null;
89. if("UNPOOLED".equals(dataSourceType)){
90.             dataSource=new UNPOOLEDDataSource(dataSourceMap.get("driver"),
91.                     dataSourceMap.get("url"),
92.                     dataSourceMap.get("username"),
93.                     dataSourceMap.get("password"));
94.         }else{
95. try {
96. throw new Exception("本框架只实现了UNPOOLED");
97.             } catch (Exception e) {
98.                 e.printStackTrace();
99.             }
100.         }
101. return dataSource;
102.     }
103. }

SqlSession编写

1. public class SqlSession {
2.     TransactionManager transactionManager;
3.     Map<String, MapperStatement> mapperStatements;
4. 
5. public SqlSession(TransactionManager transactionManager, Map<String, MapperStatement> mapperStatements) {
6. this.transactionManager = transactionManager;
7. this.mapperStatements = mapperStatements;
8.     }
9. public void commit(){
10. try {
11.             transactionManager.getConnection().commit();
12.         } catch (SQLException e) {
13. throw new RuntimeException(e);
14.         }
15.     }
16. 
17. public void rollback(){
18. try {
19.             transactionManager.getConnection().rollback();
20.         } catch (SQLException e) {
21. throw new RuntimeException(e);
22.         }
23.     }
24. 
25. public void close(){
26. try {
27.             transactionManager.getConnection().close();
28.         } catch (SQLException e) {
29. throw new RuntimeException(e);
30.         }
31.     }
32. }

在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. // 重点一步
18.         Map<Integer, String> map = new HashMap<>();
19. int index = 1;
20. while (godbatisSql.indexOf("#") >= 0) {
21. int beginIndex = godbatisSql.indexOf("#") + 2;
22. int endIndex = godbatisSql.indexOf("}");
23.             map.put(index++, godbatisSql.substring(beginIndex, endIndex).trim());
24.             godbatisSql = godbatisSql.substring(endIndex + 1);
25.         }
26. 
27. final PreparedStatement ps;
28. try {
29.             ps = connection.prepareStatement(sql);
30. 
31. // 给?赋值
32.             map.forEach((k, v) -> {
33. try {
34. // 获取java实体类的get方法名
35. String getMethodName = "get" + v.toUpperCase().charAt(0) + v.substring(1);
36. Method getMethod = obj.getClass().getDeclaredMethod(getMethodName);
37.                     ps.setString(k, getMethod.invoke(obj).toString());
38.                 } catch (Exception e) {
39. throw new RuntimeException(e);
40.                 }
41.             });
42. int count = ps.executeUpdate();
43.             ps.close();
44. return count;
45.         } catch (Exception e) {
46. throw new RuntimeException(e);
47.         }
48.     }

编写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.     }


相关文章
|
2天前
|
SQL Java 数据库连接
MyBatis 框架入门理论与实践
MyBatis 框架入门理论与实践
31 6
|
2天前
|
SQL XML Java
程序员都要懂的SQL防注入Mybatis框架SQL防注入
程序员都要懂的SQL防注入Mybatis框架SQL防注入
22 0
|
2天前
|
SQL Java 数据库连接
MyBatis 优秀的持久层框架(一)
MyBatis 优秀的持久层框架
76 0
|
2天前
|
SQL 关系型数据库 Java
Mybatis-Flex框架初体验
Mybatis-Flex框架初体验
|
2天前
|
SQL 缓存 Java
持久层框架MyBatis
MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集的操作。MyBatis可以使用简单的XML或注解进行配置和原始映射,将接口和Java的POJOs(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
27 1
|
2天前
|
SQL 缓存 Java
【框架】MyBatis 框架重点解析
【框架】MyBatis 框架重点解析
7 0
|
2天前
|
SQL Java 数据库连接
【JavaEE】懒人的福音-MyBatis框架—复杂的操作-动态SQL(下)
【JavaEE】懒人的福音-MyBatis框架—复杂的操作-动态
5 0
|
2天前
|
SQL Java 数据库连接
【JavaEE】懒人的福音-MyBatis框架—复杂的操作-动态SQL(上)
【JavaEE】懒人的福音-MyBatis框架—复杂的操作-动态SQL
4 0
|
2天前
|
SQL Java 数据库连接
【JavaEE】懒人的福音-MyBatis框架—[单表]增删改查等常规操作(下)
【JavaEE】懒人的福音-MyBatis框架—[单表]增删改查等常规操作
6 0
|
2天前
|
SQL 前端开发 Java
【JavaEE】懒人的福音-MyBatis框架—[单表]增删改查等常规操作(上)
【JavaEE】懒人的福音-MyBatis框架—[单表]增删改查等常规操作
8 0