java.lang.RuntimeException: org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.builder.BuilderException: The expression 'coll' evaluated to a null value. org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.builder.BuilderException: The expression 'coll' evaluated to a null value.
时间: 2023-07-17 07:00:46 浏览: 577
这个异常是由 MyBatis-Spring 框架抛出的。它表明在 MyBatis 的映射文件中,存在一个表达式 'coll' 的值为 null。个表达式可能是在映射文件中某个 SQL 语句或参数中使用的要解决这个问题,你需要确定在哪个映射文件中出现了这个问题,并检查相关的 SQL 语句或参数。确保你正确地设置了对应的值,以避免出现 null 值。
另外,你还可以通过打印日志或调试来追踪这个异常的具体位置,以便更好地定位和解决问题。
相关问题
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: java.lang.RuntimeException: 无法自动获取数据库类型,请通过 dialect 参数指定! ### Cause: java.lang.RuntimeException: 无法自动获取
### 解决方案
当 MyBatis 遇到 `MyBatisSystemException` 或 `PersistenceException` 并提示无法自动获取数据库类型时,通常是因为框架未能正确识别所连接的数据库类型。以下是可能的原因及其解决方案:
#### 原因分析
1. **未显式指定 dialect 参数**
如果使用的分页插件(如 PageHelper),需要手动设置对应的方言(dialect)。如果未配置该参数,则可能导致框架尝试自动检测失败[^2]。
2. **驱动版本不匹配**
数据库驱动版本与实际数据库版本不符也可能引发此类问题。例如,MySQL 连接器版本过低或过高都可能引起兼容性问题。
3. **SQL 片段定义错误**
使用 `<sql>` 和 `<include>` 定义 SQL 片段时,若语法有误或者片段未被正确定位,可能会间接导致数据库类型的判断失误[^1]。
---
#### 解决方法
##### 方法一:显式指定 dialect 参数
在应用的配置文件中明确指定目标数据库的方言。对于 MySQL 数据库,可以在 `application.yml` 或 `application.properties` 文件中添加如下配置:
```yaml
pagehelper:
helperDialect: mysql
```
此操作可确保分页插件能够针对特定数据库执行优化后的 SQL 查询逻辑。
##### 方法二:升级/降级 JDBC 驱动
确认当前项目依赖的 JDBC 驱动是否适配于正在运行的目标数据库版本。以 Maven 构建为例,更新 POM.xml 中的相关依赖项至最新稳定版:
```xml
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
```
注意调整具体版本号以满足生产环境需求。
##### 方法三:验证 XML 映射文件中的语法规则
检查 Mapper 接口关联的 XML 文件是否存在潜在问题,尤其是涉及动态 SQL 的部分。例如:
```xml
<select id="selectUsers" resultType="User">
SELECT * FROM users WHERE status = #{status}
<if test="name != null">AND name LIKE CONCAT('%',#{name},'%')</if>
</select>
```
上述示例展示了如何利用条件分支构建灵活查询语句的同时保持良好的可读性和维护成本较低的特点。
另外,在复杂场景下推荐采用 CDATA 节点包裹自由形式的内容来规避特殊字符带来的解析困扰:
```xml
<insert id="insertBlogPost" parameterType="map">
<![CDATA[
INSERT INTO BLOG (ID, TITLE, AUTHOR_ID)
VALUES (#{id}, #{title}, #{authorId})
]]>
</insert>
```
---
### 总结
通过以上三种途径——即合理设定辅助工具所需的额外属性、同步基础组件间的协作关系以及细致排查核心资源描述文档内的细微偏差——基本可以有效应对由未知数据源特性触发的一系列连锁反应。
025-04-01 11:29:39.500 TRACE [org.springframework.web.servlet.DispatcherServlet] - GET "/big-screen/get-time-period-traffic?deviceType=3", parameters={masked}, headers={masked} in DispatcherServlet 'dispatcherServlet' 2025-04-01 11:29:39.502 TRACE [o.s.w.s.m.m.a.RequestMappingHandlerMapping] - Mapped to com.syzn.dv.biz.bigscreen.northbound.controller.BigScreenController#getTimePeriodTraffic(BigScreenCommonReq) 2025-04-01 11:29:39.527 TRACE [org.springframework.web.method.HandlerMethod] - Arguments: [BigScreenCommonReq(direction=null, deviceType=3, startDate=null, endDate=null)] 2025-04-01 11:29:39.539 DEBUG [org.mybatis.spring.SqlSessionUtils] - Creating a new SqlSession 2025-04-01 11:29:39.544 DEBUG [org.mybatis.spring.SqlSessionUtils] - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7caa55e8] was not registered for synchronization because synchronization is not active 2025-04-01 11:29:39.565 DEBUG [org.mybatis.spring.SqlSessionUtils] - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7caa55e8] 2025-04-01 11:29:39.567 DEBUG [o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver] - Using @ExceptionHandler com.syzn.dv.config.GlobalExceptionHandler#customErrorHandler(RuntimeException) 2025-04-01 11:29:39.568 TRACE [org.springframework.web.method.HandlerMethod] - Arguments: [org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.builder.BuilderException: Error evaluating expression 'null != deviceNo and deviceNo.size() > 0'. Cause: org.apache.ibatis.ognl.MethodFailedException: Method "size" failed for object false [java.lang.NoSuchMethodException: java.lang.String.size()]] org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.builder.BuilderException: Error evaluating expression 'null != deviceNo and deviceNo.size() > 0'. Cause: org.apache.ibatis.ognl.MethodFailedException: Method "size" failed for object false [java.lang.NoSuchMethodException: java.lang.String.si
<think>嗯,用户遇到了MyBatis中的BuilderException异常,具体是在使用OGNL表达式'null != deviceNo and deviceNo.size() > 0'时出现的。异常信息显示MethodFailedException,提示无法访问java.util.Collections$SingletonList的size方法,因为权限问题。我需要分析这个问题产生的原因,并找到解决办法。
首先,用户提到在并发环境下这个异常会重现。可能跟SingletonList有关,因为SingletonList是Collections中的一个私有静态类,返回的列表是不可变的,且可能在某些情况下导致反射访问的问题。MyBatis的OGNL在尝试调用size()方法时,可能因为SingletonList的size方法是public的,但由于类本身的访问权限限制,导致反射调用失败。
接下来,用户的表达式是检查deviceNo不为null且size大于0。但deviceNo可能是一个字符串,此时调用size()方法会出错,因为字符串没有size()方法,而是length()或length属性。或者,当deviceNo是一个单元素列表(如使用Collections.singletonList()创建)时,由于SingletonList的实现,OGNL在反射调用size方法时可能遇到访问权限问题,尤其是在Java版本或安全管理器限制的情况下。
可能的解决方案有几个方向:
1. 修改OGNL表达式,避免直接调用size()方法。比如,对于集合,可以直接判断是否为空,或者使用size属性(如deviceNo.size > 0)。但如果是字符串,可能需要检查长度,使用deviceNo.length()。
2. 检查deviceNo变量的实际类型。如果deviceNo应该是集合类型,确保它不会被误赋值为字符串或其他类型。如果是字符串,需要调整表达式。
3. 处理可能的并发问题,比如检查是否为SingletonList实例,改用线程安全的集合类型,或者避免在多线程环境下共享该变量。
另外,用户提供的引用[1]、[2]、[3]都提到了SingletonList的size方法访问异常,说明问题确实出现在使用SingletonList的情况下。可能当deviceNo是单个元素时,MyBatis将其包装为SingletonList,而该类的size方法虽然是public,但类本身是非public的,导致OGNL反射调用失败。
因此,建议用户将表达式改为更安全的形式。例如,对于集合类型,使用empty操作符,如deviceNo != null and !deviceNo.isEmpty()。如果是字符串,则检查长度。或者,如果deviceNo可能为字符串,使用getter方法,如deviceNo.length()。
同时,需要确保deviceNo在传入MyBatis之前已经正确处理,避免为null或类型错误的情况。如果有必要,可以在Java代码中进行前置检查,而不是依赖OGNL表达式。
总结,解决方案步骤可能是:
1. 修改表达式,使用OGNL的empty或size属性。
2. 确认deviceNo的实际类型,调整表达式对应的方法调用。
3. 替换可能产生SingletonList的代码,使用普通ArrayList等集合类型。</think>### 解决MyBatis中`deviceNo.size()`导致的BuilderException异常
#### 问题分析
异常由OGNL表达式`deviceNo.size()`触发,具体原因是:
1. **类型不匹配**:`deviceNo`可能实际为**字符串类型**,字符串没有`size()`方法,应使用`length()`或`length`属性。
2. **SingletonList反射限制**:当`deviceNo`为单元素集合(如`Collections.singletonList()`生成)时,`SingletonList`的`size()`方法因反射权限问题无法被OGNL调用[^1][^2][^3]。
3. **并发环境问题**:在并发操作中,集合类型可能被动态代理或特殊实现类替代,导致方法调用失败。
---
#### 解决方案
##### 1. 修改OGNL表达式(优先推荐)
根据`deviceNo`的实际类型调整表达式:
- **集合类型**:改用`deviceNo != null and deviceNo.size > 0`(OGNL支持属性语法)或`deviceNo != null and !deviceNo.isEmpty()`。
- **字符串类型**:改用`deviceNo != null and deviceNo.length() > 0`。
```xml
<!-- 修改前 -->
<if test="null != deviceNo and deviceNo.size() > 0">
<!-- 修改后(集合类型) -->
<if test="deviceNo != null and deviceNo.size > 0">
<!-- 或(字符串类型) -->
<if test="deviceNo != null and deviceNo.length() > 0">
```
##### 2. 确保类型一致性
检查`deviceNo`的Java类型:
- 如果是字符串,确保值不为空且符合预期。
- 如果是集合,避免使用`Collections.singletonList()`,改用`new ArrayList()`等标准实现。
##### 3. 防御性编程
在Java代码中提前处理空值,减少OGNL复杂度:
```java
// 在Mapper接口中处理默认值
public void query(@Param("deviceNo") List<String> deviceNo) {
if (deviceNo == null) {
deviceNo = Collections.emptyList(); // 确保始终为非null集合
}
}
```
---
#### 根本原因说明
- **OGNL反射限制**:`SingletonList`是`java.util.Collections`的私有内部类,其`size()`方法虽为public,但类本身不可被外部访问,导致反射调用失败。
- **并发环境放大问题**:线程间共享的集合可能被包装为特殊类型(如代理类),加剧反射调用失败概率。
---
#### 验证步骤
1. 添加日志输出`deviceNo`的实际类型:
```xml
<if test="deviceNo != null">
<choose>
<when test="deviceNo.getClass().name.contains('String')">
<!-- 处理字符串逻辑 -->
</when>
<when test="deviceNo instanceof java.util.Collection">
<!-- 处理集合逻辑 -->
</when>
</choose>
</if>
```
2. 单元测试中模拟`Collections.singletonList("test")`验证修复效果。
---
#### 附加建议
- **统一集合类型**:强制要求`deviceNo`使用`List`接口的实现类(如`ArrayList`),避免不可控的集合类型。
- **升级MyBatis版本**:部分旧版本OGNL存在兼容性问题,升级至3.5.6+可缓解反射限制。
---
阅读全文
相关推荐














