Hibernate Books

All times are UTC - 5 hours [ DST ]



Post new topic Reply to topic  [ 4 posts ] 
Author Message
 Post subject: Non-primary key @JoinColumn no longer working?
PostPosted: Thu Aug 10, 2017 8:29 am 
Beginner
Beginner

Joined: Thu Jan 05, 2017 1:47 pm
Posts: 27
In migrating to Hibernate 5.x from 4.x we found something that broke in our application. We're using @JoinColumn to specify a non-primary key in a @ManyToOne association. In researching this a bit it seems to be an optional part of the JPA 2.0 spec, though 4.x seemed to support it.

I'm curious whether this would be considered a bug, or whether the removal of support was intentional.

In 5.x it seems to create the schema as expected and is able to store an association correctly. Where it breaks is in setting the association to null; on tx commit we get the following exception:

Code:
javax.persistence.PersistenceException: org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private long com.nokia.nspos.persistence.impl.db.entities.CompositeId.lsb] by reflection for persistent property [com.nokia.nspos.persistence.impl.db.entities.CompositeId#lsb] : com.nokia.nspos.persistence.impl.db.entities.Referee@41c3069e
   at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1692)
   at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1602)
   at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:68)
   ... 52 more
Caused by: org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private long com.nokia.nspos.persistence.impl.db.entities.CompositeId.lsb] by reflection for persistent property [com.nokia.nspos.persistence.impl.db.entities.CompositeId#lsb] : com.nokia.nspos.persistence.impl.db.entities.Referee@41c3069e
   at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:43)
   at org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValue(AbstractComponentTuplizer.java:58)
   at org.hibernate.type.ComponentType.getPropertyValue(ComponentType.java:414)
   at org.hibernate.type.ComponentType.isDirty(ComponentType.java:254)
   at org.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:282)
   at org.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:291)
   at org.hibernate.type.TypeHelper.findDirty(TypeHelper.java:296)
   at org.hibernate.persister.entity.AbstractEntityPersister.findDirty(AbstractEntityPersister.java:4204)
   at org.hibernate.event.internal.DefaultFlushEntityEventListener.dirtyCheck(DefaultFlushEntityEventListener.java:528)
   at org.hibernate.event.internal.DefaultFlushEntityEventListener.isUpdateNecessary(DefaultFlushEntityEventListener.java:215)
   at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:142)
   at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:216)
   at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:85)
   at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:38)
   at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1295)
   at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:468)
   at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3135)
   at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2352)
   at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:491)
   at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:147)
   at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38)
   at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:231)
   at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:65)
   at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:61)
   ... 52 more
Caused by: java.lang.IllegalArgumentException: Can not set long field com.nokia.nspos.persistence.impl.db.entities.CompositeId.lsb to com.nokia.nspos.persistence.impl.db.entities.Referee
   at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
   at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
   at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
   at sun.reflect.UnsafeLongFieldAccessorImpl.getLong(UnsafeLongFieldAccessorImpl.java:60)
   at sun.reflect.UnsafeLongFieldAccessorImpl.get(UnsafeLongFieldAccessorImpl.java:36)
   at java.lang.reflect.Field.get(Field.java:393)
   at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:39)
   ... 75 more


Here's the object model I created to reproduce it in a unit test. It's simplified as much as possible

Code:
@Embeddable
public class CompositeId implements Serializable {
    @Column(nullable = false)
    private long lsb;

    @Column(nullable = false)
    private long msb;

    public CompositeId() {
    }

    public CompositeId(long lsb, long msb) {
        this.lsb = lsb;
        this.msb = msb;
    }

    public long getLsb() {
        return lsb;
    }

    public void setLsb(long lsb) {
        this.lsb = lsb;
    }

    public long getMsb() {
        return msb;
    }

    public void setMsb(long msb) {
        this.msb = msb;
    }
}


Code:
@Entity(name = "TestReferee")
public class Referee implements Serializable {
    @EmbeddedId
    private CompositeId id;

    @Column(unique = true, nullable = false)
    private String stringId;

    public Referee(CompositeId id) {
        this.id = id;
    }

    public Referee() {
    }

    public CompositeId getId() {
        return id;
    }

    public void setId(CompositeId id) {
        this.id = id;
    }

    public String getStringId() {
        return stringId;
    }

    public void setStringId(String stringId) {
        this.stringId = stringId;
    }
}


Code:
@Entity(name = "TestReferer")
public class Referer implements Serializable {

    @Id
    private long id;

    private String otherField;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "referee", referencedColumnName = "stringId")
    private Referee referee;

    public Referer() {
    }

    public Referer(long id) {

        this.id = id;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getOtherField() {
        return otherField;
    }

    public void setOtherField(String otherField) {
        this.otherField = otherField;
    }

    public Referee getReferee() {
        return referee;
    }

    public void setReferee(Referee referee) {
        this.referee = referee;
    }
}


Top
 Profile  
 
 Post subject: Re: Non-primary key @joincolumn no longer works?
PostPosted: Thu Aug 10, 2017 8:48 am 
Beginner
Beginner

Joined: Thu Jan 05, 2017 1:47 pm
Posts: 27
I should mention that this is with Hibernate 5.1.4. The 4.x release was 4.3.8.


Top
 Profile  
 
 Post subject: Re: Non-primary key @joincolumn no longer works?
PostPosted: Sun Aug 13, 2017 2:27 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1530
Quote:
In researching this a bit it seems to be an optional part of the JPA 2.0 spec, though 4.x seemed to support it.


@JoinColumn is optional since it implies the entity identifier (PK) on the other end as well as a ${property.name}_id column name for the FK. However, you can override the default convention using @JoinColumn.

However, it's not the @JoinColumn that causes you this issue.

Code:
Caused by: java.lang.IllegalArgumentException: Can not set long field com.nokia.nspos.persistence.impl.db.entities.CompositeId.lsb to com.nokia.nspos.persistence.impl.db.entities.Referee


Maybe you have a null value that's saved as long. Try changing the attribute types to Long instead.

_________________
If you liked my answer, you are going to love my High-Performance Java Persistence book and my blog as well.


Top
 Profile  
 
 Post subject: Re: Non-primary key @JoinColumn no longer working?
PostPosted: Mon Aug 14, 2017 8:41 am 
Beginner
Beginner

Joined: Thu Jan 05, 2017 1:47 pm
Posts: 27
I didn't mean that @JoinColumn is optional. It's the support for specifying the join on a non-primary-key field that is supposedly optional.

I tried modifying the msb and lsb fields and accessors to type Long but I get the same error. Looking at the root causal exception it seems to suggest a type problem, not a null problem.

And note that it successfully stores the association as expected - the column TESTREFERER.REFEREE contains the TESTREFEREE.STRINGID value. Once that is done, the only thing the code does is call Referer.setReferee(null) and EntityManager.merge(referer) in a separate transaction.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.