Mybatis学习之自定义持久层框架(六) 自定义持久层框架:完善CRUD方法并进行测试

时间:2023-03-09 07:10:12
Mybatis学习之自定义持久层框架(六) 自定义持久层框架:完善CRUD方法并进行测试

前言

没想到会等到半年以后才来写这篇文章,我已经不记得当初自己想要在这篇文章中写什么了,还好有一些零散的笔记留着,就对照着上一篇文章及零散的笔记,把内容给补充完吧。

完善CRUD方法

完善DefaultSqlSession类实现查询单个及查询多个的接口

 1 package com.hardy.sqlSession;
2
3 import com.hardy.pojo.Configuration;
4 import com.hardy.pojo.MappedStatement;
5 import java.sql.SQLException;
6 import java.util.List;
7
8 public class DefaultSqlSession implements SqlSession {
9
10 private Configuration configuration;
11
12 public DefaultSqlSession(Configuration configuration) {
13 this.configuration = configuration;
14 }
15
16 // 处理器对象
17 private Executor simpleExcutor = new SimpleExecutor();
18
19 @Override
20 public <E> List<E> selectList(String statementId, Object... params) throws Exception {
21 // 对SimpleExecutor里的query方法的调用
22 SimpleExecutor simpleExecutor = new SimpleExecutor();
23 MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
24 List<Object> list = simpleExecutor.query(configuration, mappedStatement, params);
25
26 return (List<E>) list;
27 }
28
29 @Override
30 public <T> T selectOne(String statementId, Object... params) throws Exception {
31 List<Object> objects = selectList(statementId, params);
32 if (objects.size() == 1) {
33 return (T) objects.get(0);
34 } else {
35 throw new RuntimeException("查询结果过多或返回结果过多");
36 }
37
38 }
39
40 public void close() throws SQLException {
41 simpleExcutor.close();
42 }
43
44 }

这里实现了单条记录查询即列表查询的方法。

编写User实体类

 1 package com.hardy.pojo;
2
3 /*
4 * 实体类,对应数据库的user表
5 * */
6 public class User {
7
8 private Integer id;
9
10 private String username;
11
12 public Integer getId() {
13 return id;
14 }
15
16 public void setId(Integer id) {
17 this.id = id;
18 }
19
20 public String getUsername() {
21 return username;
22 }
23
24 public void setUsername(String username) {
25 this.username = username;
26 }
27 }

创建Executor接口及SimpleExecutor实现类

 1 package com.hardy.sqlSession;
2
3 import com.hardy.pojo.Configuration;
4 import com.hardy.pojo.MappedStatement;
5
6 import java.sql.SQLException;
7 import java.util.List;
8
9 public interface Executor {
10
11 public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception;
12
13 void close() throws SQLException;
14
15 }
  1 package com.hardy.sqlSession;
