views:

54

answers:

2

Using the EclipseLink JPA2 implementation (not sure if it's the same with the Hibernate implementation)

I have a simple structure where an Organization entity has contracts. Here's the sql I exported from postgres to create the Organization

CREATE TABLE organization (
    key bigint NOT NULL,
    version integer
);

If I specify the Contract side of the Organization like this:

@ManyToOne( optional=false )
@JoinColumn( name="organization_key", referencedColumnName="key" )
private Organization organization;

and then dump the schema I get this.

CREATE TABLE contract ( 
    key bigint NOT NULL, 
    version integer, 
    organization_key character varying(255), 
);

It doesn't make sense to me that it would use a character varying(255) field for the reference to the organization key. I know I can use the columnDefinition as follows:

@ManyToOne( optional=false )
@JoinColumn( name="organization_key", referencedColumnName="key", columnDefinition="bigint NOT NULL" )
private Organization organization;

to get the bigint type instead of the character type.

Is it unrealistic for me to expect it to get the correct column type? Am I using it wrong, or do I have the wrong expectations? Is it expected for me to have to use the columnDefinition each time?

Update: Here's the relevant info from the Organization entity

@Entity
@Table( name = "organization" )
@SequenceGenerator( name = "ORGANIZATION_SEQ_GEN", sequenceName = "ORGANIZATION_SEQUENCE" )
public class Organization
        implements DataObject<Long>
{

    /**
     * key for this instance. Should be managed by JPA provider.
     */
    @Id
    @GeneratedValue( strategy = GenerationType.SEQUENCE, generator = "ORGANIZATION_SEQ_GEN" )
    private Long key;

    /**
     * JPA version column
     */
    @Version
    protected int version;

    /**
     * All contracts for this organization
     */
    @OneToMany(mappedBy="organization" )
    @OrderBy( "endDate DESC" )
    private List<Contract> contracts;

    ... getters and setters        
 }

And here's the Contract entity

@Entity
@Table( name = "contract" )
@SequenceGenerator( name = "CONTRACT_SEQ_GEN", sequenceName = "CONTRACT_SEQUENCE" )
public class Contract
        implements DataObject<Long>
{

    /**
     * key for this instance. Should be managed by JPA provider.
     */
    @Id
    @GeneratedValue( strategy = GenerationType.SEQUENCE, generator = "CONTRACT_SEQ_GEN" )
    private Long key;

    /**
     * Organization that owns this contract, required.
     */
    @ManyToOne( optional=false )
    @JoinColumn( name="organization_key", referencedColumnName="key" )
    private Organization organization;

    /**
     * JPA version column
     */
    @Version
    protected int version;

    ... getters and setters
}
+2  A: 

Is it unrealistic for me to expect it to get the correct column type?

No, it's not unrealistic and the current result is clearly unexpected.

Am I using it wrong, or do I have the wrong expectations?

Your mappings doesn't look wrong. Could you just try the following and confirm that you get the same result (I'm simply omitting the referencedColumnName that you should not have to define anyway)?

@Entity
@Table( name = "contract" )
@SequenceGenerator( name = "CONTRACT_SEQ_GEN", sequenceName = "CONTRACT_SEQUENCE" )
public class Contract implements DataObject<Long> {
    @Id
    @GeneratedValue( strategy = GenerationType.SEQUENCE, generator = "CONTRACT_SEQ_GEN" )
    private Long key;
    ...
    @ManyToOne( optional=false )
    @JoinColumn( name="organization_key" )
    private Organization organization;
    ...
}

I don't have PostgreSQL installed, can't try myself.

Is it expected for me to have to use the columnDefinition each time?

No.

Pascal Thivent
updated with the relevant info from the two entities.
digitaljoel
@digitaljoel: See my update. BTW, did you define a `eclipselink.target-database` property in your `persistence.xml`? And what is the value of `eclipselink.ddl-generation`?
Pascal Thivent
Thanks for the help on this Pascal. <property name="eclipselink.target-database" value="org.eclipse.persistence.platform.database.PostgreSQLPlatform" />and my ddl-gneration is drop-and-create-tables while I'm messing with this stuff. Once I get it worked out, I just comment that out altogether. Removing the referencedColumnName now...
digitaljoel
interesting, with this: @ManyToOne( optional=false ) @JoinColumn( name="organization_key" ) private Organization organization;I get this:CREATE TABLE contract ( key bigint NOT NULL, version integer, organization_key bigint,);looks like you've solved my problem!
digitaljoel
@digitaljoel: Glad it's working without the `referencedColumnName` defined, although I find the behavior pretty weird when it is. Your mapping wasn't wrong IMO, sounds like a bug.
Pascal Thivent
I agree. It doesn't seem that having the referenced column, even if it is redundant, should cause it to map like that. Thanks for the ideas and quick response, I appreciate it.
digitaljoel
Actually, it sounds like a feature. See James answer.
Pascal Thivent
+2  A: 

I believe the issue is one of case sensitivity.

You did not set a @Column on the key id attribute, so the default column name is "KEY". In your @ManyToOne you referenced "key", which is not the same column, so EclipseLink (which is case sensitive and supports non Id foreign key references) assumed this was a different column, and one that it did not know about, so gave it the default type of VARCHAR.

Either change the referencedColumnName to "KEY" or remove it as it is not required when referencing a singleton Id.

It would be worthwhile to log a bug on EclipseLink that a warning should be logged when a column reference is not found, or has the wrong case (maybe even switch the case automatically). Actually we might be logging a warning already, you may wish to check your log.

James
Ahhh, makes sense. I didn't know EL was case sensitive. Thanks for throwing some light on that.
Pascal Thivent