Java for Web学习笔记(一二五)映射(1):再谈converter

我们在《Java for Web学习笔记(一零八):再谈Entity映射(1)数据转换》中已经给出了通过转换器将数据库列的信息和指定类属性进行转换的例子。

原生JDBC的时间日期转换

JDBC的版本

我们原来使用的mysql-connector-java-5.1.x支持多个JDBC的版本,我们可以通过下面的原生jdbc代码来检查当前使用的版本:

try(Connection connection =  DriverManager.getConnection(
        "jdbc:mysql://localhost/test?user=test&password=123456");){
    DatabaseMetaData meta = connection.getMetaData();	
    log.info("Dirver version is {}",meta.getDriverVersion());
    log.info("JDBC version {}.{}",
            meta.getJDBCMajorVersion(),meta.getJDBCMinorVersion());
}

在我的开发环境中显示如下:

20180601 11:28:15.781 [localhost-startStop-1]  [INFO ] TestService:41 init() - Dirver version is mysql-connector-java-5.1.46 ( Revision: 9cc87a48e75c2d2e87c1a293b2862ce651cb256e )
20180601 11:28:15.781 [localhost-startStop-1]  [INFO ] TestService:42 init() - JDBC version 4.0

其实在spring framework启动过程中也有相关的显示:

20180228 09:16:39.460 [localhost-startStop-1]  [DEBUG] (Hibernate) JdbcEnvironmentInitiator: Database ->
       name : MySQL
    version : 5.7.21-0ubuntu0.16.04.1
      major : 5
      minor : 7
20180228 09:16:39.460 [localhost-startStop-1]  [DEBUG] (Hibernate) JdbcEnvironmentInitiator: Driver ->
       name : MySQL Connector Java
    version : mysql-connector-java-5.1.44 ( Revision: b3cda4f864902ffdde495b9df93937c3e20009be )
      major : 5
      minor : 1
20180228 09:16:39.460 [localhost-startStop-1]  [DEBUG] (Hibernate) JdbcEnvironmentInitiator: JDBC version : 4.0
20180228 09:16:39.495 [localhost-startStop-1]  [INFO ] (Hibernate) Dialect: HHH000400: Using dialect: org.hibernate.dialect.MySQL5InnoDBDialect

要确保开发环境中使用JDBC 4.2,可以使用mysql-connector-java-8.0.11.jar。

SQL中datetime的转换

datetime在原生jdbc的api中可以转换为java.sql.timestamp,java.sql.date和java.sql.time。其中java.sql.timestamp可以很方便转换为java 8的Instant和LocalDateTime。

// -------------- Read ---------------
//datetime在原生jdbc的api中可以转换为java.sql.timestamp,java.sql.date和java.sql.time。
java.sql.Date date = resultSet.getDate(1);
java.sql.Time time = resultSet.getTime(1);
java.sql.Timestamp timestamp = resultSet.getTimestamp(1);

//java.sql.timestamp可以很方便转换为java 8的Instant和LocalDateTime
LocalDateTime localDateTime = timestamp.toLocalDateTime();
Instant instant = timestamp.toInstant();

// 也可以通过getObject的方式直接转为java 8的LocalDateTime, LocalDate和LocalTime,但在我们的
// 测试中不能转成Instant, OffsetDateTime, ZonedDateTime,无论是JDBC4.0还是4.2,都不行。
// JDBC 4.2的API是提供了Instant, OffsetDateTime, ZonedDateTime,但具体的实现还在
// mysql-connector-java, 而mysql-connector-java上不支持。估计和Mysql里面的datetime和
// timestamp不带timezone信息有关。
LocalDateTime localDateTime = resultSet.getObject(1, LocalDateTime.class);
LocalDate localDate = resultSet.getObject(1, LocalDate.class);
LocalTime localTime = resultSet.getObject(1, LocalTime.class);

// -------------- Write ---------------
// LocalDateTime可以通过SetObject()直接设置,但Instant,OffsetDateTime,ZonedDateTime不行,
// 会抛出异常。同样JDBC 4.2的api是提供了相关接口,但是mysql-connector-java上不支持。
statement.setObject(1, LocalDateTime.now(),JDBCType.TIMESTAMP);
statement.setObject(1, LocalDateTime.now());

Attribute Converter

要进行转换,还是需要使用转换器。下面以数据库的timestamp(或者datetime),在JPA中对应java.sql.timestamp和Instant之间的转换为例子。

创建转换器

//【1】进行Converter的标记,并实现AttributeConverter接口
@Converter
public class InstantConverter implements AttributeConverter<Instant, Timestamp>{

   //【2】实现AttributeConverter,进行类型转换。这里需要提醒的是,一定要记得处理null。
    @Override
    public Timestamp convertToDatabaseColumn(Instant instant) {
        return instant == null ? null:new Timestamp(instant.toEpochMilli());
    }

    @Override
    public Instant convertToEntityAttribute(Timestamp dbData) {
        return dbData == null ? null:Instant.ofEpochMilli(dbData.getTime());
    }
}

转换器需要在entity的扫描范围内

我们在root上下文的entityManagerFactory()中,确保在扫描范围内

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
    ... ...
    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    // 例子中entity放在cn.wei.flowingflying.chapter24.entities内,转换器放在
    // cn.wei.flowingflying.chapter24.converters包内
    factory.setPackagesToScan("cn.wei.flowingflying.chapter24.entities",
                              "cn.wei.flowingflying.chapter24.converters");
    ... ...
}

在entity上加载转换器

方式1:在属性上加载

@Entity
public class MyEntity{
    @Convert(converter = InstantConverter.class)
    private Instant dateCreated;
    .....
}

方式2:在方法上加载

@Entity
public class MyEntity{
    @Convert(converter = InstantConverter.class)
    private Instant dateCreated;
    .....
}

方式3:在类上加载

@Convert(attributeName = "dateCreated", converter = InstantConverter.class)
@Entity
public class MyEntity{
    private Instant dateCreated;
}
@Converts({
    @Convert(attributeName = "dateCreated", converter = InstantConverter.class),
    @Convert(attributeName = "dateModified", converter = InstantConverter.class)
})
@Entity
public class MyEntity{
    private Instant dateCreated;
}

相关链接:我的Professional Java for Web Applications相关文章

猜你喜欢

转载自blog.csdn.net/flowingflying/article/details/81083331