用于java.sql.Timestamp的Vaadin转换器

时间:2014-10-24 11:43:39

标签: postgresql timestamp converter vaadin datefield

我正在使用PostgreSQL数据库,timestamp列有类java.sql.Timestamp。即使这个类扩展了java.util.Date,当我编辑PopupDateFiels时,我也获得了错误

无法将java.util.Date类型的值转换为模型类型类java.sql.Timestamp。没有设置转换器且类型不兼容。

默认转换器工厂无法正常工作。我试着写

dateField.setConverter(new DateToSqlDateConverter());

dateField.setConverter(StringToDateConverter.class);

结果相同。
通过点击日历中的某一天,我可以看到欧洲格式的有效日期和时间" 23.10.2014 13.44",但是在控制台上提交失败的类似消息:

Caused by: com.vaadin.data.util.converter.Converter$ConversionException: Could not convert value to Timestamp
at com.vaadin.ui.AbstractField.convertToModel(AbstractField.java:725)
at com.vaadin.ui.AbstractField.getConvertedValue(AbstractField.java:811)
at com.vaadin.ui.AbstractField.commit(AbstractField.java:247)
... 42 more
Caused by: com.vaadin.data.util.converter.Converter$ConversionException: Unable to convert value of type java.util.Date to model type class java.sql.Timestamp. No converter is set and the types are not compatible.
at com.vaadin.data.util.converter.ConverterUtil.convertToModel(ConverterUtil.java:181)
at com.vaadin.ui.AbstractField.convertToModel(AbstractField.java:745)
... 45 more

我在哪里可以得到合适的转换器?谢谢你的建议。

2 个答案:

答案 0 :(得分:2)

我推荐这种方式:

PopupDateField pdf = new PopupDateField();
Timestamp ts = new Timestamp(System.currentTimeMillis());
ObjectProperty<Timestamp> prop = new ObjectProperty<Timestamp>(ts);
pdf.setPropertyDataSource(prop);
pdf.setConverter(MyConverter.INSTANCE);

使用此转换器:

public class MyConverter implements Converter<Date, Timestamp> {

    private static final long serialVersionUID = 1L;
    public static final MyConverter INSTANCE = new MyConverter();

    @Override
    public Timestamp convertToModel(Date value,
            Class<? extends Timestamp> targetType, Locale locale)
            throws ConversionException {
        return value == null ? null : new Timestamp(value.getTime());
    }

    @Override
    public Date convertToPresentation(Timestamp value,
            Class<? extends Date> targetType, Locale locale)
            throws ConversionException {
        return new Date(value.getTime());
    }

    @Override
    public Class<Timestamp> getModelType() {
        return Timestamp.class;
    }

    @Override
    public Class<Date> getPresentationType() {
        return Date.class;
    }

    private Object readResolve() {
        return INSTANCE; // preserves singleton property
    }

}

答案 1 :(得分:0)

不要将java.sql.Timestamp视为java.util.Date

的子类

虽然java.sql.Timestamp在技术上继承自java.util.Date,但类doc的最后一段声明您不应该这样认为,您应该忽略这一事实。

  

由于Timestamp类和上面提到的java.util.Date类之间存在差异,因此建议代码不要将Timestamp值一般视为java.util.Date的实例。 Timestamp和java.util.Date之间的继承关系实际上表示实现继承,而不是类型继承。

换句话说,这个类结构是一个笨拙的黑客。早期Java中的日期时间类很匆忙,而且没有经过深思熟虑。

转换中的一个问题是java.sql.Timestamp具有纳秒分辨率,但java.util.Date只有毫秒,因此数据将丢失。

请注意,Vaadin使用支持网格的SQLContainer(可能是表格)犯了这个错误。 SQLContainer中的java.sql.Timestamp列被视为java.util.Date(超类),从而在涉及微秒或纳秒时丢失数据。 (请参阅下面的转换器实现作为解决方法。)

java.time

Java 8及更高版本中的新java.time package重新考虑了那些旧类,从头开始重新编写。这些新课程的灵感来自Joda-Time,由JSR 310定义,并由ThreeTen-Extra project扩展。

java.time类是为纳秒级分辨率而构建的。因此,在java.time.Instant和java.sql.Timestamp之间进行转换时不会丢失数据。

旧的和新的日期时间类都有在新旧之间来回转换的方法。请注意下面提到的java.sql.Timestamp::toInstantTimestamp.from( instant )方法。

转换为java.time.Instant的字符串↔java.sql.Timestamp

此示例可能有助于也可能无法回答问题。我正在将java.sql.Timestamp数据转换为java.time.Instant的字符串。

除了文本格式之外,请记住我的转换器和默认转换器之间存在一个差异:没有数据丢失! (见上面的讨论)此示例屏幕截图发生,没有任何小数秒。但如果它确实有微秒或纳秒,那么该数据将被保留并显示为需要。

screenshot of two columns from a Grid, one using the converter presented here for string of Instant to java.sql.Timestamp, and one using default converter for Date

以下是一些代码,用于执行从java.sql.Timestamp转换的Instant的String表示形式。换句话说:

  • 演示文稿:字符串(使用ISO_INSTANT格式化程序的即时消息)
  • 型号: java.sql.Timestamp

