web-dev-qa-db-ja.com

H2データベース/ SpringブートアプリケーションでJdbcSQLException(16進数以外の文字)が発生するのはなぜですか?

したがって、短いバージョンでは、何らかの文字エンコーディングの問題があるか、DBが何らかの理由で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>

Gradle:

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

スプリングブート自動DB作成スクリプトスニペット:

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

編集:さらに掘り下げた後、「validate」が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]
14
DaFoot

私の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

PersonRepository

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 5mavenおよびJava.time.LocalDateに基づいて構築されています。

13
Patrick

JPA 2.1は、Java 8の前にリリースされ、Date and Time APIはその時点では存在しませんでした。したがって、@ TemporalアノテーションはタイプJava.util.DateおよびJava.util.Calendarの属性に適用されます

LocalDate属性をDATE列に、またはLocalDateTimeTIMESTAMP列に格納する場合は、Java.sql.DateまたはJava.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());
    }
}

2つのメソッドconvertToDatabaseColumnおよびconvertToEntityAttributeを使用してAttributeConverter<LocalDate, Date>インターフェースを実装する必要があります。メソッド名を見るとわかるように、そのうちの1つはエンティティー属性のタイプ(LocalDate)からデータベース列タイプ(Date)への変換を定義し、もう1つは逆変換を定義します。 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;

    ...

}
2
Vazgen Torosyan

私の場合(Spring Boot 1.5.10)必要なのは、pom.xmlのプロパティセクションに以下を追加することだけです。

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

デフォルトでは、このバージョンのSpring BootはHibernate 5.0.12を使用しているようです。

1
ashario

Hibernateでlast_grading_dateの列のタイプを指定しませんでした。以下を使用できます。

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

それが機能しない場合は、LocalDateクラスをJava.sql.Dateに変更します。

0
Asoub