Vaadin converter for java.sql.Timestamp

有些话、适合烂在心里 提交于 2019-12-06 06:29:57
Krayo

I recommend this way:

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

with this converter:

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
    }

}

Do Not Think Of java.sql.Timestamp As Subclass Of java.util.Date

While java.sql.Timestamp does technically inherit from java.util.Date, the last paragraph of the class doc states that you should not consider it so, that you should ignore that fact.

Due to the differences between the Timestamp class and the java.util.Date class mentioned above, it is recommended that code not view Timestamp values generically as an instance of java.util.Date. The inheritance relationship between Timestamp and java.util.Date really denotes implementation inheritance, and not type inheritance.

In other words, this class structure is a clumsy hack. The date-time classes in early Java were rushed, and were not well thought-out.

One problem in converting is that java.sql.Timestamp has nanosecond resolution but java.util.Date has only milliseconds, so data will be lost.

Beware that Vaadin makes this mistake with a SQLContainer backing a Grid (and presumably a Table). The java.sql.Timestamp columns in the SQLContainer are treated as java.util.Date (superclass) thereby losing data if microseconds or nanoseconds were involved. (See my converter implementation below as workaround.)

java.time

The new java.time package in Java 8 and later is a re-think of those old classes, a do-over from scratch. These new classes are inspired by Joda-Time, defined by JSR 310, and extended by the ThreeTen-Extra project.

The java.time classes are built for nanosecond resolution. So no data loss when converting between java.time.Instant and java.sql.Timestamp.

Both the old and new date-time classes have methods for converting back and forth between old and new. Notice the java.sql.Timestamp::toInstant and Timestamp.from( instant ) methods called below.

Converter for String of java.time.Instant ↔ java.sql.Timestamp

This example may or may not help answer the Question. I am converting java.sql.Timestamp data for presentation as a String of java.time.Instant.

Besides textual format, remember there is one big difference between my converter and the default one: No data loss! (see discussion above) This example screenshot happens to not have any fractional seconds. But if it did have microseconds or nanoseconds, that data would be retained and displayed as need be.

Here is some code doing a String representation of an Instant converted from a java.sql.Timestamp. In other words:

  • Presentation: String (of an Instant using ISO_INSTANT formatter)
  • Model: java.sql.Timestamp

This code is modified from the source code of the com.vaadin.data.util.converter.StringToDateConverter class bundled with Vaadin 7.

This code is working for me in displaying a SQLContainer object’s java.sql.Timestamp columns in Vaadin 7.5.2 in a Grid. I've not yet tried Table but should work there too.

While working for me, this code may need some cleanup. In particular, (a) the comments may be wrong, and (b) I have calls to SLF4J logging that you may need to alter/remove.

The default ISO 8601 format is a bit hard to read for humans, so I am working an alternatives for changing the format and for adjusting the time zone.

To use this class, you must call setConverter on a Grid.Column object. Or configure a ConverterFactory as described in 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;
    }

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!