Hibernate: just save an entity with @EmbeddedId as primary key

I wanted to learn hibernate basics and do not understand why my application does not work, when adding an entry to my table using the @EmbeddedId annontations for composited primary keys:

Class representing the PK:

@Embeddable
public class OHLCVKey implements Serializable{

    private static final long serialVersionUID = -3996067621138883817L;

    @Column(name="Symbol")
    protected String symbol;

    @Column(name="Currency")
    protected String currency;

    @Column(name="Datum")
    protected java.sql.Date datum;

    public OHLCVKey() {}

    public OHLCVKey(String symbol, String currency, Date datum) {
        super();
        this.symbol = symbol;
        this.currency = currency;
        this.datum = datum;
    }



    public String getSymbol() {
        return symbol;
    }

    public void setSymbol(String symbol) {
        this.symbol = symbol;
    }

    public String getCurrency() {
        return currency;
    }

    public void setCurrency(String currency) {
        this.currency = currency;
    }

    public java.sql.Date getDatum() {
        return datum;
    }

    public void setDatum(java.sql.Date endTime) {
        this.datum = endTime;
    }

    @Override
    public String toString() {
        return String.format("OHLCKey: {%s %s %s}", symbol, currency, datum);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + (symbol == null ? 0 : symbol.hashCode());
        result = prime * result + (currency == null ? 0 : currency.hashCode());
        result = prime * result + (datum == null ? 0 : datum.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if(obj != null || !(obj instanceof OHLCVKey)) {
            OHLCVKey other = (OHLCVKey) obj;
            boolean sameSymbol = this.symbol.equals(other.symbol);
            boolean sameCurrency = this.currency.equals(other.currency);
            boolean endTime = this.datum.equals(other.datum);
            return sameSymbol && sameCurrency && endTime;
        }
        return false;
    }
}

Persistent class:

@Entity
@Table(name = "ohlcv_data")
public class OHLCV implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = -742762046516249869L;

    @EmbeddedId
    protected OHLCVKey key;

    @Column(name = "Exchange")
    private String exchange;

    @Column(name = "Open")
    private double open;

    @Column(name = "High")
    private double high;

    @Column(name = "Low")
    private double low;

    @Column(name = "Close")
    private double close;

    @Column(name = "Volume")
    private long volume;

    public OHLCV() {};

    public OHLCV(OHLCVKey key) {
        super();
        this.key = key;
    }

    public OHLCV(OHLCVKey key, String exchange, double open, double high, double low, double close, long volume) {
        super();
        this.key = key;
        this.exchange = exchange;
        this.open = open;
        this.high = high;
        this.low = low;
        this.close = close;
        this.volume = volume;
    }

    public OHLCVKey getKey() {
        return key;
    }

    public void setKey(OHLCVKey key) {
        this.key = key;
    }

    public String getExchange() {
        return exchange;
    }

    public void setExchange(String exchange) {
        this.exchange = exchange;
    }

    public double getOpen() {
        return open;
    }

    public void setOpen(double open) {
        this.open = open;
    }

    public double getHigh() {
        return high;
    }

    public void setHigh(double high) {
        this.high = high;
    }

    public double getLow() {
        return low;
    }

    public void setLow(double low) {
        this.low = low;
    }

    public double getClose() {
        return close;
    }

    public void setClose(double close) {
        this.close = close;
    }

    public long getVolume() {
        return volume;
    }

    public void setVolume(long volume) {
        this.volume = volume;
    }
}

I assumed that after creating an OHLC instance I could add it's data to my database table with the following function (in a class called OHLCVDataManager):

public void addOHLCV(OHLCV ohlcv) { // line 50
    Session session = factory.openSession();
    Transaction tx = null;
    try {
        tx = session.beginTransaction();
        session.save(ohlcv); // line 55
        tx.commit();
        log.info("Added {} to database!", key.toString());
    } catch(Exception e) {
        if(tx != null) {
            tx.rollback();
        }
        e.printStackTrace();
    } finally {
        session.close();
        System.exit(2); //TODO remove, exit Spring application
    }
}

But I got the following PropertyAccessException/IllegalArgumentException with stack traces:

org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [protected OHLCVKey OHLCV.key] by reflection for persistent property [OHLCV#key] : OHLCV@46479254
2018-11-03 18:43:57.567  INFO 18384 --- [       Thread-9] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@7582b66f: startup date [Sat Nov 03 18:43:53 CET 2018]; root of context hierarchy
    at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:75)
    at org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:224)
    at org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:4931)
    at org.hibernate.mapping.Component$StandardGenerationContextLocator.locateGenerationContext(Component.java:480)
    at org.hibernate.id.CompositeNestedGeneratedValueGenerator.generate(CompositeNestedGeneratedValueGenerator.java:93)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:123)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:192)
    at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:38)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:177)
    at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:32)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
    at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:709)
    at org.hibernate.internal.SessionImpl.save(SessionImpl.java:701)
    at org.hibernate.internal.SessionImpl.save(SessionImpl.java:696)
    at OHLCVDataManager.addOHLCV(OHLCVDataManager.java:55)
    at OHLCVDataManager.addOHLCV(OHLCVDataManager.java:70)
    at ServerApplication.main(ServerApplication.java:16)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: java.lang.IllegalArgumentException: Can not set OHLCVKey field OHLCV
    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.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36)
    at java.lang.reflect.Field.get(Field.java:393)
    at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:71)

EDIT1:

