I have two simple beans--FatKid and Hamburgers. I need to be able to not only look up all of the hamburgers someone ate, but also who ate which particular hamburger.
FatKid.java
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Table
@Entity
public class FatKid {
private int id;
private String name;
private List<Hamburger> hamburgers;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "FATKID_ID")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Column
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name="HAMBURGER_ID")
public List<Hamburger> getHamburgers() {
return hamburgers;
}
public void setHamburgers(List<Hamburger> hamburgers) {
this.hamburgers = hamburgers;
}
}
Hamburger.java
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Table
@Entity
public class Hamburger {
private int id;
private String description;
private FatKid whoDoneAteMe;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "HAMBURGER_ID")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Column
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="FATKID_ID")
public FatKid getWhoDoneAteMe() {
return whoDoneAteMe;
}
public void setWhoDoneAteMe(FatKid whoDoneAteMe) {
this.whoDoneAteMe = whoDoneAteMe;
}
}
hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">org.h2.Driver</property>
<property name="hibernate.connection.url">jdbc:h2:~/routesetting</property>
<property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create-drop</property>
<mapping class="FatKid" />
<mapping class="Hamburger" />
</session-factory>
</hibernate-configuration>
dependencies
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>3.6.7.Final</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.160</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.9.0.GA</version>
</dependency>
client
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class OmNom {
private static final SessionFactory sessionFactory = buildSessionFactory();
public static void main(String[] args) {
Session session = sessionFactory.openSession();
session.beginTransaction();
FatKid fk = new FatKid();
fk.setName("Darrell");
session.save(fk);
session.getTransaction().commit();
session.beginTransaction();
Hamburger hamburger_1 = new Hamburger();
hamburger_1.setDescription("Juicy quarter pounder with cheese");
hamburger_1.setWhoDoneAteMe(fk);
session.save(hamburger_1);
session.getTransaction().commit();
session.beginTransaction();
Hamburger hamburger_2 = new Hamburger();
hamburger_2.setDescription("Ground buffalo burger topped with bacon and a sunny-side egg");
hamburger_2.setWhoDoneAteMe(fk);
session.save(hamburger_2);
session.getTransaction().commit();
sessionFactory.close();
}
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new Configuration().configure().buildSessionFactory();
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
throw new ExceptionInInitializerError(ex);
}
}
}
The output (and truncated stack trace):
Hibernate: insert into FatKid (FATKID_ID, name) values (null, ?)
Hibernate: insert into Hamburger (HAMBURGER_ID, description, FATKID_ID) values (null, ?, ?)
Hibernate: insert into Hamburger (HAMBURGER_ID, description, FATKID_ID) values (null, ?, ?)
Exception in thread "main" org.hibernate.exception.ConstraintViolationException: could not insert: [Hamburger]
...
Caused by: org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "FK43797FE95067143: PUBLIC.HAMBURGER FOREIGN KEY(HAMBURGER_ID) REFERENCES PUBLIC.FATKID(FATKID_ID)"; SQL statement:
insert into Hamburger (HAMBURGER_ID, description, FATKID_ID) values (null, ?, ?) [23506-160]
...
The first Hamburger is saved but it blows up on the second.
How can I get it to use the FatKid's id as their foreign key?
Your mappings look weird to me. You have a @JoinColumn in both sides of the relationship, each pointing to the primary key of the other table. That doesn't seem to be a OneToMany relationship.
Your OneToMany should tell the owner of the relationship:
@OneToMany(cascade = CascadeType.ALL, mappedBy = "whoDoneAteMe")
public List<Hamburger> getHamburgers() {
return hamburgers;
}
and then in the other side:
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "fatkid_id")
public FatKid getWhoDoneAteMe() {
return whoDoneAteMe;
}
You might be able to optimize your code further too. As your FatKid objects are aware of the Hamburger objects and you have configured cascading, you could do:
session.beginTransaction();
FatKid fk = new FatKid();
fk.setName("Darrell");
Hamburger hamburger_1 = new Hamburger();
hamburger_1.setDescription("Juicy quarter pounder with cheese");
hamburger_1.setWhoDoneAteMe(fk);
fk.getHamburgers().add(hamburger1);
Hamburger hamburger_2 = new Hamburger();
hamburger_2.setDescription("Ground buffalo burger topped with bacon and a sunny-side egg");
hamburger_2.setWhoDoneAteMe(fk);
fk.getHamburgers().add(hamburger2);
session.save(fk);
session.getTransaction().commit();
sessionFactory.close();
The above code should save the complete object graph with just one commit operation and in a single transaction.