使用JDBI插入localdate类型字段的问题

时间:2017-06-19 17:21:00

标签: java dropwizard jdbi localdate

我目前正在使用JDBI将我的POJO保存到我的数据库中。我使用MS SQL Server作为我的数据库。我目前无法在表格中插入行,因为LocalDate变量没有映射值存在问题。

scoreDate字段为LocalDate类型,不是强制性的,我没有为此字段传递任何值。但是在尝试运行以下测试时,我得到以下错误。

请您告知问题是什么以及如何解决?

测试类

@Test
    public void testInsertClientScore() throws Exception {
        final DBI dbi = databaseResource.getDBI();
        final ClientScoreDao clientScoreDao = dbi.onDemand(ClientScoreDao.class);

        final ClientScore clientScore = ImmutableClientScore.builder()
                .id(1L)
                .ClientName("TESTCLIENT")
                .build();

        assertEquals(1,clientScoreDao.insert(clientScore));

    }

错误追踪

org.skife.jdbi.v2.exceptions.UnableToExecuteStatementException: com.microsoft.sqlserver.jdbc.SQLServerException: 
Implicit conversion from data type varbinary to date is not allowed. Use the CONVERT function to run this query. [statement:"insert into ICEBERG.ClientScore (scoreId, clientname, scoredate) values (:id, :ClientName, :scoreDate)", located:"insert into ICEBERG.ClientScore (scoreId, clientname, scoredate) values (:id, :ClientName, :scoreDate)", rewritten:"insert into ICEBERG.ClientScore (scoreId, clientname, scoredate) values (?, ?, ?)", arguments:{ positional:{}, named:{ClientName:'TESTCLIENT',id:1,scoreDate:null}, finder:[]}]

    at org.skife.jdbi.v2.SQLStatement.internalExecute(SQLStatement.java:1338)
    at org.skife.jdbi.v2.Update.execute(Update.java:56)
    at org.skife.jdbi.v2.sqlobject.UpdateHandler$2.value(UpdateHandler.java:67)

.....
....

Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Implicit conversion from data type varbinary to date is not allowed. Use the CONVERT function to run this query.
    at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:217)

数据库表定义

-- ClientScore table
CREATE TABLE ICEBERG.ClientScore (
   ScoreId                 INTEGER                                         NOT NULL,
   ClientName              VARCHAR(50)                                  NOT NULL,
   ScoreDate               DATE                                            NULL,
   CONSTRAINT PK_CLIENTSCORE_TEST PRIMARY KEY (ScoreId)
)

POJO

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.mercuria.dali.jdbi.Jdbi;
import com.mercuria.dali.jdbi.JdbiOptions;
import org.immutables.value.Value;

import javax.annotation.Nullable;
import java.io.Serializable;
import java.time.LocalDate;

@Value.Immutable
@Jdbi(tableName = "ICEBERG.ClientScore")
@JsonSerialize(as = ImmutableClientScore.class)
@JsonDeserialize(as = ImmutableClientScore.class)
public interface ClientScore extends Serializable {

    @JsonProperty("scoreid")
    @JdbiOptions(columnName = "scoreId")
    Long id();

    @JsonProperty("clientname")
    @JdbiOptions(columnName = "clientname")
    String ClientName();

    @JsonProperty("scoredate")
/*    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonSerialize(using = LocalDateSerializer.class)*/
    @JdbiOptions(columnName = "scoredate")
    @Nullable
    LocalDate scoreDate();  
}

我在配置JDBI连接时为LocalDate数据类型设置了映射器。

 public static void configureDbi(DBI dbi) {
        // Register argument factories
        dbi.registerArgumentFactory(new NullDoubleArgumentFactory());
        dbi.registerArgumentFactory(new UuidArgumentFactory());
        dbi.registerArgumentFactory(new LocalDateArgumentFactory());
        dbi.registerArgumentFactory(new InstantArgumentFactory(Optional.of(TimeZone.getTimeZone("UTC"))));
        dbi.registerColumnMapper(new InstantMapper(Optional.of(TimeZone.getTimeZone("UTC"))));

        dbi.registerArgumentFactory(new OptionalArgumentFactory("com.microsoft.sqlserver.jdbc.SQLServerDriver"));
        dbi.registerArgumentFactory(new OptionalIntArgumentFactory());
        dbi.registerArgumentFactory(new OptionalDoubleArgumentFactory());

        // Register column mappers
        dbi.registerColumnMapper(new UuidColumnMapper());
        dbi.registerColumnMapper(new LocalDateMapper());
    }

1 个答案:

答案 0 :(得分:0)

我也遇到了他的问题并发现Dropwizard LocalDateArgumentFactory类确定是否创建Argument类来处理值的方式是使用instanceof check:

public boolean accepts(Class<?> expectedType, Object value, StatementContext ctx) {
    return value instanceof LocalDate;
}

由于value为null,因此返回false,我通过创建它解决了这个问题:

public class NullAwareLocalDateArgumentFactory extends LocalDateArgumentFactory {
    @Override
    public boolean accepts(Class<?> expectedType, Object value, StatementContext ctx) {
        if(value == null){
            //Look at the expected type. Tbh. not sure why it isn't simply doing this by default ...
            return LocalDate.class.isAssignableFrom(expectedType);
        }
        else {
            return super.accepts(expectedType, value, ctx);
        }
    }
}