【问题标题】:Vaadin converter for java.sql.Timestamp用于 java.sql.Timestamp 的 Vaadin 转换器
【发布时间】:2014-10-24 11:43:38
【问题描述】:

我正在使用 PostgreSQL 数据库,并且时间戳列具有 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

我在哪里可以获得合适的转换器?感谢您的建议。

【问题讨论】:

  • Postgres 表中的data type of the source column究竟是什么?默认情况下应该已经将时间戳获取到 DateField。
  • 数据类型是时间戳。所以我想知道在 vaadin 中没有合适的转换器来处理这种应该在许多应用程序中编辑的普通数据类型。

标签: postgresql timestamp converter vaadin datefield


【解决方案1】:

我推荐这种方式:

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
    }

}

【讨论】:

  • 它有效,但还有一个问题:如何更改时间部分的显示/编辑格式?我想在小时和分钟之间使用冒号。默认设置使用点。
  • @Hink 试试这个:pdf.setDateFormat("yyyy.MM.dd. HH:mm:ss");
【解决方案2】:

不要认为java.sql.Timestampjava.util.Date 的子类

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

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

换句话说,这个类结构是一个笨拙的 hack。早期 Java 中的日期时间类很仓促,没有经过深思熟虑。

转换的一个问题是java.sql.Timestamp有纳秒的分辨率,而java.util.Date只有毫秒,所以数据会丢失。

请注意,Vaadin 在 SQLContainer 支持 Grid(并且可能是 Table)时犯了这个错误。 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 的字符串。

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

这里有一些代码对从 java.sql.Timestamp 转换而来的 Instant 进行字符串表示。换句话说:

  • Presentation: 字符串(使用ISO_INSTANT 格式化程序的即时)
  • 型号: java.sql.Timestamp

此代码是从与 Vaadin 7 捆绑的 com.vaadin.data.util.converter.StringToDateConverter 类的 source code 修改而来的。

这段代码对我有用,可以在 Grid 中的 Vaadin 7.5.2 中显示 SQLContainer 对象的 java.sql.Timestamp 列。我还没有尝试过Table,但也应该在那里工作。

在为我工作时,此代码可能需要进行一些清理。特别是,(a) cmets 可能是错误的,并且 (b) 我已经调用了 SLF4J 日志记录,您可能需要更改/删除。

默认的ISO 8601 格式对人类来说有点难以阅读,所以我正在寻找一种替代方法来更改格式和调整时区。

要使用此类,您必须在 Grid.Column 对象上调用 setConverter。或者按照this wiki page 中的说明配置ConverterFactory

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

}

【讨论】:

    猜你喜欢
    • 2013-01-27
    • 2012-09-12
    • 1970-01-01
    • 2012-06-27
    • 2012-10-27
    • 2013-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多