1

enter image description here

I have a simple relationship as shown in the diagram above.

I can persist this using the following Hibernate Mapping:

<class name="Strategy" table="Strategy">
    <id name="id" column="StrategyId" unsaved-value="any">
        <generator class="identity"/>
    </id>
    <property name="status" column="Status"/>
</class>

<class name="Alert" table="Alert">
    <id name="id" column="AlertId">
        <generator class="identity"/>
    </id>
    <property name="name" column="Name"/>        
</class>

<class name="StrategyAlert" table="StrategyAlert">
    <composite-id>            
        <key-many-to-one name="strategy" class="Strategy" column="StrategyId"/>
        <key-many-to-one name="alert" class="Alert" column="AlertId"/>
    </composite-id>
    <property name="nominal" column="Nominal"/>
</class>

I am having a really hard time trying to figure out how to do this using Annotations in JPA 1.0.

Here's what I've got so far:

Alert Class:

@Entity
@Table(name="ALERT")
public class Alert implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="ALERTID")
    private int id;

    @Column(name="NAME")
    private String name;    
}

Strategy Class:

@Entity
@Table(name="STRATEGY")
public class Strategy implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)    
    @Column(name="STRATEGYID")
    private int id;

    @Column(name="STATUS")
    private String status;
}

StrategyAlertPK class:

public class StrategyAlertPK implements Serializable {

    private int strategyId;
    private int alertId;
}

StrategyAlert class:

@Entity
@Table(name="STRATEGYALERT")
@IdClass(StrategyAlertPK.class)
public class StrategyAlert implements Serializable {

    @Id
    @Column(name="STRATEGYID")
    private int strategyId;

    @Id
    @Column(name="ALERTID")
    private int alertId;

    @ManyToOne
    @JoinColumn(name="STRATEGYID", insertable=false, updatable=false)
    private Strategy strategy;

    @ManyToOne
    @JoinColumn(name="ALERTID", insertable=false, updatable=false)
    private Alert alert;

    @Column
    private String nominal;
}

Test Case:

em.getTransaction().begin();       
Alert alert = new Alert();
alert.setName("NAME");
em.persist(alert);

Strategy strategy = new Strategy();
strategy.setStatus("STATUS");        
em.persist(strategy);

StrategyAlert strategyAlert = new StrategyAlert();
strategyAlert.setAlert(alert);
strategyAlert.setStrategy(strategy);
strategyAlert.setNominal("NOMINAL");
em.persist(strategyAlert);        
em.getTransaction().commit();      

I'm getting the following error:

Referential integrity constraint violation: "FK80432DA9CE00672E: PUBLIC.STRATEGYALERT FOREIGN KEY(STRATEGYID) REFERENCES PUBLIC.STRATEGY(STRATEGYID) (0)

I'm using <property name="hibernate.hbm2ddl.auto" value="create" /> to generate the tables.

How do I annotate the StrategyAlert class correctly?

user1810292
  • 279
  • 6
  • 17

2 Answers2

1

Take a look at this questions: How to implement a complex many to many relationship in JPA?

I think the problem here is you have to join the Id declaration and the manyToOne declaration as described in JPA 1, I can´t test the code right now, but more or less it has to be like this

@Entity
@Table(name="STRATEGYALERT")
@IdClass(StrategyAlertPK.class)
public class StrategyAlert implements Serializable {
   @Id
   @ManyToOne
   @JoinColumn(name="STRATEGYID", insertable=false, updatable=false)
   private Strategy strategy;

   @Id
   @ManyToOne
   @JoinColumn(name="ALERTID", insertable=false, updatable=false)
   private Alert alert;

   @Column
   private String nominal;

   // TODO Getters and setters
}

UPDATE

Hi,

now I can test the code, and is working for m with the following code (some minors corrections):

/**
 * 
 */
package hib;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 *
 */
@Entity
@Table(name="ALERT")
public class Alert implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="ALERTID")
    private int id;

    @Column(name="NAME")
    private String name;

    /**
     * @return the id
     */
    public int getId() {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }
}

Strategy

/**
 * 
 */
package hib;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 *
 */
@Entity
@Table(name="STRATEGY")
public class Strategy implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)    
    @Column(name="STRATEGYID")
    private int id;

    @Column(name="STATUS")
    private String status;

    /**
     * @return the id
     */
    public int getId() {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * @return the status
     */
    public String getStatus() {
        return status;
    }

    /**
     * @param status the status to set
     */
    public void setStatus(String status) {
        this.status = status;
    }
}

StrategyAlertPK

/**
 * 
 */
package hib;

import java.io.Serializable;

import javax.persistence.Embeddable;

/**
 *
 */
@Embeddable
public class StrategyAlertPK implements Serializable {

    @ManyToOne
    private Strategy strategy;

    @ManyToOne
    private Alert alert;
    /**
     * @return the strategy
     */
    public Strategy getStrategy() {
        return strategy;
    }
    /**
     * @param strategy the strategy to set
     */
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }
    /**
     * @return the alert
     */
    public Alert getAlert() {
        return alert;
    }
    /**
     * @param alert the alert to set
     */
    public void setAlert(Alert alert) {
        this.alert = alert;
    }

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }

        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        StrategyAlertPK that = (StrategyAlertPK) o;

        if (strategy != null ? !strategy.equals(that.strategy) : that.strategy != null) {
            return false;
        }
        if (alert != null ? !alert.equals(that.alert) : that.alert != null) {
            return false;
        }

        return true;
    }

    public int hashCode() {
        int result;
        result = (strategy != null ? strategy.hashCode() : 0);
        result = 31 * result + (alert != null ? alert.hashCode() : 0);
        return result;
    }


}