此代码是根据与Vaadin 7捆绑在一起的source code类的com.vaadin.data.util.converter.StringToDateConverter修改的。

此代码对我来说是在SQLContainer中在Vaadin 7.5.2中显示Grid对象的java.sql.Timestamp列。我还没有尝试Table,但也应该在那里工作。

在为我工作时,此代码可能需要一些清理。特别是,(a)评论可能是错误的,(b)我有SLF4J日志记录,您可能需要更改/删除。

默认的ISO 8601格式对于人类来说有点难以阅读,因此我正在尝试更改格式和调整时区。

要使用此类,您必须在Grid.Column对象上调用setConverter。或按照ConverterFactory

中的说明配置this wiki page
package com.powerwrangler.util;

import com.vaadin.data.util.converter.Converter;
import java.text.DateFormat;
import java.time.format.DateTimeParseException;
import java.sql.Timestamp;
import java.util.Locale;
import java.time.Instant;
import org.slf4j.LoggerFactory;

/**
 *
 * Converts java.sql.Date objects for presentation as String in format of a java.time.Instant object.
 *
 * String format by default is DateTimeFormatter.ISO_INSTANT, such as '2011-12-03T10:15:30Z'.
 *
 * Based on code from Vaadin’s bundled StringToDateConverter.java
 *
 * PRESENTATION: String (of an Instant)
 *
 * MODEL: java.sql.Date
 *
 * @author Basil Bourque.
 * 
 * This code is derived from the com.vaadin.data.util.converter.StringToDateConverter class bundled with Vaadin 7. 
 * That original class was published with this Copyright:
 * 
 * Copyright 2000-2014 Vaadin Ltd.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * 
 */
public class StringOfInstantToSqlTimestampConverter implements Converter<String , Timestamp>
{

    // Member vars.
    final org.slf4j.Logger logger = LoggerFactory.getLogger( this.getClass() );

    /**
     * Returns the format used by {@link #convertToPresentation(Date, Class,Locale)} and
     * {@link #convertToModel(String, Class, Locale)}.
     *
     * @param locale The locale to use
     *
     * @return A DateFormat instance
     */
    protected DateFormat getFormat ( Locale locale ) {
        if ( locale == null ) {
            locale = Locale.getDefault();
        }

        DateFormat f = DateFormat.getDateTimeInstance( DateFormat.MEDIUM , DateFormat.MEDIUM , locale );
        f.setLenient( false );
        return f;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.vaadin.data.util.converter.Converter#convertToModel(java.lang.Object, java.lang.Class, java.util.Locale)
     */
    @Override
    public Timestamp convertToModel ( String value , Class<? extends Timestamp> targetType , Locale locale )
            throws com.vaadin.data.util.converter.Converter.ConversionException {
        if ( targetType != getModelType() ) {
            throw new Converter.ConversionException( "Converter only supports "
                    + getModelType().getName() + " (targetType was "
                    + targetType.getName() + ")" );
        }

        if ( value == null ) {
            return null;
        }

        // Remove leading and trailing white space
        String trimmed = value.trim();

//        ParsePosition parsePosition = new ParsePosition( 0 );
//        Date parsedValue = this.getFormat( locale ).parse( trimmed , parsePosition );
//        if ( parsePosition.getIndex() != trimmed.length() ) {
//            throw new Converter.ConversionException( "Could not convert '" + trimmed + "' to " + getModelType().getName() );
//        }
        Instant instant = null;
        try {
            instant = Instant.parse( trimmed ); // Uses DateTimeFormatter.ISO_INSTANT.
        } catch ( DateTimeParseException e ) {
            throw new Converter.ConversionException( "Could not convert '" + trimmed + "' to java.time.Instant on the way to get " + getModelType().getName() );
        }
        if ( instant == null ) {
            logger.error( "The instant is null after parsing. Should not be possible. Message # ACE6DA4E-44C8-434C-A2AD-F946E5CFAEFD." );
            throw new Converter.ConversionException( "The Instant is null after parsing while attempting to convert '" + trimmed + "' to java.time.Instant on the way to get " + getModelType().getName() + "Message # 77A767AB-7D42-490F-9C2F-2775F4443A8D." );
        }
        Timestamp parsedValue = Timestamp.from( instant );

        return parsedValue;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.vaadin.data.util.converter.Converter#convertToPresentation(java.lang .Object, java.lang.Class,
     * java.util.Locale)
     */
    @Override
    public String convertToPresentation ( Timestamp value , Class<? extends String> targetType , Locale locale )
            throws com.vaadin.data.util.converter.Converter.ConversionException {
        if ( value == null ) {
            return null;
        }

        Instant instant = value.toInstant();
        String dateTimeStringInIsoFormat = instant.toString();   // Uses DateTimeFormatter.ISO_INSTANT.
        return dateTimeStringInIsoFormat;
        //return getFormat( locale ).format( value );
    }

    /*
     * (non-Javadoc)
     *
     * @see com.vaadin.data.util.converter.Converter#getModelType()
     */
    @Override
    public Class<Timestamp> getModelType () {
        return Timestamp.class;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.vaadin.data.util.converter.Converter#getPresentationType()
     */
    @Override
    public Class<String> getPresentationType () {
        return String.class;
    }

}