Hibernate и H2 "нарушение ограничения ссылочной целостности" Для двунаправленного отображения OneToMany
так у меня есть два простых бобов-FatKid и гамбургеры. Теперь, по неизвестным мне причинам, я должен быть в состоянии не только найти все гамбургеры, которые кто-то съел, но и кто съел какой именно гамбургер. На код!
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;
}
}
гамбургер.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;
}
}
спящий режим.контекстно-свободная грамматика.в 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>
зависимости
<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>
клиент
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);
}
}
}
поэтому, когда я запускаю код, я заканчиваю с вывод (и усеченная трассировка стека)
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]
...
таким образом, первый гамбургер сохраняется, но затем он взрывается на втором. Оба должны иметь возможность использовать идентификатор FatKid в качестве внешнего ключа, но он, похоже, не работает. Любое понимание будет высоко оценен.
спасибо, Кевин!--7-->
1 ответов
сопоставлений выглядят странно для меня. У вас есть @JoinColumn в обеих сторонах отношений, каждая из которых указывает на первичный ключ другой таблицы. Это не похоже на одномерные отношения.
ваш OneToMany должен сказать владельцу отношения:
@OneToMany(cascade = CascadeType.ALL, mappedBy = "whoDoneAteMe")
public List<Hamburger> getHamburgers() {
return hamburgers;
}
а потом с другой стороны:
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "fatkid_id")
public FatKid getWhoDoneAteMe() {
return whoDoneAteMe;
}
вы также можете оптимизировать свой код. В качестве объектов FatKid известны объекты гамбургер и настройки каскадные, вы могли бы сделать:
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();
приведенный выше код должен сохранить полный граф объектов только с одной операцией фиксации и в одной транзакции.