Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » EclipseLink » Using a @Converter on (nullable) foreign key fields
Using a @Converter on (nullable) foreign key fields [message #1719448] Fri, 08 January 2016 04:42 Go to next message
David Wheeler is currently offline David WheelerFriend
Messages: 6
Registered: November 2015
Junior Member
We use UUIDs as primary keys for some of our tables, and consequently we've set up an eclipselink converter to handle the conversion to/from postgres uuid.

However, we've recently come across an issue that we've been unable to resolve, which is where we have a uni-directional one to one reference to a uuid-keyed entity. The problem is that eclipselink doesn't use the converter correctly when the value is null.

In the case of bi directional one to one references, we have made our converter traverse the reference and update the remote descriptor's mapping to use uuids, but we can't work out how we could do that in the uni-directional case.

Is there any way to update the converter to find the uni-directional fields that reference our uuid key and update their type to be UUID?

Sample code to reproduce issue is attached.


DB: Postgresql
Version: 2.6.1.v20150605-31e8258
Env: Glassfish 4.1.1

Caused by: org.postgresql.util.PSQLException: ERROR: column "testsibling" is of type uuid but expression is of type character varying
  Hint: You will need to rewrite or cast the expression.
  Position: 62


public class UUIDConverter implements org.eclipse.persistence.mappings.converters.Converter
{
  private static final Logger LOG = Logger.getLogger(UUIDConverter.class.getName());

  @Override
  public UUID convertObjectValueToDataValue(Object objectValue, Session session)
  {
    return (UUID) objectValue;
  }

  @Override
  public UUID convertDataValueToObjectValue(Object dataValue, Session session)
  {
    return (UUID) dataValue;
  }

  @Override
  public boolean isMutable()
  {
    return false;
  }

  @Override
  public void initialize(DatabaseMapping mapping, Session session)
  {
    DatabaseField field = mapping.getField();
    field.setSqlType(Types.OTHER);
    field.setTypeName("java.util.UUID");
    field.setColumnDefinition("UUID");


    if (LOG.isLoggable(Level.FINE))
      LOG.log(Level.FINE, "Setting local field " + field + " to be a UUID");

    // Find the bi-directional references other objects have to this field, and update them to be uuid too
    for (DatabaseMapping m : mapping.getDescriptor().getMappings())
    {
      if (m instanceof OneToOneMapping)
      {
        OneToOneMapping oneToOneMapping = (OneToOneMapping) m;
        
        DatabaseField relationshipField = oneToOneMapping.getSourceToTargetKeyFields().get(field);
        if (relationshipField != null)
        {
          relationshipField.setSqlType(Types.OTHER);
          relationshipField.setColumnDefinition("UUID");
          relationshipField.setTypeName("java.util.UUID");
          if (LOG.isLoggable(Level.FINE))
            LOG.log(Level.FINE, "Setting foreign key field " + relationshipField + " to be a UUID");
        }
      }
    }
  }
}


@Entity
@NamedQuery(name="Test.findByField1", query="SELECT T from Test T where T.field1 = :field1")
public class Test
{

  @Id
  @Converter(converterClass=UUIDConverter.class, name="test_uuid")
  @Convert("test_uuid")
  @Column(name="test")
  private UUID test = UUID.randomUUID();
  
  private String field1;

  @OneToOne(optional=true)
  @JoinColumn(name="testsibling")
  private TestSibling testSibling;

}


@Entity
public class TestSibling
{

  @Id
  @Converter(converterClass=UUIDConverter.class, name="testsibling_uuid")
  @Convert("testsibling_uuid")
  @Column(name="testsibling")
  private UUID testSibling = UUID.randomUUID();
  
  private String field2;

//  @OneToOne(mappedBy="testSibling")
//  private Test test;

 }


  @Schedule(second="*/5", minute="*", hour="*")
  @Timeout
  public void start()
  {
    Test test = new Test();
    test.setField1(Integer.toString(new Random().nextInt()));
    em.persist(test);
    
    LOG.info("Victory");
  }

Full sample code attached
  • Attachment: jpatest.zip
    (Size: 7.03KB, Downloaded 79 times)
Re: Using a @Converter on (nullable) foreign key fields [message #1719530 is a reply to message #1719448] Fri, 08 January 2016 16:24 Go to previous messageGo to next message
Chris Delahunt is currently offline Chris DelahuntFriend
Messages: 1300
Registered: July 2009
Senior Member
Seems like you are on the right track, but are missing a few use cases. You need to add a customizer that does pretty much the same thing as your converter's initialize method; on any mapping where the target's field is a UUID, the source field needs to be changed. This customizer then needs to be set on any class that references a class that has a UUID - or it can just be a session customizer that goes through all descriptors. Assuming the converter's initialize changes are run before customizers, this would then cover all relationships to your class with a UUID (both bi and uni directional cases), but you'll have to check that it is run first.

You then still might want to cover the unidirectional OneToMany case - if the UUID class has unidirectional OneToMany to other classes. If you wanted, this could be handled in your converter's initialize method which now is only needed to check if any of its mappings are an instance of UnidirectionalOneToManyMapping, as OneToOneMapping handling is already done by the target descriptor customizations mentioned previously.

Hope this helps.

Regards,
Chris
Re: Using a @Converter on (nullable) foreign key fields [message #1719780 is a reply to message #1719530] Tue, 12 January 2016 01:00 Go to previous messageGo to next message
David Wheeler is currently offline David WheelerFriend
Messages: 6
Registered: November 2015
Junior Member
Thanks for your help Chris.

Using a customizer seems a bit complex for what appears to be a simple requirement - using UUIDs.

What we have decided to do in the end is have PostgreSQL know how to convert text to uuid to resolve the one known problem with simple converter which is when postgres attempts to fill in 'null' as a text field rather than a uuid if an optional reference is left null.

    drop cast if exists (character varying as uuid);
    create or replace function uuid(_text character varying) returns uuid language sql as 'select uuid_in(_text::cstring)';
    create cast (character varying as uuid) with function uuid(character varying) as assignment;


This seems to be working for our needs, along with the simplified converter

public class UUIDConverter implements org.eclipse.persistence.mappings.converters.Converter
{
  private static final long serialVersionUID = 1L;
  
  @Override
  public UUID convertObjectValueToDataValue(Object objectValue, Session session)
  {
    return (UUID) objectValue;
  }

  @Override
  public UUID convertDataValueToObjectValue(Object dataValue, Session session)
  {
    return (UUID) dataValue;
  }

  @Override
  public boolean isMutable()
  {
    return false;
  }

  @Override
  public void initialize(DatabaseMapping mapping, Session session)
  {
    DatabaseField field = mapping.getField();
    field.setSqlType(Types.OTHER);
    field.setTypeName("java.util.UUID");
    field.setColumnDefinition("UUID");
  }


It seems to me that it's an EclipseLink bug that it doesn't treat the references to converted fields as converted also, but I notice that the JPA 2.1 attribute converter explicitly disallows use on @Id fields. Is there some complexity that makes this difficult? Should I raise a bug?
Re: Using a @Converter on (nullable) foreign key fields [message #1787489 is a reply to message #1719780] Sat, 26 May 2018 14:41 Go to previous message
Shimizu Ogawa is currently offline Shimizu OgawaFriend
Messages: 1
Registered: May 2018
Junior Member
Hi David,

Thank you very much for that cast idea, it saved my day!

It really looks like Postgresql/Eclipselink bug/gap and is not fixed yet so we need such kinds of workarounds. This will be the second "magical trick" in my solution, first one was the similar cast for jsonb type.

Please let me know if by chance you have found more elegant solution.
Previous Topic:Eclipselink with JTA
Next Topic:Stuck thread when EJBQueryImpl.getSingleResult
Goto Forum:
  


Current Time: Fri Sep 21 20:01:59 GMT 2018

Powered by FUDForum. Page generated in 0.01666 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top