StrategyAlert

/**
 * 
 */
package hib;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

/**
 *
 */
@Entity
@Table(name="STRATEGYALERT")
    @AssociationOverrides({ @AssociationOverride(name = "pk.strategy", joinColumns = @JoinColumn(name = "STRATEGYID")),
    @AssociationOverride(name = "pk.alert", joinColumns = @JoinColumn(name = "ALERTID")) })
public class StrategyAlert implements Serializable {

    @EmbeddedId
    private StrategyAlertPK pk = new StrategyAlertPK();

    @Column
    private String nominal;

    @Transient
    public Strategy getStrategy() {
        return pk.getStrategy();
    }

    public void setStrategy(Strategy strategy) {
        pk.setStrategy(strategy);
    }

    @Transient
    public Alert getAlert() {
        return pk.getAlert();
    }

    public void setAlert(Alert alert) {
        pk.setAlert(alert);
    }

    /**
     * @return the nominal
     */
    public String getNominal() {
        return nominal;
    }

    /**
     * @param nominal the nominal to set
     */
    public void setNominal(String nominal) {
        this.nominal = nominal;
    }
}

With that code your test is running OK (I tested it with JPA 1.0.1, Hibernate 3.3.1.GA and spring 2.5.5). I am using H2 as memory database so I had to change Identity to AUTO in generated values, maybe you don´t need to do that.

Hope helps!

Community
  • 1
  • 1
Francisco Hernandez
  • 2,378
  • 14
  • 18
  • Thanks for help Francisco I tried that and I get an error that the Id Class can't find it's properties: "Unable to find properties (alertId, strategyId) in entity annotated with @IdClass:hib.StrategyAlert" – user1810292 Nov 04 '15 at 14:31
  • I have tried everything I can think of and I cannot get this to work. Considering how easy it is in XML you would think it should be easy enough using Annotations. Does anyone know how to get this working? – user1810292 Nov 05 '15 at 09:01
  • Hi, I updated my answer, now I can test the code and is working for me. Hope helps – Francisco Hernandez Nov 05 '15 at 11:17
  • I'm also using H2.I copy/pasted your code just renamed package to hib. I'm getting the following error: `Caused by: org.hibernate.PropertyAccessException: could not set a field value by reflection setter of hib.StrategyAlertPK.alert` and `Caused by: java.lang.IllegalArgumentException: Can not set int field hib.StrategyAlertPK.alert to hib.Alert`. Im using HIbernate v3.2.4 I might try another version as I can't see why this is happening. – user1810292 Nov 05 '15 at 11:54
  • I tried it with latest version of Hibernate I can use - v3.3.1.GA / Annotations 3.4.0.GA / EntityManager 3.4.0.GA and I get exact same error: `Can not set int field hib.StrategyAlertPK.alert to hib.Alert`. I don't know what to try next – user1810292 Nov 05 '15 at 12:16
  • Sorry! My mistake. I updated the answer, I changed only StrategyAlertPK, The PK class must have its attributes as entities, not as primitive types. Change it and try again – Francisco Hernandez Nov 05 '15 at 12:24
  • I tried that and it does persist the values but it looks like it's serializing the objects instead of storing the IDs. The Strategy Column contains `aced0005737200096869622e416c657274dbd1f96fcd2ddb5c02000249000269644c00046e616d657400124c6a6176612f6c616e672f537472696e673b7870000000017400044e414d45` and the alert column contains something similar and both are VARBINARY – user1810292 Nov 05 '15 at 12:38
  • Hi, new update. I Changed StrategyAlertPK and StrategyAlert, I Think this is the good one. Try it out! :D – Francisco Hernandez Nov 05 '15 at 14:15
  • Brilliant - that works! Thanks for figuring it out I really appreciate it! – user1810292 Nov 05 '15 at 14:25
0

I have found a solution but I don't think this is the correct way to do it.

I have a static nested class for the ID. Then in both setter methods I also set the ID. This seems a very manual way to do it and surely there must be a better way?

@Entity
@Table(name="STRATEGYALERT")
public class StrategyAlert {

    @Embeddable
    public static class Id implements Serializable {
        private static final long serialVersionUID = -8270591004786497335L;

        @Column(name="STRATEGYID")
        private int strategyId;

        @Column(name="ALERTID")
        private int alertId;

        public Id() {}

        public Id(int strategyId, int alertId) {
            this.strategyId = strategyId;
            this.alertId = alertId;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + alertId;
            result = prime * result + strategyId;
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if(this == obj)
                return true;
            if(obj == null)
                return false;
            if(getClass() != obj.getClass())
                return false;
            Id other = (Id)obj;
            if(alertId != other.alertId)
                return false;
            if(strategyId != other.strategyId)
                return false;
            return true;
        }

    }    

    @EmbeddedId
    private Id id = new Id();

    @ManyToOne
    @JoinColumn(name="STRATEGYID", insertable=false, updatable=false)
    private Strategy strategy;

    @ManyToOne
    @JoinColumn(name="ALERTID", insertable=false, updatable=false)
    private Alert alert;

    @Column(name="NOMINAL")
    private String nominal;    

    public StrategyAlert() {}

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;    
        // SET ID
        this.id.strategyId = strategy.getId();
    }

    public void setAlert(Alert alert) {
        this.alert = alert;    
        // SET ID
        this.id.alertId = alert.getId();
    }

    // Other Getters & Setters...
}
user1810292
  • 279
  • 6
  • 17