Search code examples
javajpaeclipselinkfirebirdjaybird

Jaybird with JPA on UUID field (char(16) character set octets)


I have problem with UUID ID fields defined by domain

CREATE DOMAIN OCTET16 AS CHAR(16) CHARACTER SET OCTETS;

I have defined AttributeConverter for jpa as import java.util.UUID;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

import com.ekser.nakkash.icdv.tools.UUIDTools;

@Converter
public class UUIDAttributeConverter implements AttributeConverter<UUID, byte[]> {
    @Override
    public byte[] convertToDatabaseColumn(UUID arg0) {
        return UUIDTools.asBytes((UUID) arg0);
    }
    @Override
    public UUID convertToEntityAttribute(byte[] arg0) {
        return UUIDTools.asUUID((byte[])arg0);
    }
}

and defined jpa fields as

@Id
@Convert(converter = UUIDAttributeConverter.class)
private UUID id;

but jpa (Eclipselink+Eclipse Gemini) gives error as it tries to convert not binary presentation but textual form of 36 character

I suppose problem is that jaybird gives type of field as CHAR(16), (I have checked with ResultSetMetaData), I know that there is option octetsAsBytes, I have used it with jbdc url like

jdbc:firebirdsql:localhost:D:/aktarma-12-13/VERI.FDB?octetsAsBytes=true

with no result.

My setup is pretty complicated

Java 1.8
Efxclipse 2.4.0 RCP
Firebird 2.5.6
Jaybird 2.2.12 JDK_1.8
HikariCP 2.4.1
EclipseLink 2.6.4
Eclipse Gemini 1.2.0.M1

So I think when ResultSetMetaData will give me type of field as BINARY the problem will gone. But how? Any suggestions.

I just not want to hold ids as char(32) or char(36).

Edit: After switching to Jaybird 3.beta-2, one can clearly see on third line invalid stream header: F1505533. This is a part ASCII decoded id (char(16) character set octets). How to make JPA to accept this value as raw byte and make it pass to UUIDAttributeConverter class??!!

Exception in thread "JavaFX Application Thread" javax.persistence.PersistenceException: Exception [EclipseLink-66] (Eclipse Persistence Services - 2.6.4.v20160829-44060b6): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Could not deserialize object from byte array.
Internal Exception: java.lang.RuntimeException: java.io.StreamCorruptedException: invalid stream header: F1505533
Mapping: org.eclipse.persistence.mappings.DirectToFieldMapping[id-->YAZDIRMA_TURLERI.ID]
Descriptor: RelationalDescriptor(com.ekser.nakkash.icdv.pojo.YazdirmaTuru --> [DatabaseTable(YAZDIRMA_TURLERI)])
    at org.eclipse.persistence.internal.jpa.QueryImpl.getResultList(QueryImpl.java:484)
    at com.ekser.nakkash.icdv.gui.MainPartIcdv.lambda$1(MainPartIcdv.java:180)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Node.fireEvent(Node.java:8413)
    at javafx.scene.control.Button.fire(Button.java:185)
    at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:182)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:96)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89)
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
    at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
    at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:380)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:294)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$354(GlassViewEventHandler.java:416)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:415)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
    at com.sun.glass.ui.View.notifyMouse(View.java:937)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
    at java.lang.Thread.run(Unknown Source)
Caused by: Exception [EclipseLink-66] (Eclipse Persistence Services - 2.6.4.v20160829-44060b6): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Could not deserialize object from byte array.
Internal Exception: java.lang.RuntimeException: java.io.StreamCorruptedException: invalid stream header: F1505533
Mapping: org.eclipse.persistence.mappings.DirectToFieldMapping[id-->YAZDIRMA_TURLERI.ID]
Descriptor: RelationalDescriptor(com.ekser.nakkash.icdv.pojo.YazdirmaTuru --> [DatabaseTable(YAZDIRMA_TURLERI)])
    at org.eclipse.persistence.exceptions.DescriptorException.notDeserializable(DescriptorException.java:1232)
    at org.eclipse.persistence.mappings.converters.SerializedObjectConverter.convertDataValueToObjectValue(SerializedObjectConverter.java:144)
    at org.eclipse.persistence.mappings.foundation.AbstractDirectMapping.getObjectValue(AbstractDirectMapping.java:616)
    at org.eclipse.persistence.mappings.foundation.AbstractDirectMapping.valueFromRow(AbstractDirectMapping.java:1220)
    at org.eclipse.persistence.mappings.DatabaseMapping.readFromRowIntoObject(DatabaseMapping.java:1539)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildAttributesIntoObject(ObjectBuilder.java:462)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:1005)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildWorkingCopyCloneNormally(ObjectBuilder.java:899)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObjectInUnitOfWork(ObjectBuilder.java:852)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:735)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:689)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.buildObject(ObjectLevelReadQuery.java:805)
    at org.eclipse.persistence.queries.ReadAllQuery.registerResultInUnitOfWork(ReadAllQuery.java:962)
    at org.eclipse.persistence.queries.ReadAllQuery.executeObjectLevelReadQuery(ReadAllQuery.java:573)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1175)
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:904)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1134)
    at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:460)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1222)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1857)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1839)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1804)
    at org.eclipse.persistence.internal.jpa.QueryImpl.executeReadQuery(QueryImpl.java:258)
    at org.eclipse.persistence.internal.jpa.QueryImpl.getResultList(QueryImpl.java:473)
    ... 56 more
