Whome
Whome

Reputation: 10400

OpenJPA and AttributeConverter beween Calendar and mysql DATETIME not working

OpenJPA3.2.2 supports AttributeConverter but I always get a cast error. class java.lang.String cannot be cast to class java.util.Calendar (java.lang.String and java.util.Calendar are in module java.base of loader 'bootstrap')

I hava tried String, java.sql.Timestamp, java.util.Date "db type" but OpenJPA gives a cast error. What is the type I should use for Mysql DATETIME in an attribute converter?

Java bean must use Calendar field for legacy reason, this is an old codebase used by several apps. If not using @Convert annotation in a field then calendar is saved to a database, but I need to force calendar value to a utc timezoned yyyy-MM-dd HH:mm:ss.

@Entity @Table(name="service") @Access(AccessType.FIELD)
public class Service {
 ...
    @Column(nullable=false) 
    @Convert(converter=JPAConverter.class)
    private Calendar updated;
}

CREATE TABLE IF NOT EXISTS service (
  id bigint UNSIGNED NOT NULL default '0',
  updated datetime NOT NULL,
  PRIMARY KEY (id)
) ENGINE=InnoDB;

@Converter(autoApply=false)
public class JPAConverter implements AttributeConverter<Calendar, String> {
        @Override
        public String convertToDatabaseColumn(Calendar val) {
            if(val==null) return null;
            return DateUtil.formatDateTime(val, DateUtil.TIMEZONE_UTC);
        }

        @Override
        public Calendar convertToEntityAttribute(String dbVal) {
            if(dbVal==null) return null;
            return DateUtil.parseDateTimeFromUTC(dbVal);
        }
}

Stackrace is

Caused by: java.lang.ClassCastException: class java.lang.String cannot be cast to class java.util.Calendar (java.lang.String and java.util.Calendar are in module java.base of loader 'bootstrap')
    at org.apache.openjpa.jdbc.sql.DBDictionary.setTyped(DBDictionary.java:1551)
    at org.apache.openjpa.jdbc.sql.RowImpl.flush(RowImpl.java:1002)
    at org.apache.openjpa.jdbc.sql.RowImpl.flush(RowImpl.java:962)
    at org.apache.openjpa.jdbc.kernel.PreparedStatementManagerImpl.flushAndUpdate(PreparedStatementManagerImpl.java:119)
    at org.apache.openjpa.jdbc.kernel.BatchingPreparedStatementManagerImpl.flushAndUpdate(BatchingPreparedStatementManagerImpl.java:80)
    at org.apache.openjpa.jdbc.kernel.PreparedStatementManagerImpl.flushInternal(PreparedStatementManagerImpl.java:102)
    at org.apache.openjpa.jdbc.kernel.PreparedStatementManagerImpl.flush(PreparedStatementManagerImpl.java:90)
    at org.apache.openjpa.jdbc.kernel.ConstraintUpdateManager.flush(ConstraintUpdateManager.java:555)
    at org.apache.openjpa.jdbc.kernel.ConstraintUpdateManager.flush(ConstraintUpdateManager.java:109)
    at org.apache.openjpa.jdbc.kernel.BatchingConstraintUpdateManager.flush(BatchingConstraintUpdateManager.java:61)
    at org.apache.openjpa.jdbc.kernel.AbstractUpdateManager.flush(AbstractUpdateManager.java:109)
    at org.apache.openjpa.jdbc.kernel.AbstractUpdateManager.flush(AbstractUpdateManager.java:81)
    at org.apache.openjpa.jdbc.kernel.JDBCStoreManager.flush(JDBCStoreManager.java:755)
    at org.apache.openjpa.kernel.DelegatingStoreManager.flush(DelegatingStoreManager.java:146)
    at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:2310)
    at org.apache.openjpa.kernel.BrokerImpl.flushSafe(BrokerImpl.java:2201)
    at org.apache.openjpa.kernel.BrokerImpl.beforeCompletion(BrokerImpl.java:2118)
    ... 55 more

Upvotes: 0

Views: 56

Answers (1)

Whome
Whome

Reputation: 10400

I had to give up with AttributeConverter and use an openjpa specific functions. Maybe things worked better with OpenJPA4.x library but I don't want to do javax.* to jakarta.* package name change now.

import org.apache.openjpa.persistence.Externalizer;
import org.apache.openjpa.persistence.Factory;

@Factory("JPAUtil.db2calendar") // utc-to-calendar
@Externalizer("JPAUtil.calendar2db")
@Column(nullable=false) @Temporal(TemporalType.TIMESTAMP)
private Calendar created;

public class JPAUtil {
    private static DateTimeFormatter calFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneOffset.UTC);

    public static String calendar2db(Calendar val, StoreContext ctx) {
        if(val==null) return null;
        return calFormat.format(val.toInstant());
    }

    public static Calendar db2calendar(String val, StoreContext ctx) {
        if(val==null) return null;
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis( Instant.from(calFormat.parse(val)).toEpochMilli() );
        return cal;
    }
}

Upvotes: 0

Related Questions