Java 使用JDBI插入localdate类型字段时出现问题
我目前正在使用JDBI将POJO持久化到数据库中。我正在使用MS SQL Server作为我的数据库。我当前无法在表中插入行,因为映射时Java 使用JDBI插入localdate类型字段时出现问题,java,dropwizard,jdbi,localdate,Java,Dropwizard,Jdbi,Localdate,我目前正在使用JDBI将POJO持久化到数据库中。我正在使用MS SQL Server作为我的数据库。我当前无法在表中插入行,因为映射时LocalDate变量没有值 scoreDate字段是LocalDate类型,是非强制性的,我不会为此字段传递任何值。但是在尝试运行下面的测试时,我得到了下面的错误 请告知问题可能是什么以及如何解决? 测试类 @Test public void testInsertClientScore() throws Exception { fina
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());
}
我也遇到了他的问题,发现Dropwizard LocalDateArgumentFactory类决定是否创建参数类来处理该值的方式是使用instanceof check:
public boolean accepts(Class<?> expectedType, Object value, StatementContext ctx) {
return value instanceof LocalDate;
}
public boolean接受(类expectedType、对象值、语句上下文ctx){
返回值instanceof LocalDate;
}
由于值为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);
}
}
}
公共类NullAwareLocalDateArgumentFactory扩展了LocalDateArgumentFactory{
@凌驾
公共布尔接受(类expectedType、对象值、语句上下文ctx){
如果(值==null){
//查看预期的type.Tbh。不确定它为什么不只是默认执行此操作。。。
返回LocalDate.class.isAssignableFrom(expectedType);
}
否则{
返回super.accepts(expectedType、value、ctx);
}
}
}