Caused by: java.lang.RuntimeException: java.io.StreamCorruptedException: invalid stream header: F1505533
    at org.eclipse.persistence.sessions.serializers.JavaSerializer.deserialize(JavaSerializer.java:57)
    at org.eclipse.persistence.mappings.converters.SerializedObjectConverter.convertDataValueToObjectValue(SerializedObjectConverter.java:142)
    ... 79 more
Caused by: java.io.StreamCorruptedException: invalid stream header: F1505533
    at java.io.ObjectInputStream.readStreamHeader(Unknown Source)
    at java.io.ObjectInputStream.<init>(Unknown Source)
    at org.eclipse.persistence.internal.helper.CustomObjectInputStream.<init>(CustomObjectInputStream.java:37)
    at org.eclipse.persistence.sessions.serializers.JavaSerializer.deserialize(JavaSerializer.java:53)
    ... 80 more

Solution

  • I have made a simple project using eclipselink 2.6.4 to test this, and both with Jaybird 2.2.12 (setting octetsAsBytes=true is required) and Jaybird 3.0.0-beta-2 it works:

    Code also on: https://gist.github.com/mrotteveel/273aa9e836880211820f54ff21164ec1

    Todo Entity:

    package com.example.eclipselink.entity;
    
    import com.example.eclipselink.converter.UUIDAttributeConverter;
    
    import javax.persistence.Convert;
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import java.util.UUID;
    
    @Entity
    public class Todo {
    
        @Id
        @Convert(converter = UUIDAttributeConverter.class)
        private UUID id;
    
        public UUID getId() {
            return id;
        }
    
        public void setId(UUID id) {
            this.id = id;
        }
    }
    

    UUIDAttributeConverter:

    package com.example.eclipselink.converter;
    
    import javax.persistence.AttributeConverter;
    import javax.persistence.Converter;
    import java.nio.ByteBuffer;
    import java.util.UUID;
    
    @Converter
    public class UUIDAttributeConverter implements AttributeConverter<UUID, byte[]> {
    
        @Override
        public byte[] convertToDatabaseColumn(UUID uuid) {
            if (uuid == null) return null;
            byte[] buffer = new byte[16];
            ByteBuffer bb = ByteBuffer.wrap(buffer);
            bb.putLong(uuid.getMostSignificantBits());
            bb.putLong(uuid.getLeastSignificantBits());
            return buffer;
        }
    
        @Override
        public UUID convertToEntityAttribute(byte[] bytes) {
            if (bytes == null) return null;
            ByteBuffer bb = ByteBuffer.wrap(bytes);
            long high = bb.getLong();
            long low = bb.getLong();
            return new UUID(high, low);
        }
    }
    

    Main:

    package com.example.eclipselink;
    
    import com.example.eclipselink.entity.Todo;
    
    import javax.persistence.EntityManager;
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.Persistence;
    import javax.persistence.Query;
    import java.util.List;
    import java.util.UUID;
    
    public class Main {
        private static final String PERSISTENCE_UNIT_NAME = "todos";
        private static EntityManagerFactory factory;
    
        public static void main(String[] args) {
            factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
            EntityManager em = factory.createEntityManager();
            // read the existing entries and write to console
            Query q = em.createQuery("select t from Todo t");
            List<Todo> todoList = q.getResultList();
            for (Todo todo : todoList) {
                System.out.println(todo.getId());
            }
            System.out.println("Size: " + todoList.size());
    
            // create new todo
            em.getTransaction().begin();
            Todo todo = new Todo();
            todo.setId(UUID.randomUUID());
            em.persist(todo);
            em.getTransaction().commit();
    
            em.close();
        }
    }
    

    Persistence context:

    <?xml version="1.0" encoding="UTF-8" ?>
    <persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
                 version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
        <persistence-unit name="todos" transaction-type="RESOURCE_LOCAL">
            <class>com.example.eclipselink.entity.Todo</class>
            <class>com.example.eclipselink.converter.UUIDAttributeConverter</class>
            <properties>
                <property name="javax.persistence.jdbc.driver" value="org.firebirdsql.jdbc.FBDriver" />
                <property name="javax.persistence.jdbc.url"
                          value="jdbc:firebirdsql://localhost/d:/data/db/fb3/eclipselink.fdb?charSet=UTF-8&amp;octetsAsBytes=true" />
                <property name="javax.persistence.jdbc.user" value="sysdba" />
                <property name="javax.persistence.jdbc.password" value="masterkey" />
            </properties>
    
        </persistence-unit>
    </persistence>
    

    DDL:

    CREATE TABLE TODO
    (
      ID char(16) CHARACTER SET OCTETS NOT NULL,
      CONSTRAINT PK_TODO PRIMARY KEY (ID)
    );
    

    Output after a few runs:

    [EL Info]: 2017-01-13 15:59:14.733--ServerSession(1139700454)--EclipseLink, version: Eclipse Persistence Services - 2.6.4.v20160829-44060b6
    [EL Info]: connection: 2017-01-13 15:59:15.093--ServerSession(1139700454)--/file:/D:/Development/project/eclipselink/target/classes/_todos login successful
    4c062d69-849e-4946-8e25-edfc5d7441be
    dac10396-cfe2-4fb0-b048-65f954a82da5
    8dab770e-ebd2-4ebc-a29b-4b8aae0b449a
    9d376c67-fdc5-4e21-8013-f71cde5119aa
    Size: 4