我正在将我的应用程序从Spring Boot 1.4. x和Spring Data 1.10.2迁移到Spring Boot 2.0.0和Spring Data 2.0.4
我遇到的最新问题似乎是Hibernate版本的变化。我有LocalDateTime/LocalDate,它们作为blob字节数组存储在DB中。它们看起来像\xaced00057372000d6a6176612e746……
当我使用升级的依赖项启动应用程序时。我得到时间戳上的尾随垃圾:
和类型时间戳/日期/时间的错误值:{1}
。
我相信Hibernate现在看起来将LocalDateTime/LocalDate本机视为时间戳。以及来自先前实现的blob条目(不使用转换器)。导致这个问题。
你觉得怎么处理这件事?
一个实体可能有以下内容,对于排序很重要的日期,我们确实使用了转换器:
....
@Column("CREATE_DATE")
private LocalDate dateCreated;
@Column("TIME_STAMP_UPDATED")
@Convert(converter = MyLocalDateTimeConverter.class)
private LocalDateTime timestampUpdate;
....
我已经通过编写一个转换器解决了这个问题。值得注意的是,最初在HibernateORM中构建LDT到Blob转换器的团队并不期望人们使用它,而是包括Java8转换器或应用他们自己的转换器,就像我在上面所做的那样,我们需要更多的精度。
我想在DB中保留date作为TIMESTAMP或其他更实用的类型是有意义的,但同时我觉得如果我们构建某个东西,我们应该期望它以这种方式使用。尽管如此,这是我解决它的方法。
我收集了两个可用的解决方案。
>
是更新我们的模式,将所有这些blob转换为TIMESTAMP并删除DB上的旧列。不是一个漂亮的解决方案。
创建一个Mapper来强制ORM正确处理实体字段。使用Spring,这是非常简单的事情,我正在使用的解决方案取得了成功。如果你在我的代码中看到一些可以改进的东西,请评论!我想确保我正在实施和分享最好的解决方案。
我创建了一个新类。
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.ArrayUtils;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.time.LocalDateTime;
...
@Converter
@Log4j2
public class LegacyLocalDateTimeConverter implements AttributeConverter<LocalDateTime, Byte[]> {
@SneakyThrows
@Override
public Byte[] convertToDatabaseColumn(final LocalDateTime localDateTime) {
if(localDateTime == null) {
return null;
}
try (final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
final ObjectOutputStream oos = new ObjectOutputStream(buffer)) {
oos.writeObject(localDateTime);
return ArrayUtils.toObject(buffer.toByteArray());
}catch (Exception e){
log.error("Error occurred converting ldt to byte[], e.message: [{}], e.cause: [{}], e.stack: [{}], e" +
".class: [{}]",
e.getMessage(), e.getCause(), e.getStackTrace(), e.getClass());
throw e;
}
}
@SneakyThrows
@Override
public LocalDateTime convertToEntityAttribute(final Byte[] bytes) {
if(bytes == null) {
return null;
}
try(final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(ArrayUtils.toPrimitive(bytes));
final ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream)){
final LocalDateTime dateTime = LocalDateTime.parse(objectInputStream.readObject().toString());
log.info("Object: [{}]", dateTime);
return dateTime;
}catch (Exception e){
log.error("Error occurred converting byte[] to ldt, e.message: [{}], e.cause: [{}], e.stack: [{}], e" +
".class: [{}]",
e.getMessage(), e.getCause(), e.getStackTrace(), e.getClass());
throw e;
}
}
}
然后,我的问题中第一个代码块中的示例变为:
....
@Column("CREATE_DATE")
@Convert(converter = LegacyLocalDateTimeConverter.class)
private LocalDate dateCreated;
@Column("TIME_STAMP_UPDATED")
private LocalDateTime timestampUpdate;
....
您会注意到我从timeStampUpdate
中删除了转换器,并将我们的新转换器应用于dateCreated
。Spring boot 2.0.0和Spring Data 2.0.4不再需要我们原来的转换器,因为它现在原生处理这个问题。在我的例子中,新的转换器(用H2和Postgres测试)处理存储在DB中的blob/byteArray对象,并且不再抛出有问题的错误。