所以我有4个实体(人,汽车,车库,图片)
人与汽车之间的关系是一对多(一个人可以拥有许多汽车)当使用“ session.remove(somePerson)”时,我有一个例外:“无法删除或更新父行:外键约束失败”。但是我可以使用'session.remove(someCar)'并且它可以工作。我认为我的问题是,当某个人被移除时,休眠无法更新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:\n\t" + "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
所以我有4个实体(人,汽车,车库,图像),当使用'session.remove(somePerson)'时,人与汽车之间的关系是oneToMany(一个人可以拥有很多汽车),我有一个例外:'不能。 ..
关系的所有者是Car,而不是Person,因此,如果不删除具有当前代码的Car的Car,就无法删除该Car。您可以通过先删除关联来修改删除操作的方式(将Car中的owner
字段设置为null
并在删除Person之前保存Car实体),或者可以通过以下方式更改建立关系的方式:处理它的第三个表(当我遇到与第一个解决方案相同的问题时)。