簡體   English   中英

為什么我的H2數據庫/ Spring Boot應用程序出現JdbcSQLException(非十六進制字符)?

[英]Why am I getting JdbcSQLException (non-hex characters) with my H2 database / Spring boot application?

所以簡短的版本,我猜我有某種字符編碼問題,或者數據庫出於某種原因以Hibernate / Spring-jpa不喜歡的格式存儲/返回日期。

但是,如果我能解決問題的話,我會感到不安!

使用Hibernate 5在實體道具中使用J8 LocalDate東西。

正在創建數據庫並正確插入了數據(您將在下面的日志代碼段中看到返回的日期值)。

日志片段:

2016-10-26 13:25:19.885 ERROR 1028 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: 
Could not read entity state from ResultSet : EntityKey[uk.co.deditech.entity.Person#2]; 
nested exception is org.hibernate.exception.GenericJDBCException: Could not read entity state from ResultSet : 
EntityKey[uk.co.deditech.entity.Person#2]] with root cause org.h2.jdbc.JdbcSQLException: Hexadecimal string contains non-hex character: "2016-03-23" [90004-192]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345) ~[h2-1.4.192.jar:1.4.192]
at org.h2.message.DbException.get(DbException.java:179) ~[h2-1.4.192.jar:1.4.192]
at org.h2.message.DbException.get(DbException.java:155) ~[h2-1.4.192.jar:1.4.192]
at org.h2.util.StringUtils.convertHexToBytes(StringUtils.java:986) ~[h2-1.4.192.jar:1.4.192]
at org.h2.value.Value.convertTo(Value.java:973) ~[h2-1.4.192.jar:1.4.192]
at org.h2.value.Value.getBytes(Value.java:422) ~[h2-1.4.192.jar:1.4.192]
at org.h2.jdbc.JdbcResultSet.getBytes(JdbcResultSet.java:1077) ~[h2-1.4.192.jar:1.4.192]
<snip>

搖籃:

compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile("org.springframework.boot:spring-boot-starter-freemarker")
compile group: 'com.h2database', name: 'h2', version:'1.4.192'

實體:

@Entity
@Table(name = "person")
public @Data class Person {
   ...
   @Column(name = "last_grading_date", nullable = true)
   private LocalDate lastGradingDate;
}

Spring Boot自動數據庫創建腳本片段:

schema.sql
create table PERSON
(
id int not null,
last_grading_date date
)

data.sql
insert into person (id, last_grading_date)
values (1, '2015-02-20');

屬性(問題發生在添加以下編碼屬性之前和之后):

spring.datasource.url=jdbc:h2:mem:AZ;DB_CLOSE_DELAY=-1;
spring.datasource.driverClassName=org.h2.Driver
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.datasource.sql-script-encoding=UTF-8

編輯:經過更多的挖掘后,我發現“驗證”是spring.jpa.hibernate.ddl-auto屬性的設置。 所以我嘗試了。

我現在在啟動過程中遇到以下錯誤...

Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: wrong column type encountered in column [last_grading_date] in table [person]; found [date (Types#DATE)], but expecting [binary(255) (Types#VARBINARY)]
at org.hibernate.tool.schema.internal.SchemaValidatorImpl.validateColumnType(SchemaValidatorImpl.java:105) ~[hibernate-core-5.0.11.Final.jar:5.0.11.Final]

我通過在pom中添加此依賴項來使其工作:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-java8</artifactId>
    <version>${hibernate.version}</version>
</dependency>

我不知道為什么它不能開箱即用,但是有了這種依賴性,它解決了這個問題。

我還在屬性下面添加了此屬性: <hibernate.version>5.0.5.Final</hibernate.version>

我的用於復制的示例代碼:

Data.sql:

insert into person (id, last_grading_date)
values (1, '2015-02-20');

application.properties

spring.datasource.url=jdbc:h2:mem:AZ;DB_CLOSE_DELAY=-1;
spring.datasource.driverClassName=org.h2.Driver
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.datasource.sql-script-encoding=UTF-8

人員資料庫

public interface PersonRepository extends JpaRepository<Person, Integer>{

}

@Entity
@Table(name = "person")
public class Person {

    @Id
    @Column
    private int id;

    @Column(name = "last_grading_date", nullable = true)
    @Type(type = "java.time.LocalDate")
    private LocalDate lastGradingDate;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public LocalDate getLastGradingDate() {
        return lastGradingDate;
    }

    public void setLastGradingDate(LocalDate lastGradingDate) {
        this.lastGradingDate = lastGradingDate;
    }
}

應用方式

@SpringBootApplication
public class TestApplication implements CommandLineRunner{

    @Autowired
    PersonRepository repo;

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }

    @Override
    public void run(String... arg0) throws Exception {
        Person p = repo.findOne(1);
        System.out.println(p.getLastGradingDate());
    }
}

結果: 2015-02-20


GitHub上添加了一個工作示例。 該演示基於Spring-bootJava 8Hibernate 5mavenjava.time.LocalDate

JPA 2.1在Java 8之前發布,而Date and Time API當時根本不存在。 因此,@ Temporal批注只能應用於類型為java.util.Date和java.util.Calendar的屬性。

如果要將LocalDate屬性存儲在DATE列中,或者將LocalDateTimeTIMESTAMP列中,則需要自己定義到java.sql.Datejava.sql.Timestamp的映射。

屬性轉換器是JPA 2.1規范的一部分,因此可以與任何JPA 2.1實現一起使用,例如Hibernate或EclipseLink。 我為以下示例使用了Wildfly 8.2和Hibernate 4.3。

轉換LocalDate

如下面的代碼片段所示,為LocalDate創建屬性轉換器不需要做很多事情。

@Converter(autoApply = true)
public class LocalDateAttributeConverter implements AttributeConverter<LocalDate, Date> {

    @Override
    public Date convertToDatabaseColumn(LocalDate locDate) {
        return (locDate == null ? null : Date.valueOf(locDate));
    }

    @Override
    public LocalDate convertToEntityAttribute(Date sqlDate) {
        return (sqlDate == null ? null : sqlDate.toLocalDate());
    }
}

您需要使用其兩個方法convertToDatabaseColumnconvertToEntityAttribute實現AttributeConverter<LocalDate, Date>接口。 在方法名稱上可以看到,其中一個定義了從實體屬性( LocalDate )的類型到數據庫列類型( Date )的轉換,另一個定義了逆向轉換。 轉換本身非常簡單,因為java.sql.Date已經提供了與LocalDate之間進行轉換的方法。

另外,屬性轉換器需要使用@Converter注釋進行注釋。 由於可選的autoApply = true屬性,該轉換器將應用於LocalDate類型的所有屬性。 如果要為每個屬性分別定義轉換器的用法,請看這里。

屬性的轉換對開發人員是透明的, LocalDate屬性可以用作任何其他實體屬性。 例如,您可以將其用作查詢參數。

LocalDate date = LocalDate.of(2015, 8, 11);
TypedQuery<MyEntity> query = this.em.createQuery("SELECT e FROM MyEntity e WHERE date BETWEEN :start AND :end", MyEntity.class);
query.setParameter("start", date.minusDays(2));
query.setParameter("end", date.plusDays(7));
MyEntity e = query.getSingleResult();

轉換LocalDateTime

LocalDateTime的屬性轉換器基本相同。 您需要實現AttributeConverter<LocalDateTime, Timestamp>接口,並且轉換器需要使用@Converter批注進行批注。 LocalDateConverter相似, LocalDateTimejava.sql.Timestamp之間的轉換是通過Timestamp的轉換方法完成的。

@Converter(autoApply = true)
public class LocalDateTimeAttributeConverter implements AttributeConverter<LocalDateTime, Timestamp> {

    @Override
    public Timestamp convertToDatabaseColumn(LocalDateTime locDateTime) {
        return (locDateTime == null ? null : Timestamp.valueOf(locDateTime));
    }

    @Override
    public LocalDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
        return (sqlTimestamp == null ? null : sqlTimestamp.toLocalDateTime());
    }
}

實體實例

@Entity
public class MyEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", updatable = false, nullable = false)
    private Long id;

    @Column
    private LocalDate date;

    @Column
    private LocalDateTime dateTime;

    ...

}

就我而言(Spring Boot 1.5.10),我要做的就是將以下內容添加到pom.xml屬性部分

<hibernate.version>5.2.12.Final</hibernate.version>

默認情況下,此版本的Spring Boot看起來使用的是Hibernate 5.0.12.Final

您沒有在休眠狀態中為last_grading_date指定列的類型。 您可以使用:

@Column(name = "last_grading_date", nullable = true)
@Type(type="date")
private LocalDate lastGradingDate;

如果不起作用,請將LocalDate類更改為java.sql.Date

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM