使用 MyBatis、H2 和 Oracle 程序进行集成测试

Posted

技术标签:

【中文标题】使用 MyBatis、H2 和 Oracle 程序进行集成测试【英文标题】:Integration testing with MyBatis, H2 and Oracle procedures 【发布时间】:2021-03-19 18:29:40 【问题描述】:

我有一个存储的 Oracle 过程,我这样调用:

CALL MY_PROC(
        p_arg    =>   #arg,     jdbcType=INTEGER,  mode=IN,
        p_var    =>   #var,     jdbcType=VARCHAR,  mode=IN,
        p_date   =>   #date,    jdbcType=DATE,     mode=IN
    )

我使用MyBatis 写了它,如下所示:

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.MyMapper">
    <update id="callMyProc" statementType="CALLABLE">
        CALL MY_PROC(
            p_arg    => #arg,    jdbcType=INTEGER,  mode=IN,
            p_var    => #var,    jdbcType=VARCHAR,  mode=IN,
            p_date   => #date,   jdbcType=DATE,     mode=IN
        )
    </update>
</mapper>

该过程本身做了一些FOR UPDATE,因为我必须在更新它们之前锁定行。代码完成了它的工作,但我现在想编写一些集成测试,我使用 H2 作为数据库。我知道在这种情况下,我应该编写用户定义的函数来处理这种情况。我很确定写一个例如(为了问题,简化了 sql 查询):

CREATE ALIAS IF NOT EXISTS MY_PROC AS $$
import java.net.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.UUID;
import java.time.LocalDateTime;
@CODE
void callMyProc(Connection conn, Integer p_arg, String p_var, LocalDateTime p_date) throws Exception 
    String sqlStatement = String.format("UPDATE mytable SET myvar = '%s', mydate = '%t' WHERE myarg = %d", p_var, p_date, p_arg);
    PreparedStatement ps = conn.prepareStatement(sqlStatement);
    ps.execute();

$$;

可以解决问题,但不幸的是,由于 MyBatis 格式,我遇到了语法错误:

### The error may exist in com.example/MyMapper.xml
### The error may involve com.example.MyMapper.callMyProc
### The error occurred while executing an update
### SQL: CALL MY_PROC(                 p_arg       => ?, p_var => ?, p_date => ?  )
### Cause: org.h2.jdbc.JdbcSQLSyntaxErrorException: Błąd składniowy w wyrażeniu SQL "CALL MY_PROC(
                P_ARG[*]       => ?,
                P_VAR[*]       => ?,
                P_DATE[*]       => ?
            )"
Syntax error in SQL statement "CALL MY_PROC(
                P_ARG[*]       => ?,
                P_VAR[*]       => ?,
                P_DATE[*]       => ?
CALL MY_PROC(
                p_arg       => ?,
                p_var[*]       => ?,
                p_date[*]       => ?
            ) [42000-200]

我的问题是,我应该如何为该过程编写我的 H2 别名,以便同时满足 MyBatis 和 H2 的要求?

【问题讨论】:

H2 似乎不支持p_x =&gt; ? 语法,即使是MODE=Oracle。尝试将参数引用从 p_x =&gt; #y 更改为 #y 【参考方案1】:

正如用户 ave 所建议的那样,事实证明,摆脱 Oracle 语法而只需要像这样编写 MyBatis 映射器就足够了:

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.MyMapper">
    <update id="callMyProc" statementType="CALLABLE">
        CALL MY_PROC(
            #arg,    jdbcType=INTEGER,  mode=IN,
            #var,    jdbcType=VARCHAR,  mode=IN,
            #date,   jdbcType=TIMESTAMP,     mode=IN
        )
    </update>
</mapper>

我还必须将date 类型更改为TIMESTAMP,别名应如下所示:

CREATE ALIAS IF NOT EXISTS MY_PROC AS $$
import java.net.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Date;
@CODE
void callMyProc(Connection conn, Integer p_arg, String p_var, Date p_date) throws Exception 
    String sqlStatement = String.format("UPDATE mytable SET myvar = '%s', mydate = PARSEDATETIME('%s','yyyy-MM-dd HH:mm:ss') WHERE myarg = %d", p_var, p_date.toString(), p_arg);
    PreparedStatement ps = conn.prepareStatement(sqlStatement);
    ps.execute();

$$;

匹配LocalDateTime 类型的Java(此时从LocalDateTimejava.util.Date 的映射是自动完成的)。以这种方式编写的唯一缺点是在调用存储过程时必须注意参数的顺序。

【讨论】:

以上是关于使用 MyBatis、H2 和 Oracle 程序进行集成测试的主要内容,如果未能解决你的问题,请参考以下文章

使用 MyBatis + H2 配置 Spring Boot 身份验证/登录

spring Boot 整合H2+Mybatis环境搭建

具有多个数据源 Oracle 和 H2 的 Spring Boot

H2 和 Oracle 兼容性问题

尝试从 Oracle 导入到 H2 时出现 Temenos DBImport 错误

为啥 Grails 使用 H2 而不是 Oracle?