DbUnit - JdbcSQLException:找不到函数“*”

时间:2017-09-25 15:06:01

标签: java sql-server user-defined-functions dbunit

我在MS SQL Server中有一个用户定义的函数,从Java代码调用,在H2数据库中运行集成测试时似乎未定义。您可以在the previous question中找到我的代码。

测试代码:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {H2Config.class})
@TestExecutionListeners({
        DependencyInjectionTestExecutionListener.class,
        DbUnitTestExecutionListener.class,
        TransactionalTestExecutionListener.class
})
@TransactionConfiguration(defaultRollback = true)
public class TableDaoTest {

    @Autowired
    private TableDao tableDao;

    @Test
    @DatabaseSetup("/datasets/import.xml")
    public void testMethod01() {
        tableDao.getRecordsByGroup();
        ...

数据库模式由Hibernate自动生成。正如您所见,DbUnit使用xml数据集填充测试数据。此测试失败,因为我在MS SQL Server DB中存在的函数在H2数据库中未定义。

应用程序日志:

Caused by: org.hibernate.exception.GenericJDBCException: could not prepare statement
    ...
Caused by: org.h2.jdbc.JdbcSQLException: Function "SAFE_MOD" not found; SQL statement:
    select table10_.id, table10_.value, ... from Table1 table10_ where table10_.group1=dbo.safe_mod(?, ?);
    ...

如何在DbUnit测试之前导入/创建函数?

3 个答案:

答案 0 :(得分:1)

@naXa 和@Alan 方法适用于标量值函数。对于表值函数,使用 ResultSet:

package com.package.app;
import org.h2.tools.SimpleResultSet;

@SuppressWarnings("unused")
public class H2Functions {

    // All function params goes here
    // LocalDate not working here, we have to use java.sql.Date
    public static ResultSet gePrice(Long recipientId, Long currencyId, Date priceDate) {

        SimpleResultSet rs = new SimpleResultSet();
        rs.addColumn("price", Types.DECIMAL, 10, 0);
        rs.addColumn("priceDate", Types.TIMESTAMP, 10, 0);

        rs.addRow(new BigDecimal("123.23"), new Timestamp(LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)) );

        return rs;
    }
}

示例 application.yml 配置:

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:h2:DB_NAME;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    name:
    username:
    password:
    hikari:
      auto-commit: false
      # create alias for functions on db connection initialization
      connection-init-sql: "CREATE ALIAS IF NOT EXISTS SAFE_MOD DETERMINISTIC FOR \"com.package.app.H2Functions.gePrice\";"

然后你可以用你的 POJO 包装响应。

您可以在 H2 文档中找到有关用户定义函数的更多信息: http://www.h2database.com/html/features.html#user_defined_functions

答案 1 :(得分:0)

H2数据库不支持用户定义的SQL函数。但是,在此数据库中,Java函数也可以用作存储过程。

@SuppressWarnings("unused")
public class H2Function {
    public static int safeMod(Integer n, Integer divider) {
        if (divider == null) {
            divider = 5000;
        }

        return n % divider;
    }

}

请注意,仅支持静态Java方法;类和方法都必须是公共的。

必须先通过调用CREATE ALIAS ... FOR来声明(在数据库中注册)Java函数,然后才能使用它:

CREATE ALIAS IF NOT EXISTS safe_mod DETERMINISTIC FOR "by.naxa.H2Function.safeMod";

这个语句应该在任何测试之前执行,所以我决定把它放在连接init SQL中:

@Bean
public DataSource dataSource() {
    BasicDataSource dataSource = new BasicDataSource();

    dataSource.setDriverClassName("org.h2.Driver");
    dataSource.setUrl("jdbc:h2:mem:my_db_name");
    dataSource.setUsername("sa");
    dataSource.setPassword("");   
    dataSource.setConnectionInitSqls(Collections.singleton(
        "CREATE ALIAS IF NOT EXISTS safe_mod DETERMINISTIC FOR \"by.naxa.H2Function.safeMod\";"));

    return dataSource;
}

答案 2 :(得分:0)

请相信naxa,此解决方案基于他们的解决方案。这是为了从postgres中“存根” WORD_SIMILARITY,

@RunWith(SpringRunner.class)
@SpringBootTest
@TestPropertySource(
        locations = "classpath:application-test.properties")
public class testServiceTests {

    @Autowired
    private MyService myService;

    @Test
    public void someTest() {

    }
}

这应该在您的application-test.properties中

spring.datasource.driverClassName=org.h2.Driver
spring.datasource.url=jdbc:h2:~/my_database;MODE=PostgreSQL;INIT=CREATE ALIAS IF NOT EXISTS WORD_SIMILARITY DETERMINISTIC FOR "com.example.H2Function.wordSimilarity";TRACE_LEVEL_FILE=0;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;
spring.datasource.username=postgres
spring.datasource.password=postgres
hibernate.dialect=org.hibernate.dialect.PostgreSQL9Dialect
hibernate.flushMode=FLUSH_AUTO
hibernate.hbm2ddl.auto=create-drop
spring.datasource.data=classpath:V1__base-schema.sql

这是要替换的H2功能

@SuppressWarnings("unused")
public class H2Function {

    public static double wordSimilarity(String string, String word) {
        if ( word== null ) {

            return 0;
        }

        return 0.5;
    }

}