【问题标题】:Mysql SQL query DATEDIFF failed in H2 where mode was MYSQLMysql SQL 查询 DATEDIFF 在 H2 中失败,其中模式为 MYSQL
【发布时间】:2023-03-23 06:29:01
【问题描述】:

背景:在我的一个项目中,我正在使用 JUnit 在 Spring Batch 上做 component testing。这里的应用程序数据库是 MYSQL。在 Junit 测试执行中,我让数据源在

之间切换
  • MYSQL 和
  • H2(jdbc:h2:mem:MYTESTDB;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=MYSQL)

基于配置。 使用 MYSQL 作为数据源进行调试,使用 H2 在构建服务器中单独运行测试。

一切正常,直到在应用程序逻辑中我不得不使用带有 DATEDIFF 的查询。

问题:查询失败,

org.h2.jdbc.JdbcSQLException: SQL 语句中的语法错误

原因:即使通过H2 run on MySQL mode,它也使用了 H2 函数并且这些函数是不同的

MYSQL DATEDIFF definition is DATEDIFF(expr1,expr2)
 e.g. SELECT DATEDIFF('2010-11-30 23:59:59','2010-12-31') 
      ==> 1

H2 DATEDIFF definision is DATEDIFF(unitstring, expr1, expr2)
 unitstring =  { YEAR | YY | MONTH | MM | WEEK | DAY | DD | DAY_OF_YEAR
 | DOY | HOUR | HH | MINUTE | MI | SECOND | SS | MILLISECOND | MS }
 e.g. SELECT DATEDIFF(dd, '2010-11-30 23:59:59','2010-12-31') 
      ==> 1

解决方案尝试并失败:我尝试编写自定义函数

 package com.asela.util;                                                                                                                                

 import java.lang.reflect.Field;                                                                                                                                                         
 import java.sql.Date;                                                                                                                                                                   
 import java.time.temporal.ChronoUnit;                                                                                                                                                   
 import java.util.Map;                                                                                                                                                                   
 import java.util.Objects;                                                                                                                                                               

 import org.h2.expression.Function;                                                                                                                                                      

 public class H2Function {                                                                                                                                                               
    public static long dateDifference(Date date1, Date date2) {                                                                                                                          
        Objects.nonNull(date1);                                                                                                                                                          
        Objects.nonNull(date2);                                                                                                                                                          
        return ChronoUnit.DAYS.between(date1.toLocalDate(), date2.toLocalDate());                                                                                                        
    }                                                                                                                                                                                    
 } 

并用 H2 设置它

DROP ALIAS IF EXISTS DATEDIFF;
CREATE ALIAS DATEDIFF FOR "com.asela.util.H2Function.dateDifference";

以上无法替换现有的 DATEDIFF 仍然失败

org.h2.jdbc.JdbcSQLException:函数别名“DATEDIFF”已经存在; SQL语句:

我可以尝试其他任何方法来完成这项工作吗?

【问题讨论】:

    标签: java mysql testing h2


    【解决方案1】:

    使用 Reflection 解决了这个问题。访问 H2 函数映射并从那里删除 DATEDIFF。然后添加替换功能。

    package com.asela.util;
    
    import java.lang.reflect.Field;
    import java.sql.Date;
    import java.time.temporal.ChronoUnit;
    import java.util.Map;
    import java.util.Objects;
    
    import org.h2.expression.Function;
    
    public class H2Function {
    
        @SuppressWarnings("rawtypes")
        public static int removeDateDifference() {
            try {
                  Field field = Function.class.getDeclaredField("FUNCTIONS");
                  field.setAccessible(true);
                  ((Map)field.get(null)).remove("DATEDIFF");
            } catch (Exception e) {
                throw new RuntimeException("failed to remove date-difference");
            }
            return 0;
        }
    
        public static long dateDifference(Date date1, Date date2) {
            Objects.nonNull(date1);
            Objects.nonNull(date2);
            return ChronoUnit.DAYS.between(date1.toLocalDate(), date2.toLocalDate());
        }
    }
    

    然后在架构中

    CREATE ALIAS IF NOT EXISTS REMOVE_DATE_DIFF FOR "com.asela.util.H2Function.removeDateDifference";
    CALL REMOVE_DATE_DIFF();
    DROP ALIAS IF EXISTS DATEDIFF;
    CREATE ALIAS DATEDIFF FOR "com.asela.util.H2Function.dateDifference";
    

    【讨论】:

    • 对于新的 h2 版本(对我来说是 1.4.200)Function.class.getDeclaredField("FUNCTIONS"); 应该替换为 Function.class.getDeclaredField("FUNCTIONS_BY_NAME");
    猜你喜欢
    • 2011-12-04
    • 2013-11-20
    • 1970-01-01
    • 2014-01-10
    • 1970-01-01
    • 2020-05-30
    • 1970-01-01
    • 2013-03-21
    • 2021-01-21
    相关资源
    最近更新 更多