休眠:如何在删除所属实体后更新父行



所以我有4个实体(人、车、车库、图像(

人和车之间的关系是一对多的(一个人可以有很多辆车(使用"session.remove(somePerson("时出现异常:"无法删除或更新父行:外键约束失败"。然而,我可以使用"session.remove(someCar(",它是有效的。我想我的问题是,当一个人被删除时,hibernate无法更新Cars中的外键,我想这与级联有关。。

只是澄清一下:当我删除(somePerson(时,我想要的结果是:somePerson已被删除。他的车不会被移走!(所有者列中将为null(。

Car.java

package com.example;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "cars")
public class Car {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String licensePlate;
private double price;
private int year;
@ManyToOne(fetch = FetchType.LAZY)
@Cascade(CascadeType.SAVE_UPDATE)
@JoinColumn(name = "owner_id")
private Person owner;
@OneToOne(fetch = FetchType.LAZY)
@Cascade(CascadeType.ALL)
private Image image;
@ManyToMany
@Cascade({CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(
name = "cars_garages",
joinColumns = @JoinColumn(name = "car_id"),
inverseJoinColumns = @JoinColumn(name = "garage_id")
)
private List<Garage> garageList;
//GROUP C'tors
public Car(String licensePlate, double price, int year) {
this();
this.licensePlate = licensePlate;
this.price = price;
this.year = year;
}
public Car() {
garageList = new ArrayList<>();
}
//GROUP adders
public void addGarage(Garage garage) {
if (!garageList.contains(garage))
garageList.add(garage);
if (!garage.getCarList().contains(this))
garage.getCarList().add(this);
}
//GROUP setters and getters
public int getId() {
return id;
}
public String getLicensePlate() {
return licensePlate;
}
public void setLicensePlate(String licensePlate) {
this.licensePlate = licensePlate;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public Image getImage() {
return image;
}
public void setImage(Image image) {
this.image = image;
if (image.getCar() != this)
image.setCar(this);
}
public Person getOwner() {
return owner;
}
public void setOwner(Person owner) {
this.owner = owner;
if (!owner.getCarList().contains(this))
owner.getCarList().add(this);
}
public List<Garage> getGarageList() {
return garageList;
}
public void setGarageList(List<Garage> garageList) {
this.garageList = garageList;
}
@Override
public String toString() {
StringBuilder string = new StringBuilder("Car: ID = " + id + ", license plate = " + licensePlate +
", price = " + price + ", year = " + year + "nowner details:nt" + "ID: " + owner.getId()
+ ", full name = " + owner.getFirstName() + " " + owner.getLastName() + ", email address = " +
owner.getEmailAddress() + ", password = " + owner.getPassword() + "n"
+ "This car can get service at the following addresses:n");
for (Garage garage : garageList)
string.append("t").append(garage.getAddress()).append("n");
return string.toString();
}
}

Person.java

package com.example;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "persons")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String firstName, lastName, emailAddress, password;
@OneToMany(
//cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "owner"
)
@Cascade({CascadeType.SAVE_UPDATE, CascadeType.DETACH})
private List<Car> carList;
@ManyToMany//(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@Cascade({CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(
name = "persons_garages",
joinColumns = @JoinColumn(name = "person_id"),
inverseJoinColumns = @JoinColumn(name = "garage_id")
)
private List<Garage> garageList;
//GROUP C'tors
public Person(String firstName, String lastName, String password, String emailAddress) {
this();
this.firstName = firstName;
this.lastName = lastName;
this.password = password;
this.emailAddress = emailAddress;
}
public Person() {
this.carList = new ArrayList<>();
this.garageList = new ArrayList<>();
}
//GROUP adders
public void addCar(Car car) {
if (!carList.contains(car))
{
carList.add(car);
car.setOwner(this);
}
}
public void addGarage(Garage garage) {
if (!garageList.contains(garage))
garageList.add(garage);
if (!garage.getOwners().contains(this))
garage.getOwners().add(this);
}
public void removeCar(Car car) {
carList.remove(car);
}
//GROUP setters and getters
public int getId() {
return id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public List<Car> getCarList() {
return carList;
}
public void setCarList(List<Car> carList) {
this.carList = carList;
}
public List<Garage> getGarageList() {
return garageList;
}
public void setGarageList(List<Garage> garageList) {
this.garageList = garageList;
}
}

App.java

package com.example;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.Parent;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
public class App {
private static final int NUM_OF_PERSONS = 5;
private static final int NUM_OF_CARS = 5;
private static final int NUM_OF_GARAGES = 2;
private static final int NUM_OF_IMAGES = 5;
private static Session session;
public static void main(String[] args) {
try
{
SessionFactory sessionFactory = getSessionFactory();
session = sessionFactory.openSession();
session.beginTransaction();
generateCars();
generatePersons();
generateGarages();
generateImages();
connectEntities();
session.clear();
List<Person> personList = getAllOfType(Person.class);
session.remove(personList.get(0));
session.flush();        // Exception is being thrown here
session.getTransaction().commit();
printAllOfType(Garage.class);
printAllOfType(Car.class);
}
catch (Exception exception)
{
if (session != null)
session.getTransaction().rollback();
System.err.println("An error occurred, changes have been rolled back.");
exception.printStackTrace();
} finally
{
assert session != null;
session.close();
session.getSessionFactory().close();
}
}
private static SessionFactory getSessionFactory() throws HibernateException {
java.util.logging.Logger.getLogger("org.hibernate").setLevel(Level.OFF);
Configuration configuration = new Configuration();
configuration.addAnnotatedClass(Car.class);
configuration.addAnnotatedClass(Person.class);
configuration.addAnnotatedClass(Garage.class);
configuration.addAnnotatedClass(Image.class);
ServiceRegistry serviceRegistry =
new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
return configuration.buildSessionFactory(serviceRegistry);
}
private static void generateCars() {
Random random = new Random();
for (int i = 0; i < NUM_OF_CARS; i++)
{
Car car = new Car("MOO-" + random.nextInt(999999), 100000,
2000 + random.nextInt(19));
session.save(car);
}
session.flush();
}
private static void generatePersons() {
Random random = new Random();
String[] firstNames = {"Avi", "Dan", "John", "Didi", "Avihu"};
String[] lastNames = {"Hemmo", "Bilzerian", "Snow", "Harrari", "Medina"};
for (int i = 0; i < NUM_OF_PERSONS; i++)
{
Person person = new Person(firstNames[i % firstNames.length], lastNames[i % lastNames.length],
String.valueOf(random.nextInt(99999)),
firstNames[i % firstNames.length] + lastNames[i % lastNames.length] + "@fake.com");
session.save(person);
}
session.flush();
}
private static void generateGarages() {
String[] addresses = {"1 1st st, New York, NY", "5 5th st, New York, NY"};
for (int i = 0; i < NUM_OF_GARAGES; i++)
{
Garage garage = new Garage(addresses[i % addresses.length]);
session.save(garage);
}
session.flush();
}
private static void generateImages() {
for (int i = 0; i < NUM_OF_IMAGES; i++)
session.save(new Image());
session.flush();
}
private static <T> List<T> getAllOfType(Class<T> objectType) {
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<T> query = builder.createQuery(objectType);
query.from(objectType);
return session.createQuery(query).getResultList();
}
private static <T> void printAllOfType(Class<T> objectType) {
List<T> tList = getAllOfType(objectType);
for (T object : tList)
System.out.println(object);
}
private static void connectEntities() {
List<Person> persons = getAllOfType(Person.class);
List<Car> cars = getAllOfType(Car.class);
List<Garage> garages = getAllOfType(Garage.class);
List<Image> images = getAllOfType(Image.class);
// connect cars with owners & cars with garages
for (int i = 0; i < cars.size(); i++)
{
cars.get(i).setOwner(persons.get(i % persons.size()));
cars.get(i).addGarage(garages.get(i % garages.size()));
cars.get(i).setImage(images.get(i % images.size()));
session.update(cars.get(i));
}
session.flush();
// connect persons with garages
for (int i = 0; i < persons.size(); i++)
{
persons.get(i).addGarage(garages.get(i % garages.size()));
session.update(persons.get(i));
}
session.flush();
}
}

错误日志

An error occurred, changes have been rolled back.
javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1356)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1339)
at com.example.App.main(App.java:42)
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:59)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:99)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:200)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:45)
at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:3551)
at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:3810)
at org.hibernate.action.internal.EntityDeleteAction.execute(EntityDeleteAction.java:124)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604)
at org.hibernate.engine.spi.ActionQueue.lambda$executeActions$1(ActionQueue.java:478)
at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:723)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:475)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:348)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:40)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:102)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1352)
... 2 more
Caused by: java.sql.SQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`lab5`.`cars`, CONSTRAINT `FK82ccqb3p17md157c33e2qerq9` FOREIGN KEY (`owner_id`) REFERENCES `persons` (`id`))
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:117)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1092)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1040)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1347)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1025)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:197)
... 14 more

关系的拥有方是Car,而不是Person,因此如果不使用当前代码删除Car的Person,就无法删除它。您可以通过首先删除关联来修改删除操作的方式(将Car中的owner字段设置为null,并在删除Person之前保存Car实体(,也可以通过使用第三个表来处理关系来更改建立关系的方式(当我遇到同样的问题时,我选择了第一个解决方案(。

如果您遵循第一种方法,您应该在事务中执行整个操作(将owner字段设置为null并删除Car(,否则,如果出现问题,您可能会处于不一致的状态。

最新更新