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 223 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: 1389
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 messageGo to next 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.
Re: Using a @Converter on (nullable) foreign key fields [message #1863316 is a reply to message #1719780] Thu, 25 January 2024 17:02 Go to previous message
Adam Saghy is currently offline Adam SaghyFriend
Messages: 1
Registered: January 2024
Junior Member
Hi David,

Configuring SqlType, TypeName and column definition sounds a good idea.

Let me see whether i can come up with something that works with everything!

Regards,
Adam

[Updated on: Thu, 25 January 2024 19:39]

Report message to a moderator

Previous Topic:Recommended way to model money (jsr354) and query by amount or by currency ?
Next Topic:few questions on RepeatableWriteUnitOfWork / IsolatedCache
Goto Forum:
  


Current Time: Fri Mar 29 00:10:17 GMT 2024

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

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

Back to the top