北京网站关键字优化,高安做网站,太原网站建设制作公司哪家好,微信手机网站制作这里有两种方式#xff0c;一种为常用的 Spring 依赖注入 Mapper 的方式。另一种为直接使用 SqlSessionTemplate 执行 Sql 的方式。
Spring 依赖注入 Mapper 的方式
Mapper 接口注入 SpringIOC 容器
Spring 容器在扫描 BeanDefinition 阶段会扫描 Mapper 接口类#xff0c…这里有两种方式一种为常用的 Spring 依赖注入 Mapper 的方式。另一种为直接使用 SqlSessionTemplate 执行 Sql 的方式。
Spring 依赖注入 Mapper 的方式
Mapper 接口注入 SpringIOC 容器
Spring 容器在扫描 BeanDefinition 阶段会扫描 Mapper 接口类并生成这些类的 MapperFactoryBean 的工厂 bean 定义。Spring 容器在 createBean 阶段的时候会根据 BeanDefintion 创建 bean。在创建完 factoryBean 的时候会调用 factoryBean 的 getObject()方法从 DefaultSqlSession 的 knownMapper 重获取 Mapper 接口类的 mapperProxy。使用 MapperProxy 创建出代理类。
public T T getMapper(ClassT type, SqlSession sqlSession) {MapperProxyFactoryT mapperProxyFactory (MapperProxyFactory)this.knownMappers.get(type);if (mapperProxyFactory null) {throw new BindingException(Type type is not known to the MapperRegistry.);} else {try {return mapperProxyFactory.newInstance(sqlSession);} catch (Exception var5) {throw new BindingException(Error getting mapper instance. Cause: var5, var5);}}
}public T newInstance(SqlSession sqlSession) {MapperProxyT mapperProxy new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);return this.newInstance(mapperProxy);
}protected T newInstance(MapperProxyT mapperProxy) {return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}Mapper 类调用
在 Service 层或者 Controller 层通过注解引入 Bean这个时候引入的 Mapper 就是上文创建的 MapperProxy。MapperProxy 的增强逻辑首先过滤掉了 Object 类中的 toString()、equal()等方法。 如果调用的是 Object 类中的方法直接放过不代理 对于 Mapper 接口中的方法进行代理。代理前先检查 methodCache 是否缓存了该方法的 invoke 逻辑。 default 方法的逻辑 非 default 方法的逻辑比较重要。 通过 PlainMethodInvoker 这个类代理了其他接口方法代理逻辑在 MapperMethod 中。 MapperMethod 是最为核心的逻辑。MapperMethod 在执行构建方法时就会创建一个 SqlCommand 和一个 MethodSignature 方法签名。 SqlCommand 封装了从 SqlSession 中 Config 配置中获取到的 MappedStatement。 调用 execute 方法。传参为 MappedStatement 的增删改查的类型和参数 根据增删改查的类型选择不同的执行逻辑 增删改的逻辑 解析参数得到 param反射根据 mybatis 中参数注解解析sqlSession.insert(this.command.getName(), param) 或者sqlSession.update(this.command.getName(), param) 或者sqlSession.delete(this.command.getName(), param) 或者处理结果返回值 select 语句根据返回值类型不同调用不同执行逻辑 returnVoid返回值为空且有专门的结果类型处理器returnsManythis.executeForMany(sqlSession, args);returnsMapthis.executeForMap(sqlSession, args);returnsCursorthis.executeForCursor(sqlSession, args);returnsOne 返回一行sqlSession.selectOne(this.command.getName(), param); flush 刷新类型的 SQLresult sqlSession.flushStatements(); 如果调用的是 Object 类中的方法直接放过布袋里
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// 对于Object类中的方法放过不增强直接执行即可。return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);} catch (Throwable var5) {throw ExceptionUtil.unwrapThrowable(var5);}
}在 MapperProxy 中有一个缓存结构 methodCacheMapMethod, MapperMethodInvoker methodCache
增强逻辑中会先判断当前方法是否被缓存在 methodCache 中如果没有则创建一个放到缓存中。 MapUtil.computeIfAbsent(this.methodCache, method, (m)-{/*创建缓存*/});创建逻辑为
MapUtil.computeIfAbsent(this.methodCache, method, (m) - {// default方法的逻辑DefaultMethodInvokerjava8和Java9的不一样。if (m.isDefault()) {return privateLookupInMethod null ? new DefaultMethodInvoker(this.getMethodHandleJava8(method)) :new DefaultMethodInvoker(this.getMethodHandleJava9(method));} else {return new PlainMethodInvoker(new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()));}
}); default 的先不用管 DefaultMethodInvoker直接看 else 中的 PlainMethodInvoker
创建一个 MapperMethod然后 PlainMethodInvoker 在 invoke 方法中调用 MapperMethod 的方法 execute()。 在构造 MapperMethod 方法中创建了一个 SqlCommand 。SqlCommand 封装了从 SqlSession 中 Config 配置中获取到的 MappedStatement。在之后的 execute 方法中执行的就是 SqlCommand 中的 Mapped Statement。 // SqlCommand 封装了从SqlSession中Config配置中获取到的MappedStatement。
public MapperMethod(Class? mapperInterface, Method method, Configuration config) {this.command new SqlCommand(config, mapperInterface, method);this.method new MethodSignature(config, mapperInterface, method);
}private static class PlainMethodInvoker implements MapperMethodInvoker {private final MapperMethod mapperMethod;public PlainMethodInvoker(MapperMethod mapperMethod) {this.mapperMethod mapperMethod;}public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {return this.mapperMethod.execute(sqlSession, args);}
}接下来就是执行 SqlSession 中的增删改查方法了。可以先看一下 SqlCommand
public SqlCommand(Configuration configuration, Class? mapperInterface, Method method) {String methodName method.getName();Class? declaringClass method.getDeclaringClass();MappedStatement ms this.resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration);if (ms span stylefont-weight: bold; classmark null) {if (method.getAnnotation(Flush.class) /span null) {throw new BindingException(Invalid bound statement (not found): mapperInterface.getName() . methodName);}this.name null;this.type SqlCommandType.FLUSH;} else {this.name ms.getId();this.type ms.getSqlCommandType();if (this.type SqlCommandType.UNKNOWN) {throw new BindingException(Unknown execution method for: this.name);}}
}参数转化然后 excute Sql封装返回值
public Object execute(SqlSession sqlSession, Object[] args) {Object result;Object param;switch (this.command.getType()) {case INSERT:// 将args参数数组转换成方法中的注解的参数param this.method.convertArgsToSqlCommandParam(args);// 调用DefaultSqlSession的insert方法。// 处理结果返回值result this.rowCountResult(sqlSession.insert(this.command.getName(), param));break;case UPDATE:// 将args参数数组转换成方法中的注解的参数param this.method.convertArgsToSqlCommandParam(args);// 调用DefaultSqlSession的update方法。// 处理结果返回值result this.rowCountResult(sqlSession.update(this.command.getName(), param));break;case DELETE:// 将args参数数组转换成方法中的注解的参数param this.method.convertArgsToSqlCommandParam(args);// 调用DefaultSqlSession的delete方法。// 处理结果返回值result this.rowCountResult(sqlSession.delete(this.command.getName(), param));break;case SELECT:// select 语句情况较多根据返回值类型不同调用不同执行逻辑。// returnVoid返回值为空且有专门的结果类型处理器if (this.method.returnsVoid() this.method.hasResultHandler()) {this.executeWithResultHandler(sqlSession, args);result null;} else if (this.method.returnsMany()) {result this.executeForMany(sqlSession, args);} else if (this.method.returnsMap()) {result this.executeForMap(sqlSession, args);} else if (this.method.returnsCursor()) {result this.executeForCursor(sqlSession, args);} else {param this.method.convertArgsToSqlCommandParam(args);result sqlSession.selectOne(this.command.getName(), param);if (this.method.returnsOptional() (result null || !this.method.getReturnType().equals(result.getClass()))) {result Optional.ofNullable(result);}}break;case FLUSH:result sqlSession.flushStatements();break;default:throw new BindingException(Unknown execution method for: this.command.getName());}if (result null this.method.getReturnType().isPrimitive() !this.method.returnsVoid()) {throw new BindingException(Mapper method this.command.getName() attempted to return null from a method with a primitive return type ( this.method.getReturnType() ).);} else {return result;}}SqlSessionTemplate 执行 Sql
在创建 SqlSession 的时候已经创建了 Executor。默认为 Simple public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {Assert.notNull(sqlSessionFactory, Property sqlSessionFactory is required);Assert.notNull(executorType, Property executorType is required);this.sqlSessionFactory sqlSessionFactory;this.executorType executorType;this.exceptionTranslator exceptionTranslator;this.sqlSessionProxy (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionInterceptor());}在 Spring Boot 自动配置这篇文章中已经讲过
Configuration 类中有一个属性 mappedStatements。这是一个 HashMap解析过后的 MappedStatement 被添加到了 map 中
当我们的 SqlSession 在执行 sql 语句时会先从 configuration 中拿到 sql。然后执行。 private E ListE selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {List var6;try {MappedStatement ms this.configuration.getMappedStatement(statement);var6 this.executor.query(ms, this.wrapCollection(parameter), rowBounds, handler);} catch (Exception var10) {throw ExceptionFactory.wrapException(Error querying database. Cause: var10, var10);} finally {ErrorContext.instance().reset();}return var6;}然后看一下 public E ListE query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {BoundSql boundSql ms.getBoundSql(parameter);CacheKey key this.createCacheKey(ms, parameter, rowBounds, boundSql);return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);}
再往下一层就是执行 JDBC 那一套了获取链接执行得到 ResultSet解析 ResultSet 映射成 JavaBean。