2017-06-19 72 views
0

我目前使用JDBI将POJO保存到我的数据库中。我正在使用MS SQL Server作为我的数据库。由于映射LocalDate变量没有值的问题,我目前无法在表中插入一行。问题使用JDBI插入本地类型字段

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(); 
} 

我有映射器设置为LocalDate数据类型,同时配置我JDBI连接。

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()); 
    } 

回答

0

我碰到了他的问题,也是和发现的方式Dropwizard LocalDateArgumentFactory类确定是否要创建参数类对付值是使用instanceof检查:

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); 
     } 
    } 
}