2
3 import com.hardy.pojo.Configuration;
4 import com.hardy.pojo.MappedStatement;
5 import com.hardy.utils.GenericTokenParser;
6 import com.hardy.utils.ParameterMapping;
7 import com.hardy.utils.ParameterMappingTokenHandler;
8
9 import java.beans.PropertyDescriptor;
10 import java.lang.reflect.Field;
11 import java.lang.reflect.InvocationTargetException;
12 import java.lang.reflect.Method;
13 import java.sql.*;
14 import java.util.ArrayList;
15 import java.beans.IntrospectionException;
16 import java.util.List;
17
18 public class SimpleExecutor implements Executor {
19
20 private Connection connection = null;
21
22 @Override
23 public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement,Object[] params) throws SQLException, ClassNotFoundException, IllegalAccessException,
24 InstantiationException, NoSuchFieldException, IntrospectionException, InvocationTargetException {
25 // 1、注册驱动,获取连接
26 connection = configuration.getDataSource().getConnection();
27
28 // 2、获取sql语句:select * from user where id = #{id} and username = #{username}(解析前)
29 String sql = mappedStatement.getSql();
30
31 // 3、对sql进行处理
32 BoundSql boundSql = getBoundSql(sql);
33
34 // 4、获取转换后的sql语句:// select * from where id = ? and username = ? (解析后)
35 String finalSql = boundSql.getSqlText();
36
37 // 5、获取预编译preparedStatement对象
38 PreparedStatement preparedStatement = connection.prepareStatement(finalSql);
39
40 // 6、设置参数,获取参数全路径
41 String parameterType = mappedStatement.getParamterType();
42 Class<?> paramterTypeClass = getClassType(parameterType);
43
44
45
46 List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
47
48 for (int i = 0; i < parameterMappingList.size(); i++) {
49 ParameterMapping parameterMapping = parameterMappingList.get(i);
50 String name = parameterMapping.getName();
51
52 // 反射
53 Field declaredField = paramterTypeClass.getDeclaredField(name);
54 declaredField.setAccessible(true);
55
56 // 参数的值
57 Object o = declaredField.get(params[0]);
58
59 // 给占位符赋值
60 preparedStatement.setObject(i+1, o);
61 }
62
63 ResultSet resultSet = preparedStatement.executeQuery();
64 String resultType = mappedStatement.getResultType();
65 Class<?> resultTypeClass = getClassType(resultType);
66 ArrayList<E> results = new ArrayList<E>();
67 while (resultSet.next()) {
68 ResultSetMetaData metaData = resultSet.getMetaData();
69 E o = (E) resultTypeClass.newInstance();
70 int columnCount = metaData.getColumnCount();
71 for (int i = 1; i <= columnCount; i++) {
72 // 字段名
73 String columnName = metaData.getColumnName(i);
74
75 // 字段值
76 Object value = resultSet.getObject(columnName);
77
78 // 创建字段描述器,为属性生成读写方法
79 PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
80
81 // 获取写方法
82 Method writeMethod = propertyDescriptor.getWriteMethod();
83
84 // 向类中写入值
85 writeMethod.invoke(o, value);
86 }
87
88 results.add(o);
89 }
90
91 return results;
92
93 }
94
95 private Class<?> getClassType(String parameterType) throws ClassNotFoundException {
96 if (parameterType != null) {
97 Class<?> aClass = Class.forName(parameterType);
98 return aClass;
99 }
100
101 return null;
102 }
103
104 @Override
105 public void close() throws SQLException {
106 connection.close();
107 }
108
109 private BoundSql getBoundSql(String sql) {
110 // 标记处理类:主要是配合通用标记解析器GenericTokenParser类完成对配置文件等的解析工作,其中TokenHandler主要完成对占位符的解析工作
111 ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
112 // GenericTokenParser:通用的标记解析器,完成了代码片段中的占位符的解析,然后再根据给定的标记处理器(TokenHandler)来进行表达式的处理
113 // GenericTokenParser类 构造函数三个参数分别为openToken(开始标记)、closeToken(结束标记)、handler(标记处理器)
114 GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
115 String parse = genericTokenParser.parse(sql);
116
117 List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
118
119 BoundSql boundSql = new BoundSql(parse, parameterMappings);
120
121 return boundSql;
122 }
123
124 }

测试自定义持久层框架

编写测试类

新建一个IPersistence_test项目,在IPersistence_test项目的pom.xml文件中引入相关依赖(注意:这里需要引入自定义好的持久层框架IPersistence,因此需要先将IPersistence进行maven install 打包):

 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>com.hardy</groupId>
8 <artifactId>IPersistence_test</artifactId>
9 <version>1.0-SNAPSHOT</version>
10
11 <properties>
12 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
13 <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
14 <java.version>1.8</java.version>
15 <maven.compiler.source>1.8</maven.compiler.source>
16 <maven.compiler.target>1.8</maven.compiler.target>
17 </properties>
18
19 <!-- 引入自定义持久层框架的依赖 -->
20 <dependencies>
21 <dependency>
22 <groupId>com.hardy</groupId>
23 <artifactId>IPersistence</artifactId>
24 <version>1.0-SNAPSHOT</version>
25 </dependency>
26 </dependencies>
27
28 </project>

编写测试类:

 1 package com.hardy.test;
2
3 import com.hardy.io.Resources;
4 import com.hardy.pojo.User;
5 import com.hardy.sqlSession.SqlSession;
6 import com.hardy.sqlSession.SqlSessionFactory;
7 import com.hardy.sqlSession.SqlSessionFactoryBuilder;
8 import org.junit.Test;
9
10 import java.io.InputStream;
11 import java.util.List;
12
13 public class IPersistenceTest {
14
15 @Test
16 public void test() throws Exception {
17 InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
18 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().builder(resourceAsStream);
19 SqlSession sqlSession = sqlSessionFactory.openSession();
20
21 System.out.println("自定义持久层框架,GO!");
22 // 调用
23 User user = new User();
24 user.setId(1);
25 user.setUsername("hardy");
26 User user2 = sqlSession.selectOne("User.selectOne", user);
27 System.out.println("查询单条记录:"+ user2);
28
29 System.out.println("-----------------------------------------");
30
31 List<User> users = sqlSession.selectList("User.selectList");
32 System.out.println("查询多条记录:");
33 for (User user1 : users) {
34 System.out.println(user1);
35 }
36
37 }
38 }

执行测试类,结果如下所示:

Mybatis学习之自定义持久层框架(六) 自定义持久层框架:完善CRUD方法并进行测试

Mybatis学习之自定义持久层框架(六) 自定义持久层框架:完善CRUD方法并进行测试