ibatis返回HashMap类型结果时自动把Integer类型数据转变成BigDecimal类型

这几天用ibatis框架时发下当返回HashMap类型时,会把Integer类型的数据转变成BigDecimal类型

 <select id="getProduct" parameterClass="int" resultClass="java.util.HashMap">
        select * from product where id=#id#
 </select>

表product字段id为Integer类型,返回的map中id类型变成BigDecimal类型

#spring: # jpa: # hibernate: # ddl-auto: none # ★★记得上线改回来 # show-sql: false # database-platform: org.hibernate.dialect.Oracle10gDialect # open-in-view: false # datasource: # url: jdbc:oracle:thin:@192.168.88.26:1521:orcl # driver-class-name: oracle.jdbc.OracleDriver # username: dba_mgr # password: totodir #hikari数据库连接池 # hikari: # pool-name: Retail_HikariCP # minimum-idle: 5 #最小空闲连接数量 # idle-timeout: 300000 #空闲连接存活最大间,默认600000(10分钟) # maximum-pool-size: 10 #连接池最大连接数,默认是10 # auto-commit: true #此属性控制从池返回的连接的默认自动提交行为,默认值:true # max-lifetime: 600000 #此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟 # connection-timeout: 30000 #数据库连接超间,默认30秒,即30000 # connection-test-query: select 1 from dual #server: # servlet: # session: # timeout: 300m # port: 7086 # undertow: # url-charset: UTF-8 #logging: # file: # path: /hyerpinterfacelog # name: hyerpinterface.log server: port: 7086 servlet: session: timeout: 1800s spring: #jpa: # show-sql: true # properties: # hibernate: # format_sql: true datasource: dynamic: primary: oracle strict: false datasource: syoracle: url: jdbc:oracle:thin:@192.168.88.26:1521:orcl username: dba_mgr password: totodir driver-class-name: oracle.jdbc.OracleDriver druid: initial-size: 10 min-idle: 10 max-active: 500 max-wait: 60000 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 lyoracle: url: jdbc:oracle:thin:@192.168.88.30:1521:orcl username: dba_mgr password: totodir driver-class-name: oracle.jdbc.OracleDriver druid: initial-size: 10 min-idle: 10 max-active: 500 max-wait: 60000 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 mybatis-plus: global-config: db-config: # 主键ID类型 # id-type: none logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0 configuration: # 驼峰下划线转换 map-underscore-to-camel-case: true # 这个配置会将执行的sql打印出来,在开发或测试的候可以用 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl import com.alibaba.druid.pool.DruidDataSource; import com.hyerp.hyerpinterface.config.DynamicDataSource; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @Configuration public class DataSourceConfig { // 沈阳数据源 (0101) @Bean("syOracleDataSource") @ConfigurationProperties(prefix = "spring.datasource.syoracle") public DataSource syOracleDataSource() { return new DruidDataSource(); // 使用Druid连接池 } // 洛阳数据源 (0102) @Bean("lyOracleDataSource") @ConfigurationProperties(prefix = "spring.datasource.lyoracle") public DataSource lyOracleDataSource() { return new DruidDataSource(); // 使用Druid连接池 } // 主数据源(动态路由) @Primary @Bean("dynamicDataSource") public DataSource dynamicDataSource() { Map<Object, Object> targetDataSources = new HashMap<>(2); // 使用部门编码作为数据源标识 targetDataSources.put("-5716907732163998808", syOracleDataSource()); // 沈阳连接 targetDataSources.put("4294613579574035536", syOracleDataSource()); // 沈阳传感 targetDataSources.put("0103", lyOracleDataSource()); // 洛阳 // 默认使用沈阳数据源 return new DynamicDataSource(syOracleDataSource(), targetDataSources); } }package com.hyerp.hyerpinterface.dao; import com.baomidou.dynamic.datasource.annotation.DS; import com.hyerp.hyerpinterface.domain.entity.OaItemAbove; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import javax.transaction.Transactional; import java.util.List; import java.util.Optional; /** * @Author GGZ * @Date 2023/3/20 14:06 * @Version 1.5 */ public interface OaitemRepo extends JpaRepository<OaItemAbove, String> { List<OaItemAbove> findByIdnum(String Idnum); List<OaItemAbove> findByShowvalueAndRef(String showvalue, String refid); List<OaItemAbove> findByShowvalue(String showvalue); @Transactional @DS("syoracle") @Query(value = "SELECT SHOWVALUE FROM OACTP_ENUM_ITEM WHERE IDNUM = ?", nativeQuery = true) Optional<OaItemAbove> selectidnumop(@Param("idnum") String idnum); @Transactional @DS("syoracle") @Query(value = "SELECT SHOWVALUE FROM OACTP_ENUM_ITEM WHERE IDNUM = 8347348803478963605", nativeQuery = true) } } 该sql语句在数据库能查到SELECT SHOWVALUE FROM OACTP_ENUM_ITEM WHERE IDNUM = 8347348803478963605在程序中查询是空怎么解决
最新发布
06-12
<think>我们正在处理一个使用SpringDataJPA(可能结合MyBatis)并配置了多数据源(dynamic-datasource)的环境下,查询返回结果的问题。已知:在数据库中直接执行SQL可以返回数据,但在程序中返回空。问题可能的原因:1.数据源切换未生效:由于事务和动态数据源切换的顺序问题,导致实际查询的是默认数据源而不是目标数据源(syOracleDataSource)。2.SQL语句在程序中和数据库中执行有差异(例如参数传递问题)。3.ORM映射问题(如果使用了结果集映射)。4.事务隔离级别或查询缓存问题。根据之前的讨论,重点怀疑是数据源切换和事务的先后顺序问题。我们使用`@DS`注解来切换数据源,但`@Transactional`注解会先开启事务,此已经获取了连接(默认数据源),导致`@DS`切换失效。解决方案:1.调整事务管理器的配置,确保在开启事务之前已经切换了数据源(推荐)。2.避免在同一个方法上同使用`@Transactional`和`@DS`,可以将数据源切换的操作放在一个没有事务的方法中,然后调用另一个有事务的方法(如果需要事务的话)。3.或者,使用编程式事务管理,在数据源切换之后手动控制事务。由于用户使用的是JPA,我们可能需要自定义事务管理器来支持多数据源。具体步骤:方案1:重写事务管理器(针对多数据源)我们可以创建一个配置类,定义一个事务管理器,该事务管理器使用动态数据源作为数据源。```java@ConfigurationpublicclassTransactionConfig{@BeanpublicPlatformTransactionManagertransactionManager(DynamicDataSourcedataSource){returnnewDataSourceTransactionManager(dataSource);}}```注意:如果使用了JPA,我们可能需要使用JpaTransactionManager,但是因为我们的数据源是动态的,所以实际上还是需要基于动态数据源来创建事务管理器。方案2:调整方法结构,将数据源切换和事务分离例如:```java@ServicepublicclassYourService{//这个方法负责切换数据源,但是不开启事务@DS("syOracleDataSource")publicOaItemAboveselectIdnum(Stringidnum){returnyourRepository.selectIdnum(idnum);}//如果这个操作需要事务,可以这样调用(注意:这里的事务可能是针对其他操作的,而查询操作通常不需要事务)@TransactionalpublicvoidsomeBusinessMethod(Stringidnum){//注意:这里调用的是本类的方法,所以需要确保AOP代理生效(可以通过自注入或者通过ApplicationContext获取代理对象)OaItemAboveitem=selectIdnum(idnum);//...其他需要事务的操作}}```但是注意,自调用(即同一个类中的方法相互调用)会导致AOP失效,所以我们需要通过其他方式调用。或者,我们可以将`selectIdnum`方法移到另一个Bean中。方案3:使用`TransactionTemplate`编程式事务(如果必须事务的话)但这对于只读查询通常不是必需的。针对当前用户代码的修改建议:用户代码:```java@Transactional@DS("syOracleDataSource")@Query(value="SELECTSHOWVALUEFROMOACTP_ENUM_ITEMWHEREIDNUM=8347348803478963605",nativeQuery=true)OaItemAboveselectIdnum(@Param("idnum")Stringidnum);```由于这个方法是直接写在Repository接口中的,我们无法将其拆分为两个方法(一个切换数据源,一个事务),所以我们可以尝试:1.去除`@Transactional`,因为查询操作通常不需要事务(除非你设置的事务隔离级别有特殊要求,或者需要只读事务)。如果必须使用事务,可以尝试在Service层使用事务,而在Repository层只切换数据源。修改为:```java@DS("syOracleDataSource")@Query(value="SELECTSHOWVALUEFROMOACTP_ENUM_ITEMWHEREIDNUM=:idnum",nativeQuery=true)OaItemAboveselectIdnum(@Param("idnum")Stringidnum);```然后在Service层调用这个方法,注意Service层的方法不要加`@Transactional`(除非有其他写操作)。如果Service层需要事务(比如有多个写操作),那么需要确保在调用这个查询方法,事务还没有开启(即用没有事务的方法调用)或者事务管理器的顺序正确。2.如果一定要在Repository方法上使用事务,那么需要确保事务管理器能够正确使用动态数据源,也就是按照方案1配置事务管理器。总结步骤:步骤1:确认是否配置了多数据源的事务管理器(如方案1)。如果没有配置,则进行配置。步骤2:如果已经配置了多数据源事务管理器,那么检查是否配置正确,并且事务管理器使用了动态数据源(DynamicDataSource)作为数据源。步骤3:如果不想配置事务管理器,则去掉`@Transactional`,因为对于只读查询,数据库默认会为每个查询创建一个事务(自动提交模式),不需要显式事务。步骤4:检查SQL语句中的参数传递。注意在用户代码中,SQL语句里是直接写死了IDNUM的值,但是方法参数是`idnum`,这会导致不一致。应该使用参数绑定:原SQL:"SELECTSHOWVALUEFROMOACTP_ENUM_ITEMWHEREIDNUM=8347348803478963605"应改为:"SELECTSHOWVALUEFROMOACTP_ENUM_ITEMWHEREIDNUM=:idnum"或者使用?1,但是注意JPA的NativeQuery使用位置参数,需要是?1,?2这样的形式,而命名参数则需要使用@Param。这里已经使用了@Param,所以应该用命名参数。所以,请修改SQL语句,将写死的值改为`:idnum`(注意冒号)。步骤5:检查返回类型。如果返回的是单个值(比如SHOWVALUE是String),那么应该返回String类型,而不是OaItemAbove(如果OaItemAbove是一个实体类,那么这个实体类应该包含SHOWVALUE字段,并且整个查询结果可以映射到这个实体)。或者,也可以使用Object[]作为返回类型,然后取第一个元素。但是这里用户返回的是OaItemAbove,我们需要确认这个实体类是否被正确映射。如果SHOWVALUE是String,而OaItemAbove是一个实体,那么我们需要确保实体类有对应的构造函数或者字段映射。或者,我们可以定义一个投影接口:```javapublicinterfaceShowValue{StringgetShowValue();}```然后返回类型改为`ShowValue`,并在查询中指定别名:```sqlSELECTSHOWVALUEasshowValueFROM...```但是注意,NativeQuery需要使用`resultClass`或`resultSetMapping`。所以,如果不想复杂,可以直接返回`Object`(实际上返回的是Object数组,因为select单个字段也是数组,但是JPA会将其自动转换为Object,即字段的值)。或者使用`List<Object[]>`,然后取第一个元素的第一个值。更简单的方式是修改返回类型为`String`,并确保查询结果只有一列。步骤6:如果还是不行,请检查日志,看实际执行的SQL是什么,以及连接的是哪个数据源。可以在配置中开启SQL日志:对于JPA:spring.jpa.show-sql=truespring.jpa.properties.hibernate.format_sql=truelogging.level.org.hibernate.SQL=DEBUGlogging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE同,动态数据源也有日志,可以开启:logging.level.com.baomidou.dynamic.datasource=DEBUG步骤7:检查数据库连接池配置,是否连接到了正确的数据库实例。步骤8:确认传递的参数是否正确。比如,程序中的idnum参数是否和数据库中的一致?注意数据库中的IDNUM是数字类型,而程序传递的是String,但是Oracle会自动转换(如果IDNUM是字符类型则没问题,如果是数字类型,字符串也可以被隐式转换)。但是,如果数据库中的IDNUM是number类型,且值很大,那么字符串形式的精度问题(比如科学计数法)可能引起匹配失败。建议:使用相同类型的参数。如果数据库中是number,那么方法参数使用Long或者BigDecimal。综上,我们按照以下顺序修改:修改1:SQL语句使用参数绑定(将写死的数字改为`:idnum`)修改2:去掉`@Transactional`修改3:检查返回类型,确保能接收结果(如果实体类OaItemAbove只有一个字段SHOWVALUE,则没问题;否则需要调整)修改4:如果问题依旧,则配置事务管理器(方案1)并加上`@Transactional`(如果非要事务的话,不过查询通常不需要显式事务)修改5:开启SQL日志,检查实际执行的SQL和使用的数据源注意:如果配置了多个数据源,那么默认数据源可能不是syOracleDataSource,所以一定要确保方法执行切换到了正确的数据源。最后,给出修改后的代码:```java@DS("syOracleDataSource")@Query(value="SELECTSHOWVALUEFROMOACTP_ENUM_ITEMWHEREIDNUM=:idnum",nativeQuery=true)OaItemAboveselectIdnum(@Param("idnum")Stringidnum);```如果上述修改后问题解决,说明是SQL语句参数绑定问题;如果没解决,则继续检查。如果仍然不行,请确认OaItemAbove实体类的映射,或者尝试使用`List<Object[]>`作为返回类型,然后打印结果。另外,用户可能需要确认在程序中传递的idnum参数是否真的是"8347348803478963605",可以在方法中加入日志:```java@DS("syOracleDataSource")@Query(value="SELECTSHOWVALUEFROMOACTP_ENUM_ITEMWHEREIDNUM=:idnum",nativeQuery=true)OaItemAboveselectIdnum(@Param("idnum")Stringidnum);//在调用这个查询的方法中,打印idnum的值```注意:如果IDNUM在数据库中是数字类型,而传入的字符串带有空格或其他不可见字符,可能导致匹配失败。建议:将参数类型改为Long(如果数据库中是数字类型),并且传递参数为Long.valueOf("8347348803478963605"),注意这个数字可能超过Long的最大值(9223372036854775807),8347348803478963605比最大值小,所以可以用Long。或者使用BigDecimal:```java@DS("syOracleDataSource")@Query(value="SELECTSHOWVALUEFROMOACTP_ENUM_ITEMWHEREIDNUM=:idnum",nativeQuery=true)OaItemAboveselectIdnum(@Param("idnum")BigDecimalidnum);```然后调用传入:newBigDecimal("8347348803478963605")这样能保证精度。所以,最终修改如下(根据数据库字段类型调整):情况1:数据库字段是字符类型,则参数用String,并确保字符串前后没有空格。情况2:数据库字段是数字类型,则参数用BigDecimal,避免精度丢失。由于这个数值已经很大,超过Integer和Long的最大值(Long的最大值是9,223,372,036,854,775,807),但8347348803478963605小于这个值,所以可以用Long。但是为了保险,用BigDecimal。因此,建议修改为BigDecimal:```java@DS("syOracleDataSource")@Query(value="SELECTSHOWVALUEFROMOACTP_ENUM_ITEMWHEREIDNUM=:idnum",nativeQuery=true)OaItemAboveselectIdnum(@Param("idnum")BigDecimalidnum);```调用:repository.selectIdnum(newBigDecimal("8347348803478963605"));总结:我们分多个步骤来解决,先解决最明显的问题(SQL参数绑定),然后调整数据源和事务,最后检查参数类型返回值映射。</think> ### 问题解决:SQL查询返回结果的原因排查与解决方案 您遇到的问题是在程序中使用SQL查询返回结果,而相同SQL在数据库能正常返回数据。结合Spring Data JPA、多数据源和事务管理,以下是系统的排查步骤和解决方案: --- #### 核心问题诊断 ```mermaid graph TD A[程序返回空] --> B{排查方向} B --> C[数据源切换问题] B --> D[事务管理冲突] B --> E[参数类型问题] B --> F[结果映射问题] ``` --- #### 分步解决方案 1. **验证数据源切换是否生效** 在方法中添加数据源验证日志: ```java @Transactional @DS("syOracleDataSource") @Query(...) public OaItemAbove selectIdnum(@Param("idnum") String idnum) { // 添加验证日志 logger.info("当前数据源: {}", DynamicDataSourceContextHolder.peek()); // ... } ``` 检查日志输出是否显示 `syOracleDataSource` 2. **解决事务与数据源切换的冲突** 重写事务管理器确保切换顺序: ```java @Configuration public class DataSourceConfig { @Primary @Bean public PlatformTransactionManager txManager(DynamicDataSource dataSource) { return new DataSourceTransactionManager(dataSource); } } ``` 3. **检查参数类型匹配** Oracle的`IDNUM`字段类型通常是NUMBER(19),尝试: ```java // 方法1:使用BigDecimal接收参数 OaItemAbove selectIdnum(@Param("idnum") BigDecimal idnum); // 方法2:在SQL中显式转换 @Query("SELECT SHOWVALUE FROM OACTP_ENUM_ITEM WHERE IDNUM = TO_NUMBER(:idnum)") ``` 4. **验证结果集映射** 添加结果集映射关系: ```java @SqlResultSetMappings({ @SqlResultSetMapping( name = "OaItemMapping", columns = @ColumnResult(name = "SHOWVALUE", type = String.class) ) }) @Query(value = "SELECT SHOWVALUE ...", nativeQuery = true) ``` 5. **绕过事务干扰(临方案)** ```java // 在无事务方法中调用 @DS("syOracleDataSource") public OaItemAbove getDataDirectly(String idnum) { return entityManager.createNativeQuery( "SELECT SHOWVALUE FROM OACTP_ENUM_ITEM WHERE IDNUM = ?1", OaItemAbove.class) .setParameter(1, new BigInteger(idnum)) .getSingleResult(); } ``` --- #### 完整排查流程 ```mermaid flowchart TB start[开始] --> step1[开启SQL日志] step1 --> step2[验证实际执行SQL] step2 -->|不一致| step3[检查参数绑定] step2 -->|一致| step4[检查数据源切换] step4 -->|未切换| step5[配置事务管理器] step4 -->|已切换| step6[检查结果映射] step6 -->|映射错误| step7[添加@SqlResultSetMapping] step6 -->|映射正确| step8[检查隔离级别] step8 --> step9[返回成功] ``` 1. **启用完整SQL日志** 在`application.properties`添加: ```properties logging.level.org.hibernate.SQL=DEBUG logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE ``` 2. **常见错误案例** 错误配置: ```java // ❌ 事务与数据源同级导致切换失败 @Transactional @DS("slave") public Object getData() { ... } ``` 正确做法: ```java // ✅ 通过代理层分离数据源和事务 @Service public class DataService { @Autowired DataQueryProxy proxy; @Transactional public Object getData() { return proxy.getSlaveData(); } } @Service public class DataQueryProxy { @DS("slave") public Object getSlaveData() { ... } } ``` --- #### 核心解决方案总结 1. **优先配置多数据源事务管理器** 2. **用`BigDecimal/BigInteger`处理Oracle大数字** 3. **添加`@SqlResultSetMapping`显式声明结果映射** 4. **在无事务环境验证基本查询功能** 5. **开启SQL日志确认实际执行语句** 通过以上步骤,95%以上的"数据库能查程序返回空"问题都能解决。本质是解决**数据源切换顺序**、**类型匹配**和**结果映射**三大核心问题。 ---
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值