Mybatis原理:结果集封装详解

​ 经过sql参数解析、sql动态组装和执行sql,相对而言,结果集的封装,是mybatis数据处理的最后一环。这里只对查询结果而言,因为更新语句一般都是返回影响的行数。抛开mybatis,如果让我们组装结果,我们该如何进行呢?mybatis的查询结果统一表示为:

List<E>

即使是查询单个对象,它的查询结果还是封装成 List 对象,然后返回list集合的第一个元素。

个人根据mybatis的源码,将mybatis对结果集的封装,分成两步:

(1)通过反射,创建结果对象,其所有属性为默认值,例如,如果结果是实体对象,那么将通过无参构造函数创建对象,其所有属性一般为空,如果结果是List,则会创建一个空的List

(2)为结果对象的属性赋值,这里也是通过反射,找到set方法赋值

下面开始进入主题:

一、数据准备

  1. 查询sql以及自定义映射的resultMap:
     <resultMap id="userMap" type="User"><id property="id" column="id"></id><result property="username" column="username"></result><result property="password" column="password"></result><result property="isValid" column="is_valid"></result><collection property="blogs" javaType="java.util.List" ofType="Blog"><id property="id" column="blog_id"></id><result property="title" column="title"></result><result property="userId" column="user_id"></result></collection></resultMap><select id="getUserAndBlogByUserId" parameterType="string" resultMap="userMap">select u.id,u.username,u.password,u.is_valid,b.id as blog_id,b.title,b.user_idfrom t_user u LEFT JOIN t_blog b ON u.id = b.user_idwhere u.id=#{id}</select>

从查询sql,你也可以发现一二,比如博客表t_blog中,含有一个逻辑外键user_id,表示该博客属于哪个用户的,而每个用户可以拥有多个博客,显然是一对多的关系,而查询条件则为用户id。

2.实体类

public class User implements Serializable{private String id;private String username;private String password;private Integer isValid;//一个用户,对应多篇博客private List<Blog> blogs;
}
public class Blog implements Serializable{private String id;private String title;private String userId;
}

3.mapper接口

public interface UserMapper {//根据id查询用户及其所有博客User getUserAndBlogByUserId(String id);
}

4.测试方法

public class One2ManyQuery {public static void main(String[] args) throws IOException {//读取配置信息InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");//根据配置信息,创建SqlSession工厂SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);//SqlSession工厂创建SqlSessionSqlSession sqlSession = factory.openSession();//获取接口的代理对象UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = mapper.getUserAndBlogByUserId("123");System.out.println(user);}
}

二、结果对象的创建

​ 本次程序的起点是PreparedStatementHandler的方法,它是查询的结束,同时也是结果封装的开始入口:

  @Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();return resultSetHandler.handleResultSets(ps);}

