java Mysql SQL查询DATEDIFF在模式为MYSQL的H2中失败

fslejnso  于 2023-08-01  发布在  Java
关注(0)|答案(2)|浏览(92)

***背景:***在我的一个项目中,我正在使用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语句语法错误

  • 原因:* 即使在MySQL模式下运行H2,它也使用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语句:

  • 任何其他方法,我可以尝试使这项工作?*
ffscu2ro

ffscu2ro1#

有一个解决方法,使用 * 反射 * 解决此问题。访问H2函数Map并从那里删除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());
    }
}

字符串
然后在schema中

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";

p4tfgftt

p4tfgftt2#

Asela38的回答对我很有用。但是h2包的一些东西在我的环境(springboot)中应该改变。
非常感谢asela38和Leo Lin:)
1.从pom.xml中删除运行时范围

<dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope> <-- remove
        </dependency>

字符串

  1. h2软件包目录已更改
import org.h2.expression.function.Function; <--- change

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

public class H2Function {
    public static Long dateDiff(Date date1, Date date2) {
        if (Objects.isNull(date1) || Objects.isNull(date2)) {
            return null;
        }
        return ChronoUnit.DAYS.between(date1.toLocalDate(), date2.toLocalDate());
    }

    @SuppressWarnings("rawtypes")
    public static int removeDateDiff() {
        try {
            Field field = Function.class.getDeclaredField("FUNCTIONS_BY_NAME");
            field.setAccessible(true);
            ((Map)field.get(null)).remove("DATEDIFF");
        } catch (Exception e) {
            throw new RuntimeException("failed to remove DATEDIFF");
        }
        return 0;
    }
}

相关问题