Schema-validation: wrong column type encountered in column

Привет сталкер

Столкнулся с проблемкой на днях, которая заставила меня изрядно подумать

Есть проект на spring boot, с hibernate, liquibase и другими штуками, которые в рамках этой статьи не важны

Работал проект прекрасно до тех пор, пока не потребовалось обновить версию стартера, который мы подключаем в pom.xml внутри <parent>…</parent>

Сразу после апа версии стартера мое приложение начало выплевывать следующую ошибку: «Schema-validation: wrong column type encountered in column [valid_to] in table [scans]; found [timestamp (Types#TIMESTAMP)], but expecting [date (Types#DATE)]«

Для ленивых, в самом низу статьи есть тезисно выводы и правки, которые пришлось сделать

Полный стектрейс с этой ошибкой: (этот стектрейс тут ключевой и если вы видите у себя нечто подобное, то вероятно этот пост для вас)

Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: wrong column type encountered in column [valid_to] in table [scans]; found [timestamp (Types#TIMESTAMP)], but expecting [date (Types#DATE)]
	at org.hibernate.tool.schema.internal.SchemaValidatorImpl.validateColumnType(SchemaValidatorImpl.java:105)
	at org.hibernate.tool.schema.internal.SchemaValidatorImpl.validateTable(SchemaValidatorImpl.java:92)
	at org.hibernate.tool.schema.internal.SchemaValidatorImpl.doValidation(SchemaValidatorImpl.java:50)
	at org.hibernate.tool.hbm2ddl.SchemaValidator.validate(SchemaValidator.java:91)
	at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:475)
	at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:444)
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:879)
	at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60)
	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:360)
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:384)
	... 69 common frames omitted

Для понимания, у меня в проекте есть некий domain-класс, у которого есть поле с типом LocalDate и проблема начала проявляться именно в том, как хибер пытался проверить DDL схему на основе этого объекта при создании SessionFactory (если я правильно понял)

@Data
@Entity
@Builder
@Table(name = "scans")
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Scan extends AuditableModel<ScanIdentification> implements Serializable {
	@EmbeddedId
	private ScanIdentification id;
	...
	private LocalDate validTo;
}

Поковырявшись в ваших интернетах я пришел к тому, что необходимо для поля validTo навесить аннотейшн Column со свойством columnDefinition (это фрагмент SQL, который используется при генерации DDL для столбца)

@Column(columnDefinition = "DATE")
private LocalDate validTo;

Далее этого оказалось мало и пришлось написать конвертер, который поможет хиберу конвертить это поле при сохранении в БД и при выгрузке:

Scan.java

@Convert(converter = LocalDateConverter.class)
@Column(columnDefinition = "DATE")
private LocalDate validTo;

LocalDateConverter.java

public class LocalDateConverter implements AttributeConverter <LocalDate, Timestamp> {

@Override
public Timestamp convertToDatabaseColumn(LocalDate attribute) {
	return attribute != null ? Timestamp.valueOf(attribute.atStartOfDay()) : null;
}

@Override
public LocalDate convertToEntityAttribute(Timestamp dbData) {
	return dbData != null ? dbData.toLocalDateTime().toLocalDate() : null;
}

}

И последнее, что пришлось сделать, это построить дерево зависимостей mvn dependency:tree на старой версии стартера (которая работала) и на новой версии стартера (которая начала отваливаться с ошибкой), чтобы посмотреть diff и понять что же такого там изменилось, что начало приводить к ошибкам:

К моему удивлению единственное интересное отличие версий либ, которое я обнаружил было связано с spring-ws-core. В старой версии стартера использовалась версия 2.4.0.RELEASE, а в новой 2.4.5.RELEASE

После того как я исключил версию 2.4.5.RELEASE и руками в проект подсунул 2.4.0.RELEASE, мой проект снова запустился, все тесты прошли на 100% и он начал работать в штатном режиме

<dependency>
	<groupId>org.springframework.ws</groupId>
	<artifactId>spring-ws-core</artifactId>
	<version>2.4.0.RELEASE</version>
</dependency>

Еще раз резюмирую что пришлось сделать:

  1. @Column(columnDefinition = «DATE»)
  2. @Convert(converter = LocalDateConverter.class) + реализация LocalDateConverter
  3. 2.4.0.RELEASE для spring-ws-core (стояла более высокая, которая все ломало)

Пока у меня не дошли руки разобраться почему 2.4.5.RELEASE как-то конфликтует с мои хибером, но если руки дойдут, я обязательно дополню эту статью, а если у вас есть какие-то мысли, добро пожаловать в комментарии