第10讲 --iBATIS查询条件为多个参数

 

当查询记录的时候,过滤记录的条件为多个的时候你就不能使用 parameterClass=int 等了,这个参数只能使用一次。

 

有两种解决方案:

1.使用对象构造查询参数

 

步骤:

a.改写student.xml

 

 

<select id="selectStudentByIdAndName" resultClass="Student" parameterClass="Student">
    select * from student where  sid=#sid# and sname=#sname#
</select>

 

b.写实现

 

	public Student queryStudentsByNameAndId(Student student)
	{
		Student s=null;
		try {
			s=(Student)sqlMapClient.queryForObject("selectStudentByIdAndName",student);
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return s;
	}

 

c.测试

		StudentDAO studentDAO=new StudentDAOImpl();
		Student student=new Student();
		student.setSid(326);
		student.setSname("胡晓亮3");
		Student s=studentDAO.queryStudentsByNameAndId(student);
		log.info("score: "+s.getBirth());

 

d.结果

172  [main] INFO  cn.com.xinli.ibatis.dao.impl.StudentDAOImpl(160) - score: 2009-06-17 

 

 

 

2.使用map封装查询参数

 

a.改写student.xml,map 的定义一定要在sql的前面,第一次我做连写的时候就吧map的定义放在了后面,结果老是报错,说找不到map的定义,还就得就是 占位符一定要使用 ? 号

<parameterMap class="java.util.HashMap" id="parameterMap">
	<parameter property="sid"/>
	<parameter property="sname"/>
</parameterMap>

<select id="selectStudentByIdAndName" resultClass="Student" parameterMap="parameterMap">
    select * from student where  sid=? and sname=?
</select>

 

b.实现

public Student queryStudentsByNameAndId(HashMap<String,String> hashMap)
	{
		Student s=null;
		try {
			s=(Student)sqlMapClient.queryForObject("selectStudentByIdAndName",hashMap);
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return s;
	}

 

c.测试

 

		StudentDAO studentDAO=new StudentDAOImpl();
		HashMap m=new HashMap();
		
		m.put("sname", "胡晓亮3");
		m.put("sid", 12434);
		
		Student s=studentDAO.queryStudentsByNameAndId(m);
		log.info("score: "+s.getBirth());

 

d.结果

 

172  [main] INFO  cn.com.xinli.ibatis.dao.impl.StudentDAOImpl(160) - score: 2009-06-17

 

 

<think>我们正在解决同一个SQL在数据库直接查询正常,但在MyBatis-Flex框架中执行很慢的问题。根据之前的讨论,可能的原因包括框架配置、事务管理、实体类标注等。现在我们需要更全面地分析原因并提供解决方案。 可能的原因及解决方案: 1. **SQL生成差异**:虽然看起来是"同一条SQL",但MyBatis-Flex在构建查询时可能附加了额外的条件(如逻辑删除、多租户等)导致实际执行的SQL不同。需要检查最终执行的SQL是否与预期一致。 - 解决方案:开启MyBatis的SQL日志,确认实际执行的SQL语句。可以在配置文件中设置: ```properties logging.level.com.your.mapper.package=DEBUG ``` 或使用p6spy等工具捕获真实SQL。 2. **结果集映射开销**:MyBatis-Flex自动生成的结果映射(ResultMap)可能非常复杂,尤其是包含嵌套查询(关联查询)时,容易引起N+1查询问题。 - 解决方案: - 避免使用嵌套查询,改用JOIN查询一次获取数据。 - 使用`QueryWrapper`的`select`方法明确指定需要的列,减少不必要的数据传输和对象属性映射。 - 对于复杂对象,考虑使用DTO投影,只映射必要的字段。 3. **事务管理**:如果查询被包含在一个大事务中,可能会因为事务隔离级别或锁等待导致性能下降。 - 解决方案:将查询方法标记为`@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)`,这样它会在没有事务的环境中执行,或者加入现有事务但不会开启新事务。同时,确保事务范围尽可能小。 4. **MyBatis-Flex的增强功能带来的开销**:例如,多租户、逻辑删除、字段加密等功能会在运行时动态修改SQL,增加处理时间。 - 解决方案:检查是否启用了这些功能,如果不需要可以关闭。如果必须使用,确保这些功能的实现是高效的(比如逻辑删除字段有索引)。 5. **分页查询优化**:如果慢查询发生在分页时,特别是大偏移量的分页,MyBatis-Flex的分页查询可能会先执行完整的查询然后再截取部分结果,导致性能低下。 - 解决方案:使用优化后的分页方式,比如基于游标的分页(利用主键或索引列)或避免大偏移量分页。例如,使用`where id > ? limit ?`代替`limit ?,?`。 6. **连接池配置**:连接池的配置不当(如最大连接数过小)可能导致获取数据库连接的时间过长。 - 解决方案:检查连接池(如HikariCP)的配置,适当调整连接池参数。 7. **MyBatis缓存问题**:如果启用了二级缓存,但缓存策略不当(如缓存了大量数据),可能导致查询变慢。 - 解决方案:检查缓存配置,必要时关闭二级缓存进行测试。 8. **框架版本问题**:可能是MyBatis-Flex版本存在性能问题,尝试升级到最新版本。 9. **数据库驱动问题**:数据库驱动版本不兼容或存在bug,尝试更换驱动版本。 10. **其他环境因素**:如网络延迟(应用服务器与数据库服务器之间的网络)、应用服务器负载等。 为了准确定位问题,建议按以下步骤诊断: 1. 使用SQL日志确认实际执行的SQL语句,并在数据库中手动执行该语句,观察执行时间。 2. 如果手动执行也慢,则说明问题在数据库(如索引缺失),需要优化SQL或数据库。 3. 如果手动执行快,但通过框架执行慢,则问题在应用层。此时可以: - 使用性能分析工具(如Arthas)跟踪方法调用时间,确定是SQL执行慢还是结果映射慢。 - 暂时关闭MyBatis-Flex的增强功能(如多租户、逻辑删除)进行测试。 - 简化查询(如只查询单个字段)测试,逐步定位问题。 示例:开启SQL日志后,如果发现实际执行的SQL包含了额外的条件,可以通过调整Wrapper来避免: ```java // 假设原查询 QueryWrapper query = QueryWrapper.create() .select().from(ACCOUNT).where(ACCOUNT.ID.eq(1)); // 如果发现自动加上了逻辑删除条件,而你不希望有,可以全局或局部关闭逻辑删除 QueryWrapper query = QueryWrapper.create() .select().from(ACCOUNT).where(ACCOUNT.ID.eq(1)) .and(ACCOUNT.DELETED.eq(0)); // 这是自动加的 // 若需要关闭逻辑删除(不推荐,除非确定不需要),可以在实体类上去除@TableLogic注解 ``` 另外,如果问题出现在分页查询,可以尝试优化分页: ```java // 原始分页(可能慢) Page<User> page = new Page<>(1000, 10); // 第1000页,每页10条 QueryWrapper query = QueryWrapper.create().select().from(USER).orderBy(USER.ID.desc()); Page<User> result = userMapper.paginate(page, query); // 优化:使用基于游标的分页(假设主键为ID) QueryWrapper query = QueryWrapper.create() .select().from(USER) .where(USER.ID.lt(lastSeenId)) // 上一页最后一条记录的ID .orderBy(USER.ID.desc()) .limit(10); ``` 最后,如果问题无法自行解决,建议在MyBatis-Flex的GitHub仓库提交issue,附上复现步骤和SQL日志。 通过以上步骤,通常可以定位并解决MyBatis-Flex查询慢的问题。 相关问题: 1. 如何开启MyBatis的SQL日志? 2. 如何优化MyBatis-Flex的分页查询性能? 3. MyBatis-Flex中如何关闭多租户或逻辑删除功能? </think>### 同一条SQL在数据库查询正常,但在MyBatis-Flex框架中查询慢的原因及解决方案 #### 一、核心原因分析 1. **自动结果映射开销过大** MyBatis-Flex自动生成的`<resultMap>`可能包含大量字段映射和关联对象(如一对多映射),即使SQL查询部分字段,框架仍会处理完整的实体映射关系,导致内存和CPU开销剧增[^1]。 2. **动态SQL处理延迟** 框架在运行时动态添加逻辑(如多租户过滤、逻辑删除等)可能引入额外处理步骤。例如多租户场景下,每次查询都需通过`TenantFactory`获取租户ID,若涉及远程调用或复杂计算,会造成显著延迟[^4]。 3. **事务管理不当** 过大的事务范围会导致数据库锁持有时间延长,特别是当查询被包含在`@Transactional`声明的事务中时,可能阻塞其他操作[^2]。 4. **分页机制差异** 框架的分页查询(如`paginate()`)可能生成**优化不足的count查询**,或使用内存分页而非数据库分页,导致全表扫描: ```sql /* 框架可能生成的低效分页 */ SELECT COUNT(*) FROM large_table; -- 全表扫描 SELECT * FROM large_table LIMIT 100000, 10; -- 大偏移量查询 ``` 5. **连接池与框架协作问题** 连接池配置(如最大等待时间)与框架的事务管理可能产生冲突,导致获取连接超时或意外等待。 --- #### 二、解决方案 ##### 1. 优化结果映射 ```java // 仅选择必要字段,避免自动全字段映射 QueryWrapper query = QueryWrapper.create() .select(USER.ID, USER.NAME) // 明确指定字段 .from(USER.as("u")) .where(USER.AGE.ge(18)); List<Map<String, Object>> result = userMapper.selectMaps(query); ``` ##### 2. 消除多租户/逻辑删除开销 ```java // 临时关闭特定查询的增强功能 QueryWrapper query = QueryWrapper.create() .select().from(USER) .disableLogicDelete() // 关闭逻辑删除过滤 .ignoreTenants(); // 忽略多租户条件 List<User> users = userMapper.selectListByQuery(query); ``` ##### 3. 事务范围精细化 ```java // 将查询移出事务范围 public List<User> getUsersNonTransactional() { // 无事务注解 return userMapper.selectAll(); } @Transactional public void updateUser(User user) { // 仅写操作使用事务 userMapper.update(user); } ``` ##### 4. 分页性能优化 ```java // 使用优化的分页方式(基于游标) String lastId = "prev_max_id"; // 上一页最后一条ID QueryWrapper query = QueryWrapper.create() .select().from(ORDER) .where(ORDER.ID.gt(lastId)) .orderBy(ORDER.ID.asc()) .limit(10); // 替代传统分页 List<Order> orders = orderMapper.selectListByQuery(query); ``` ##### 5. 连接池调优配置 ```properties # application.yml (HikariCP示例) spring: datasource: hikari: maximum-pool-size: 20 connection-timeout: 3000 max-lifetime: 1800000 idle-timeout: 60000 ``` --- #### 三、诊断工具 1. **SQL日志对比** 启用框架SQL日志,确认实际执行语句是否与预期一致: ```properties mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl ``` 2. **性能分析工具** - 使用Arthas监控方法耗时:`trace com.your.mapper.* '*' - 用JDBC拦截器(如p6spy)捕获真实SQL执行时间 3. **框架特性检测** 检查是否误启用全局配置: ```java @Configuration public class MyBatisFlexConfig { @Bean public GlobalConfig globalConfig() { GlobalConfig config = new GlobalConfig(); config.setLogicDelete(false); // 全局关闭逻辑删除 return config; } } ``` --- #### 四、总结流程 ```mermaid graph TD A[发现查询慢] --> B{日志对比} B -->|SQL相同| C[检查结果映射] B -->|SQL不同| D[检查动态SQL逻辑] C --> E[缩减查询字段] D --> F[关闭多租户/逻辑删除] E --> G[验证性能] F --> G G -->|仍慢| H[分析事务范围] H --> I[移出事务边界] I -->|解决| J[优化完成] G -->|解决| J ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值