​ 由此,进入到 resultSetHandler.handleResultSets(ps) 方法,而默认会进入到的DefaultResultSetHandler的handleResultSets方法:

  @Overridepublic List<Object> handleResultSets(Statement stmt) throws SQLException {ErrorContext.instance().activity("handling results").object(mappedStatement.getId());// 要返回的结果final List<Object> multipleResults = new ArrayList<>();// 迭代变量,结果集的个数int resultSetCount = 0;// 获取第一个结果集,并包装成ResultSetWrapper对象,// ResultSetWrapper对象含有已映射和未映射的列名和属性的对应关系ResultSetWrapper rsw = getFirstResultSet(stmt);// 获取所有的ResultMapList<ResultMap> resultMaps = mappedStatement.getResultMaps();// ResultMap的个数int resultMapCount = resultMaps.size();// 校验:如果结果集有数据,但是没有定义返回的结果类型,就会报错validateResultMapsCount(rsw, resultMapCount);while (rsw != null && resultMapCount > resultSetCount) {// 依次获取ResultMapResultMap resultMap = resultMaps.get(resultSetCount);// 处理结果集,这里是重点handleResultSet(rsw, resultMap, multipleResults, null);// 获取下一个结果集rsw = getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount++;}String[] resultSets = mappedStatement.getResultSets();if (resultSets != null) {while (rsw != null && resultSetCount < resultSets.length) {ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);if (parentMapping != null) {String nestedResultMapId = parentMapping.getNestedResultMapId();ResultMap resultMap = configuration.getResultMap(nestedResultMapId);handleResultSet(rsw, resultMap, null, parentMapping);}rsw = getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount++;}}return collapseSingleResultList(multipleResults);}

那么,我们重点关注一下这句,因为它才是真正的处理结果集:

      // 处理结果集,这里是重点handleResultSet(rsw, resultMap, multipleResults, null);
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {try {if (parentMapping != null) {handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);} else {if (resultHandler == null) {// 如果结果处理器为空,则使用默认的结果处理器,没有自定义的情况下,都是走这个流程DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);// 处理每一行的值handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);// 将处理结果放到list集中multipleResults.add(defaultResultHandler.getResultList());} else {handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);}}} finally {// issue #228 (close resultsets)closeResultSet(rsw.getResultSet());}}

根据上面的代码,我们关注这一句代码是如何处理的:

          // 处理每一行的值handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);

查看详细的 handleRowValues 方法:

  public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {// 如果有嵌套的ResultMapif (resultMap.hasNestedResultMaps()) {ensureNoRowBounds();checkResultHandler();// 处理含有嵌套ResultMap的结果handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);} else {// 处理不含有嵌套ResultMap的结果handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);}}

由于我们使用了 collection 标签做一对多的映射,所以是属于嵌套的resultMap查询,个人理解,即使是一个实体对象,它也是一个resultMap,只不过它的resultType是实体对象罢了,所以走的嵌套分支:

  // 处理含有嵌套ResultMap的结果
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);

这个方法比较长:

 private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {final DefaultResultContext<Object> resultContext = new DefaultResultContext<>();ResultSet resultSet = rsw.getResultSet();// 跳过已处理的行skipRows(resultSet, rowBounds);Object rowValue = previousRowValue;while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);Object partialObject = nestedResultObjects.get(rowKey);// issue #577 && #542if (mappedStatement.isResultOrdered()) {if (partialObject == null && rowValue != null) {nestedResultObjects.clear();storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);}rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);} else {// 看这行代码,获取行值rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);if (partialObject == null) {storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);}}}if (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds)) {storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);previousRowValue = null;} else if (rowValue != null) {previousRowValue = rowValue;}}

我们找重点看吧:

        // 看这行代码,获取行值rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);

这其中 getRowValue 方法,我认为是比较重要的方法,处理逻辑大部分都在这里:

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix, Object partialObject) throws SQLException {// resultMap的唯一标志idfinal String resultMapId = resultMap.getId();// 返回值Object rowValue = partialObject;if (rowValue != null) {final MetaObject metaObject = configuration.newMetaObject(rowValue);putAncestor(rowValue, resultMapId);applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);ancestorObjects.remove(resultMapId);} else {final ResultLoaderMap lazyLoader = new ResultLoaderMap();// 创建结果对象rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);// 行值不为空,并且结果对象有类型处理器if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {// 将行值包装成元数据对象MetaObjectfinal MetaObject metaObject = configuration.newMetaObject(rowValue);boolean foundValues = this.useConstructorMappings;// 是否使用自动映射,因为我们自定义了resultMap,所以不会走这个if (shouldApplyAutomaticMappings(resultMap, true)) {foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;}// 给对象的属性赋值foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;putAncestor(rowValue, resultMapId);// 给嵌套的结果属性赋值foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;ancestorObjects.remove(resultMapId);foundValues = lazyLoader.size() > 0 || foundValues;rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;}if (combinedKey != CacheKey.NULL_CACHE_KEY) {nestedResultObjects.put(combinedKey, rowValue);}}return rowValue;}

结果对象的创建,就在这里:

      // 创建结果对象rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);

详细如下:

 private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {this.useConstructorMappings = false; // reset previous mapping resultfinal List<Class<?>> constructorArgTypes = new ArrayList<>();final List<Object> constructorArgs = new ArrayList<>();Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();for (ResultMapping propertyMapping : propertyMappings) {// issue gcode #109 && issue #149if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);break;}}}this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping resultreturn resultObject;}

你会发现这句,最终也是通过反射创建对象的:

resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);

三、属性赋值

​ 回到上一步的 DefaultResultSetHandler的getRowValue 方法

​ 通过代码发现,这里的逻辑关系,大概是这样的,sql中的列名 ->通过映射关系,拿到对应的属性名 ->通过属性名,获取set的方法名,最后,通过反射设置值

1.普通属性赋值

        // 给对象的属性赋值
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
  private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)throws SQLException {// 已经映射的列名集合final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);boolean foundValues = false;// 所有属性和列的映射规则final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();for (ResultMapping propertyMapping : propertyMappings) {// 列名String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);if (propertyMapping.getNestedResultMapId() != null) {// the user added a column attribute to a nested result map, ignore itcolumn = null;}if (propertyMapping.isCompositeResult()|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))|| propertyMapping.getResultSet() != null) {// 根据列名,获取属性值Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);// issue #541 make property optional// 属性名final String property = propertyMapping.getProperty();if (property == null) {continue;} else if (value == DEFERRED) {foundValues = true;continue;}if (value != null) {foundValues = true;}if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {// gcode issue #377, call setter on nulls (value is not 'found')// 给对象的属性设置值metaObject.setValue(property, value);}}}return foundValues;}

这里也就是分3步:

(1)根据列名获取值,在resultSet中,根据列名获取值,这个是很容易拿到的

(2)获取属性名,在我们定义的映射规则中就包含了属性名,这个也可以拿到

(3)给对象的属性设置值,这里还是通过反射技术,调用相应的set方法来设值

2.集合对象的属性的设置

回到 DefaultResultSetHandler的getRowValue 方法

        // 给嵌套的结果属性赋值
foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
private boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String parentPrefix, CacheKey parentRowKey, boolean newObject) {boolean foundValues = false;for (ResultMapping resultMapping : resultMap.getPropertyResultMappings()) {final String nestedResultMapId = resultMapping.getNestedResultMapId();if (nestedResultMapId != null && resultMapping.getResultSet() == null) {try {final String columnPrefix = getColumnPrefix(parentPrefix, resultMapping);final ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);if (resultMapping.getColumnPrefix() == null) {// try to fill circular reference only when columnPrefix// is not specified for the nested result map (issue #215)Object ancestorObject = ancestorObjects.get(nestedResultMapId);if (ancestorObject != null) {if (newObject) {linkObjects(metaObject, resultMapping, ancestorObject); // issue #385}continue;}}final CacheKey rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);Object rowValue = nestedResultObjects.get(combinedKey);boolean knownValue = rowValue != null;instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject); // mandatoryif (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw)) {rowValue = getRowValue(rsw, nestedResultMap, combinedKey, columnPrefix, rowValue);if (rowValue != null && !knownValue) {linkObjects(metaObject, resultMapping, rowValue);foundValues = true;}}} catch (SQLException e) {throw new ExecutorException("Error getting nested result map values for '" + resultMapping.getProperty() + "'.  Cause: " + e, e);}}}return foundValues;}

我们着重看这句:

// 实例化集合属性
instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);

这一步,便会处理集合属性:

private Object instantiateCollectionPropertyIfAppropriate(ResultMapping resultMapping, MetaObject metaObject) {final String propertyName = resultMapping.getProperty();Object propertyValue = metaObject.getValue(propertyName);if (propertyValue == null) {Class<?> type = resultMapping.getJavaType();if (type == null) {type = metaObject.getSetterType(propertyName);}try {//如果是集合if (objectFactory.isCollection(type)) {// 创建一个空集合propertyValue = objectFactory.create(type);// 设置集合属性metaObject.setValue(propertyName, propertyValue);// 返回集合属性的引用return propertyValue;}} catch (Exception e) {throw new ExecutorException("Error instantiating collection property for result '" + resultMapping.getProperty() + "'.  Cause: " + e, e);}} else if (objectFactory.isCollection(propertyValue.getClass())) {return propertyValue;}return null;}

那么list集合里面的元素又是在什么时候放进去的呢?这是在DefaultResultSetHandler的applyNestedResultMappings中拿到

行值rowValue后,才进行添加的。

DefaultResultSetHandler的applyNestedResultMappings的部分代码:

          boolean knownValue = rowValue != null;instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject); // mandatoryif (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw)) {rowValue = getRowValue(rsw, nestedResultMap, combinedKey, columnPrefix, rowValue);if (rowValue != null && !knownValue) {linkObjects(metaObject, resultMapping, rowValue);foundValues = true;}}
linkObjects方法:
  private void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Object rowValue) {final Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);if (collectionProperty != null) {final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);// 集合添加元素targetMetaObject.add(rowValue);} else {metaObject.setValue(resultMapping.getProperty(), rowValue);}}

这样就完成了属性的赋值

控制台的l输出,可以参考一下:

img

MyBatis 是怎么把查询出来的结果集映射到实体类(JavaBean)上的,原理解释

MyBatis 是怎么把查询出来的结果集映射到实体类(JavaBean)上的,原理解释:

相信有很多开发者对MyBatis并不陌生,但谈起MyBatis底层到底是怎么工作的,没个两三年的工程师并非能完全掌握到手,读完以下这篇文章,相信你很快能理解MyBatis是怎么通过JDBC的封装,得到结果集并将结果集映射到JavaBean实体上的一个过程,在这里就不唠叨套多,先来了解下原理。

Mybatis3源码分析(16)-Sql解析执行-结果集映射(ResultSetHandler)
MyBatis查询结果集映射到JavaBean原理浅谈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/555284.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python内置函数面向对象_Pyhton——面向对象进阶二:类的内置函数补充、描述符...

Pyhton——面向对象进阶二&#xff1a;一、类的内置函数补充1、isinstance(obj,cls)——检查obj是否是该类的对象class Hoo:def __init__(self,name,tem):self.name nameself.tem temclass foo(Hoo):passf1foo(e,20)print(isinstance(f1,Hoo))首先 f1 肯定是 foo 的对象&…

vue项目打包后部署到服务器(超详细步骤)

耽误了几天, 终于开始写第二篇博客了, 这篇会讲怎么将vue项目打包部署到服务器, 其实和上一篇的uni-app步骤一样的, 就是最后多了一步修改nginx配置, 好 , 上操作 一 ,打包项目 vscode下载链接&#xff1a;https://pan.baidu.com/s/1ibHt7XB6EZy37BDb1CigWw 提取码&#xff1…

postman怎么不登陆使用_最新百度云不限速,免安装、免登陆、不限速,打开网站就能使用...

上次给大家安利了一波Pandownload手机版/电脑版。那篇文章中也说了&#xff0c;这类应用使用不当可能会遇到账号被限速的情况&#xff0c;而且手机版必须登录才能进行不限速下载。总之&#xff0c;凡是没登录账号的小伙伴&#xff0c;下载过程会非常曲折。那么是否有无需登录就…

vue项目配置打包测试环境/生产环境

vue项目配置打包测试环境/生产环境&#xff1a; 开发环境运行命令&#xff1a;npm run serve 生产环境打包命令&#xff1a;npm run pro 测试环境打包命令&#xff1a;npm run build 步骤&#xff1a; 1.项目中添加一个配置ip的js文件&#xff0c;比如如下的ip-config.js&…

SpringMVC访问WEB-INF下的jsp解决方案Spring Boot集成使用jsp

SpringMVC访问WEB-INF下的jsp解决方案 一. 问题 ​将项目中用到的jsp等文件放在WEB-INF目录下。实际开发过程中&#xff0c;需要在框架页面通过iframe嵌入对应的具体页面&#xff0c;此处如果直接调用对应页面所在的url地址&#xff0c;则会提示404错误。 ​ WEB-INF目录下的…

SpringBoot | 详解SpringBoot配置文件及其原理

文章目录一、配置文件二、YAML语法1、基本语法2、值的写法(1)、字面量&#xff1a;普通的值&#xff08;数字&#xff0c;字符串&#xff0c;布尔&#xff09;(2)、对象、Map&#xff08;属性和值&#xff09;(3)、数组&#xff08;List、Set&#xff09;三、配置文件值注入1、…

【深入理解JVM】:Java内存模型JMM

多任务和高并发的内存交互 多任务和高并发是衡量一台计算机处理器的能力重要指标之一。一般衡量一个服务器性能的高低好坏&#xff0c;使用每秒事务处理数&#xff08;Transactions Per Second&#xff0c;TPS&#xff09;这个指标比较能说明问题&#xff0c;它代表着一秒内服…

You may use special comments to disable some warnings.Use // eslint-disable-next-line to ignore...

vue没写什么爆红 错误原因 ESLint] 对语法的要求过于严格导致编译的时候报上图那些错误。 要知道&#xff0c;这并不是代码有异常&#xff0c;而是代码格式有问题&#xff0c;这些错误并不会影响代码的执行结果。 解决方法 很简单&#xff0c;既然是ESLint 语法错误&#xff…

java jni 原理_JNI的实现原理

JNI接口函数和指针Java虚拟机访问本机代码通过调用JNI的功能特性。JNI的功能都可以通过一个接口指针。一个接口指针是一个指向指针的指针。这个指针指向一个一个指针数组&#xff0c;这个数组中的每一个成员指向一个函数入口。每个接口的功能是在一个预定义的内部数组的偏移量。…

jQuery ajax读取本地json文件_jQuery请求本地JSON文件,在谷歌浏览器运行时报跨域错误_Vscode使用Live Server

json文件 {"first":[{"name":"张三","sex":"男"},{"name":"李四","sex":"男"},{"name":"王武","sex":"男"},{"name":"李…

Vue打包并发布项目

一、 打包vue项目步骤&#xff1a; 1、对当前vue项目进行打包的命令如下&#xff1a; npm run build2、打包完成&#xff0c;会输出Build complete并且在vue项目中会生成一个名字为dist的打包文件。如下图&#xff1a; 二、 使用静态服务器工具包发布打包的vue项目 1、首先…

Grammarly:最优秀的日常英文写作辅助工具——论文英文校验

使用Grammarly也有一些心得&#xff0c;在此分享给大家。 1&#xff0c;Grammarly是什么&#xff1f; Grammarly是一款在线语法纠正和校对工具&#xff0c;支持Windows、Mac、iOS和Android等多个平台。它能够检查单词拼写、纠正标点符号、修正语法错误、调整语气以及给出风格…

SpringMVC @RequestBody和@ResponseBody原理解析

SpringMVC RequestBody和ResponseBody原理解析 前言 RequestBody作用是将http请求解析为对应的对象。例如&#xff1a; http请求的参数&#xff08;application/json格式&#xff09;&#xff1a; {"accountId": 10,"adGroupId": "12345678",…

java 高性能缓存_高性能Java缓存----Caffeine

简单介绍Caffeine是新出现的一个高性能的Java缓存&#xff0c;有了它完全可以代替Guava Cache&#xff0c;来实现更加高效的缓存&#xff1b;Caffeine采用了W-TinyLFU回收策略&#xff0c;集合了LRU和LFU的优点&#xff0c;提供了一个最佳的命中率&#xff0c;在效率上可以秒杀…

@ResponseBody 转化成json后与实体类字段名不一致_SpringMVC字符串解析成json对象(@RequestBody注解和@ResponseBody注解)

ResponseBody 转化成json后与实体类字段名不一致 实体类A字段名由B改成C后&#xff0c;Controller 中返回的List中字段名仍然是C 经过ResponseBody返回到前台后又变成了B 后来发现公司项目采用的是阿里的fastjson&#xff0c; 是开源的Json格式化工具库 此工具库是根据实体类…

java togglebutton_ToggleButton和Switch使用大全

本文转载自&#xff1a;Android零基础入门第21节&#xff1a;ToggleButton和Switch使用大全http://www.apkbus.com/blog-205190-68463.html(出处: 安卓巴士 - 安卓开发 - Android开发 - 安卓 - 移动互联网门户)&#xff0c;转载应备注出处&#xff0c;尊重原创上期学习了CheckB…

MyBatis之工作原理,简单实体的增加、修改、删除、查询_Mybatis-原理总结

一、MyBatis之工作原理 MyBatis是一个半自动映射框架。所谓半自动&#xff0c;是相对Hibernate全表映射而言的&#xff0c;MyBatis需要手动匹配提供POJO、SQL和映射关系。 我们知道&#xff0c;JDBC有四个核心对象&#xff1a; &#xff08;1&#xff09;DriverManager&#…

MySQL优化:如何避免回表查询?_什么是索引覆盖?

数据库表结构&#xff1a; create table user (id int primary key,name varchar(20),sex varchar(5),index(name) )engineinnodb;select id,name where nameshenjianselect id,name,sex where nameshenjian多查询了一个属性&#xff0c;为何检索过程完全不同&#xff1f; 什…

mysql提示Column count doesn‘t match value count at row 1错误

我们在对数据库进行添加信息时可能会遇到如下错误&#xff1a; Column count doesn’t match value count at row 1 该错误的意思是传入表的字段数和values值的个数不一样 我总结了一下&#xff0c;主要有3个易错点&#xff1a; 1.要传入表中的字段数和values后面的值的个数不…

java 阅发布模式_redis发布订阅模式

一 前言虽然有消息队列&#xff0c;我们还是要了解一下redis发布订阅模式哟&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;二发布订阅模式PUBLISH 命令向通道发送信息&#xff0c;此客户端称为publisher 发布者&#xff1b;SUBSCRIBE 向命令通道订阅信息&#…