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.util
或 java.sql
包中使用任何 class。如果你想明确地包含一些导入,或者如果你需要来自其他包的 classes 而不是提到的,则应在第一个 $$
标记之后立即提供相应的导入。此外,您需要在导入结束和实际 Java 方法开始处包含 @CODE
信号 H2。
- 如果您需要在代码中引用
Connection
数据库,它应该是方法的第一个参数。
- 更喜欢提出而不是隐藏异常:它将允许您的事务作为一个整体适当地提交或回滚。
您可以照常调用这样的函数:
CALL INIT_PSEUDOS (5);
请为 maxPseudo
参数提供适当的值。
请将提供的代码视为一个使用示例:您可以通过不同的方式改进代码,例如使用 PreparedStatement
s 而不是 Statement
s 以提高效率,检查参数可空性等。
“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 的语法。
代码说明
数据库连接
我尝试在不使用外部数据库的情况下在本地数据库中存储 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.util
或java.sql
包中使用任何 class。如果你想明确地包含一些导入,或者如果你需要来自其他包的 classes 而不是提到的,则应在第一个$$
标记之后立即提供相应的导入。此外,您需要在导入结束和实际 Java 方法开始处包含@CODE
信号 H2。 - 如果您需要在代码中引用
Connection
数据库,它应该是方法的第一个参数。 - 更喜欢提出而不是隐藏异常:它将允许您的事务作为一个整体适当地提交或回滚。
您可以照常调用这样的函数:
CALL INIT_PSEUDOS (5);
请为 maxPseudo
参数提供适当的值。
请将提供的代码视为一个使用示例:您可以通过不同的方式改进代码,例如使用 PreparedStatement
s 而不是 Statement
s 以提高效率,检查参数可空性等。
“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 的语法。