接口中我们定义openSession()方法,用于生产SqlSession
package com.dxh.sqlSession; public interface SqlSessionFacetory { public SqlSession openSession(); } package com.dxh.sqlSession; import com.dxh.pojo.Configuration; public class DefaultSqlSessionFactory implements SqlSessionFacetory{ private Configuration configuration; public DefaultSqlSessionFactory(Configuration configuration) { this.configuration = configuration; } @Override public SqlSession openSession() { return new DefaultSqlSession(configuration); } }同样我们在DefaultSqlSessionFactory中传入Configuration,Configuration需要我们一直往下传递
5.创建SqlSession接口以及它的实现类在接口中,我定义两个方法:
因为参数类型和个数我们都不知道,所以我们使用泛型,同时,传入statementId(namespace、. 、id 组成)
package com.dxh.sqlSession; import java.util.List; public interface SqlSession { //查询多条 public <E> List<E> selectList(String statementId,Object... params) throws Exception; //根据条件查询单个 public <T> T selectOne(String statementId,Object... params) throws Exception; } package com.dxh.sqlSession; import com.dxh.pojo.Configuration; import java.util.List; public class DefaultSqlSession implements SqlSession { private Configuration configuration; public DefaultSqlSession(Configuration configuration) { this.configuration = configuration; } @Override public <E> List<E> selectList(String statementId, Object... params) throws Exception { //将要完成对simpleExecutor里的query方法调用 SimpleExecutor simpleExecutor = new SimpleExecutor(); List<Object> list = simpleExecutor.query(configuration, configuration.getMappedStatementMap().get(statementId), params); return (List<E>) list; } @Override public <T> T selectOne(String statementId, Object... params) throws Exception { List<Object> objects = selectList(statementId, params); if (objects.size()==1){ return (T) objects.get(0); }else{ throw new RuntimeException("查询结果为空或者返回结果过多"); } } }这里selectOne方法和selectList方法的参数结构都是一样的,所以我们可以通过selectList.get(0)的方式得到一个返回结果。而selectList中则是重点,我们需要创建一个对象SimpleExecutor并在其中执行SQL
6.创建Executor接口及实现类SimpleExecutor实现类 package com.dxh.sqlSession; import com.dxh.pojo.Configuration; import com.dxh.pojo.MappedStatement; import java.sql.SQLException; import java.util.List; public interface Executor { /** * * @param configuration 数据库配置信息 * @param mappedStatement SQL配置信息 * @param params 可变参 * @return */ public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement,Object... params) throws SQLException, Exception; } package com.dxh.sqlSession; import com.dxh.config.BoundSql; import com.dxh.pojo.Configuration; import com.dxh.pojo.MappedStatement; import com.dxh.utils.GenericTokenParser; import com.dxh.utils.ParameterMapping; import com.dxh.utils.ParameterMappingTokenHandler; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.sql.*; import java.util.ArrayList; import java.util.List; /** * @author https://github.com/CoderXiaohui * @Description * @Date 2020-11-07 22:27 */ public class SimpleExecutor implements Executor{ /** * 就是在执行JDBC的代码 */ @Override public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception { //1. 注册驱动,获取链接 Connection connection = configuration.getDataSource().getConnection(); //2. 获取SQL语句 //假设获取的SQL是 : select * from user where id = #{id} and username = #{userName} JDBC是无法识别的, // 所以要转换sql : select * from user where id = ? and username = ? ,转换过程中还需要对#{}中的值进行解析存储 String sql = mappedStatement.getSql(); BoundSql boundSql = getBoundSql(sql); //3. 获取预处理对象:preparedStatement PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText()); //4. 设置参数 //获取到参数的全路径 String paramterType = mappedStatement.getParamterType(); Class<?> paramterTypeClass = getClassType(paramterType); List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList(); for (int i = 0; i < parameterMappingList.size(); i++) { ParameterMapping parameterMapping = parameterMappingList.get(i); String content = parameterMapping.getContent(); //反射 Field declaredField = paramterTypeClass.getDeclaredField(content); //暴力访问,防止它是私有的 declaredField.setAccessible(true); Object o = declaredField.get(params[0]); //下标从1开始 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(); //metaData.getColumnCount() :查询结果的总列数 for (int i = 1; i <= metaData.getColumnCount(); i++) { //字段名 String columnName = metaData.getColumnName(i); //字段的值 Object value = resultSet.getObject(columnName); //使用反射或者内省,根据数据库表和实体的对应关系,完成封装 //PropertyDescriptor 内省库中的一个类,就是把resultTypeClass中的columnName属性来生产读写方法 PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass); Method writeMethod = propertyDescriptor.getWriteMethod(); //把具体的值封装到o这个对象中 writeMethod.invoke(o,value); } objects.add(o); } return (List<E>) objects; } private Class<?> getClassType(String paramterType) throws ClassNotFoundException { if (paramterType!=null){ Class<?> aClass = Class.forName(paramterType); return aClass; } return null; } /** * 完成对#{}解析工作: * 1. 将#{}使用?进行替换 * 2. 解析出#{}里面的值进行存储 * @param sql * @return */ private BoundSql getBoundSql(String sql) { //标记处理类:配置标记解析器来完成对占位符的处理工作 ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler(); GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler); //返回解析后的sql String parseSql = genericTokenParser.parse(sql); //#{}里面解析出来的参数名称 List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings(); BoundSql boundSql = new BoundSql(parseSql,parameterMappings); return boundSql; } } package com.dxh.config; import com.dxh.utils.ParameterMapping; import java.util.ArrayList; import java.util.List; /** * 该方法的作用下面讲解 */ public class BoundSql { private String sqlText;//解析后的sql private List<ParameterMapping> parameterMappingList = new ArrayList<>(); public BoundSql(String sqlText, List<ParameterMapping> parameterMappingList) { this.sqlText = sqlText; this.parameterMappingList = parameterMappingList; } }这里的实现大致可分为6部分:
注册驱动,获取链接:通过传入的configuration得到datasource,然后调用getConnection()得到链接