JDBC 与 H2 和 MySQL 模式:创建过程失败

JDBC with H2 and MySQL mode: create procedure fails

代码说明

数据库连接

我尝试在不使用外部数据库的情况下在本地数据库中存储 Java 对象。 为此,我通过 Hibernate 将 JDBC 与 H2 一起使用:

    /**
     * @param connection the connection to set
     */
    public static void setConnectionHibernate() {
        Properties connectionProps = new Properties();
        connectionProps.put("user", "sa");
        try {
            Class.forName("org.h2.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        url = "jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MODE=MySQL;";
    }

查询

我使用以下代码将过程存储在字符串中:

    static final String CREATE_PROCEDURE_INITPSEUDOS = "CREATE OR REPLACE PROCEDURE init_pseudos (MaxPseudo INT) BEGIN WHILE MaxPseudo >= 0 DO"
            +
            " INSERT INTO Pseudos (indexPseudo)" +
            " VALUES (MaxPseudo);" +
            " SET MaxPseudo = MaxPseudo - 1;" +
            " END WHILE;" +
            " END init_pseudos;";

查询执行

然后我用这段代码执行语句:

    public static void initBaseDonneePseudos() {
        try (Connection connection = DriverManager.getConnection(url, connectionProps);
                Statement stmt = connection.createStatement()) {
            stmt.execute(RequetesSQL.CREATE_TABLE_PSEUDOS);
            stmt.execute(RequetesSQL.CREATE_PROCEDURE_INITPSEUDOS);
            stmt.execute(RequetesSQL.CREATE_FUNCTION_RECUPEREPSEUDO);
            stmt.execute(RequetesSQL.INIT_TABLE_PSEUDOS);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

问题

测试

我执行这个测试来测试语句:

    @Nested
    class BaseDonneeInteractionTest {

        @BeforeEach
        public void setUp() {
            BaseDonnee.setConnectionHibernate();
        }

        @Test
        void testInitBaseDonnee() {
            assertDoesNotThrow(() -> BaseDonnee.initBaseDonneePseudos());
        }

    }

错误

但是我得到这个错误

我没有找到查询的问题,有人有办法解决这个问题吗?

问题是在 H2 中没有您尝试定义的明确过程或函数。

为此,H2 允许您改为创建使用过的定义函数。请考虑合适的簧片 documentation.

基本上,您通过为一堆 Java 代码声明 ALIAS 来创建用户定义的函数。

例如,在您的用例中,您的 CREATE_PROCEDURE_INITPSEUDOS 可能看起来类似于:

CREATE ALIAS INIT_PSEUDOS AS  $$
import java.sql.Connection;
import java.sql.Statement;
import java.sql.SQLException;
@CODE
void init_pseudos(final Connection conn, final int maxPseudo) throws SQLException {
  try (Statement stmt = conn.createStatement()) {
    while (maxPseudo >= 0) do {
      stmt.execute("INSERT INTO Pseudos (indexPseudo)  VALUES (MaxPseudo);");
      maxPseudo = maxPseudo - 1;
    }
  }
}
$$;

注意以下几点:

  • 正如我所说,您将用户定义的函数定义为 Java 代码。 Java 代码应包含在两个 $$ 分隔符之间。
  • 虽然我明确包含了一些导入,但您可以在代码中的 java.utiljava.sql 包中使用任何 class。如果你想明确地包含一些导入,或者如果你需要来自其他包的 classes 而不是提到的,则应在第一个 $$ 标记之后立即提供相应的导入。此外,您需要在导入结束和实际 Java 方法开始处包含 @CODE 信号 H2。
  • 如果您需要在代码中引用 Connection 数据库,它应该是方法的第一个参数。
  • 更喜欢提出而不是隐藏异常:它将允许您的事务作为一个整体适当地提交或回滚。

您可以照常调用这样的函数:

CALL INIT_PSEUDOS (5);

请为 maxPseudo 参数提供适当的值。

请将提供的代码视为一个使用示例:您可以通过不同的方式改进代码,例如使用 PreparedStatements 而不是 Statements 以提高效率,检查参数可空性等。

“MySQL 兼容模式”不会使 H2 与 MySQL 100% 兼容。它只是改变了一些事情。 documentation 列出了它们:

  • 允许使用 INDEX(..) 或 KEY(..) 在 CREATE TABLE 语句中创建索引。例子:create table test(id int primary key, name varchar(255), key idx_name(name));
  • 将浮点数转换为整数时,小数位不会被截断,但会四舍五入。
  • INSERT 语句支持 ON DUPLICATE KEY UPDATE,由于此功能,VALUES 在某些上下文中具有特殊的非标准含义。
  • INSERT IGNORE 得到部分支持,如果未指定 ON DUPLICATE KEY UPDATE,可用于跳过具有重复键的行。
  • 部分支持 REPLACE INTO。
  • 从 CHAR 值的右侧删除空格。
  • REGEXP_REPLACE() 使用 \ 作为反向引用。
  • 日期时间值函数return命令中的相同值。
  • 0x 文字被解析为二进制字符串文字。
  • 允许在 DISTINCT 查询的 ORDER BY 子句中使用不相关的表达式。
  • 某些 MySQL 特定的 ALTER TABLE 命令得到部分支持。
  • TRUNCATE TABLE 重新启动生成列的下一个值。
  • 如果标识列的值是手动指定的,其序列将在插入后更新以生成值。
  • NULL 值的工作方式类似于 DEFAULT 值是对标识列的赋值。
  • 引用约束不需要现有的主键或引用列的唯一约束,如果不存在此类约束,则会自动创建唯一约束。
  • 支持 LIMIT / OFFSET 子句。
  • 可以使用
  • AUTO_INCREMENT 子句。
  • YEAR 数据类型被视为 SMALLINT 数据类型。
  • GROUP BY 子句可以包含 SELECT 列表中从 1 开始的表达式位置。
  • 允许在数值和布尔值之间进行不安全的比较运算符。

就是这样。没有关于程序的事情。正如@jccampanero 在另一个答案中指出的那样,如果要创建存储过程,则必须使用特定于 H2 的语法。