ohlcvDataManager.addOHLCV("TEST", "USD", new java.sql.Date(2018, 1, 1), "TEST", 1, 1, 1, 1, 1);

// OHLCDataManager#addOHLCV call:    
public void addOHLCV(String symbol, String currency, java.sql.Date date, 
String exchange, double open, double high, double low, double close, long volume) {
    addOHLCV(new OHLCV(new OHLCVKey(symbol,currency, date),exchange, open, high, low, close, volume));
}

EDIT2: I have testet hibernate-core version 5.3.7 and 5.4 with java 1.8

EDIT2: There is just one OHLCV class and one OHLCVKey class in my classpath, I have also tried to rename the classes.

I think you are hitting a bug with composite keys mapped as EmbededID in hibernate 5.0 and 5.1 It is supposed to be fixed in 5.1.1 https://hibernate.atlassian.net/browse/HHH-10618

Try changing the hibernate version. Also I can see in one of the comments that @K.Nicholas is saying that the same code works for him. This is why I am even more inclined to think you are hitting this bug.

what if no ID column in the table for Hibernate? (Object Relational , How do I make two columns a primary key in hibernate? @EmbeddedId is used for composite primary key. i.e. more than one column behaves jointly as primary key. We need to create an entity as Embeddable and the Embeddable entity can be embedded in other entity to achieve composite primary key. Person.java

Downgrading to version

4.3.11.Final

worked...

how to make two column as a primary key in hibernate annotation , How do you make a string a primary key in hibernate? I wanted to learn hibernate basics and do not understand why my application does not work, when adding an entry to my table using the @EmbeddedId annontations for composited primary keys: Class representing the PK:

It's been a while since I touched hibernate , but from the error it sounds like it is unable to access [protected OHLCVKey OHLCV.key] this field. To narrow it down, I'd suggest two things. First, see if you can remove the casting in this line OHLCVKey key = (OHLCVKey) session.save(ohlcv); // line 55 and make it session.save(ohlcv), just to rule out if it is the casting that is causing it or the save itself. Second, although it doesn't make much sense, try if you can change the access modifier here to private.

 @EmbeddedId
    protected OHLCVKey key;

If any of these two don't help, try using HQL and see.

Hibernate String Primary Key with Annotation, to generate this property for you. Even in the XML configuration based approach its an optional tag and can be skipped. Mapping a primary key column with JPA and Hibernate is simple. You just need to add an attribute to your entity, make sure that its type and name match the database column, annotate it with @Column and you’re done. You can then use the primary key to load the entity, and Hibernate sets the primary key value automatically.

Caused by: java.lang.IllegalArgumentException: Can not set OHLCVKey field OHLCV
    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.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36)
    at java.lang.reflect.Field.get(Field.java:393)
    at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:71)

That peace of stacktrace means that Hibernate is trying to set a field value to some object, but their classes are incompatible:

protected void ensureObj(Object object) {
    if (!this.field.getDeclaringClass().isAssignableFrom(object.getClass())) {
        this.throwSetIllegalArgumentException(object);
    }
}

It means that Hibernate has loaded metadata from different class rather than from a class of an object you are trying to save. Please, check if there are only one class OHLCV in the classpath of your application, because example you provided works well.

EmbeddedId (hibernate-jpa-2.1-api 1.0.0.Final API), Applied to a persistent field or property of an entity class or mapped superclass to denote a composite primary key that is an embeddable class. The AttributeOverride annotation may be used to override the column mappings declared within the embeddable class. In this article, you learned how to implement a composite primary key in hibernate using @Embeddable and @EmbeddedId annotations. You can find the entire source code for the sample project that we built in this article in my jpa-hibernate-tutorials github repository .

Hibernate: just save an entity with @EmbeddedId as primary key, I wanted to learn hibernate basics and do not understand why my application does not work, when adding an entry to my table using the  The child class B have 2 onetoone sub childeren C and D which again uses embededId as the primary key (composite) the entity is getting saved properly in old spring boot 1.2.x version but after incrementing the version to 2.2.x i am getting foreign key constraint in table B and C.

JPA + Hibernate, In this case the 'composite primary key class' is embeddable i.e. It is also a more intuitive way to map a primary key using object composition. Java Collections - Only put Map key/value if the specified key does not exist  You should be just fine with your EntityManager. It has different naming but the same meaning. Once you invoke entityManager.persist(someInstance) , you should be able to get someInstance's id by calling someInstance.getId() -- assuming your ID property is named id.

JPA Entity Primary Key (@Id, @IdClass, @EmbeddedId), Primary key values are unique per entity class. Instances of different entity classes, however, may share the same primary key value. Only entity objects have  Let's understand by example. We have one Animal entity, it has name and location attributes. Now two different entity Lion and Elephant can have Animal attributes just by embedding the Animal Entity. We can override the attributes. In animal entity there is location attribute and in elephant there is place attribute.

Comments
  • What is the hibernate version?
  • Does the OHLCV definitely have a populated OHLCVKey at the point where it is being saved? Note how saveWithGeneratedOrRequestedId() is calling saveWithGeneratedId() in the stack trace, which suggests it couldn't find a key value.
  • Add the code that constructs the instance to be saved.
  • @AlanHay see EDIT1
  • Your hashCode() implementation of the PK is probably flawed. You always return super.hashCode() instead of result. This might be a source for your problem.
  • I used 5.3.7, also updating to 5.4 does not work for me
  • Both things do not solve the problem
  • There is just one OHLCV class in my classpath, I have also renamed my class.. There are only spring-boot dependencies.