“用户”实体是“汽车”的一对多双向关系。 “Car”实体是“CarPhoto”实体(它是“Photo”实体的继承)的一对多bidir关系。 “Car”实体也与“CarPhoto”具有一对一的关系。
因此,CarPhotos是汽车的照片,汽车也指出这些照片中的任何一张是否是封面照片(想象一张专辑中正面显示一张照片的专辑)。
设置封面照片时会出现问题。
请查看下面的代码并输入错误消息:
@Entity
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "USERS_SEQ")
@TableGenerator(name = "USERS_SEQ", table = "SEQUENCE", pkColumnName = "SEQ_NAME", pkColumnValue = "USERS_SEQ", valueColumnName = "SEQ_COUNT", allocationSize = 1)
@Column(nullable = false)
private long id;
@OneToMany(mappedBy = "user", cascade = { CascadeType.ALL }, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<Car> cars;
//...
}
@Entity
public class Car {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "CARS_SEQ")
@TableGenerator(name = "CARS_SEQ", table = "SEQUENCE", pkColumnName = "SEQ_NAME", pkColumnValue = "CARS_SEQ", valueColumnName = "SEQ_COUNT", allocationSize = 1)
@Column(nullable = false)
private long id;
@OneToMany(mappedBy = "car", cascade = { CascadeType.ALL }, fetch = FetchType.LAZY, orphanRemoval = true)
private Set<CarPhoto> photos;
// coverPhoto shall be NULL or a object in the Set<CarPhoto> photos
@OneToOne
@JoinColumn(name = "COVER_PHOTO", referencedColumnName = "ID")
private CarPhoto coverPhoto;
//...
}
@Entity
@DiscriminatorValue("C")
public class CarPhoto extends Photo {
@ManyToOne(cascade = { CascadeType.DETACH })
@JoinColumn(name = "CARID", nullable = false)
@NotNull
private Car car;
//...
}
@Entity
@Inheritance
@DiscriminatorColumn(name = "DESCRIMINATOR")
public abstract class Photo {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "PHOTOS_SEQ")
@TableGenerator(name = "PHOTOS_SEQ", table = "SEQUENCE", pkColumnName = "SEQ_NAME", pkColumnValue = "PHOTOS_SEQ", valueColumnName = "SEQ_COUNT", allocationSize = 50)
@Column(nullable = false)
private long id;
//...
}
@Test
public void testCarAddCarPhotos() throws Exception {
User user = dg.getTestUser(); // Get a user object
Car car = dg.getTestCar(); // Get a car object
CarPhoto photo = dg.getTestCarPhoto(); // Get a car photo object
user.addToCars(car); // Add car to user cars list
car.setUser(user); // ^^^ To keep bi-directional relationship^^^
photo.setCar(car); // Add car as owner to this photo
car.addToPhotos(photo); // ^^^ To keep bi-directional relationship^^^
em.persist(user);
em.flush();
em.clear();
// This tests works perfectly, the user has a car, and the car has a photo
}
@Test
public void testCarAddCarPhotosAddCoverPhoto() throws Exception {
User user = dg.getTestUser(); // Get a user object
Car car = dg.getTestCar(); // Get a car object
CarPhoto photo = dg.getTestCarPhoto(); // Get a car photo object
user.addToCars(car); // Add car to user cars list
car.setUser(user); // ^^^ To keep bi-directional relationship^^^
photo.setCar(car); // Add car as owner to this photo
car.addToPhotos(photo); // ^^^ To keep bi-directional relationship^^^
// NOW TEST TO A THE COVERPHOTO
car.setCoverPhoto(photo);
em.persist(user);
em.flush();
// ERROR:
// javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.4.0.v20120608-r11652): org.eclipse.persistence.exceptions.DatabaseException
// Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`vehicledb_test`.`PHO
// TOS`, CONSTRAINT `FK_PHOTOS_CARID` FOREIGN KEY (`CARID`) REFERENCES `CARS` (`ID`))
// Error Code: 1452
// Call: INSERT INTO PHOTOS (ID, DESCRIPTION, FILENAME, TITLE, UPLOADTIME, CARID, DESCRIMINATOR) VALUES (?, ?, ?, ?, ?, ?, ?)
// bind => [1, jequejnnzkxhzahaimg, rabbit, computer, 2013-05-16 21:59:42.524, 3, C]
// Query: WriteObjectQuery(Photo [id=1, description=jequejnnzkxhzahaimg, fileName=rabbit, title=computer, uploadTime=Thu May 16 21:59:42 CEST 2013])
// at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:804)
// at se.while_se.service.jpa.UserRepositoryTest.testModifyUserRemoveCarWithPhoto(UserRepositoryTest.java:251)
// at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
// at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
// at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
// at java.lang.reflect.Method.invoke(Method.java:601)
// at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
// at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
// at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
// at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
// at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
// at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
// at org.junit.rules.TestWatchman$1.evaluate(TestWatchman.java:48)
// at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
// at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
// at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
// at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
// at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
// at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
// at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
// at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
// at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
// at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
// at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
// at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:35)
// at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:146)
// at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:97)
// at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
// at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
// at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
// at java.lang.reflect.Method.invoke(Method.java:601)
// at org.apache.maven.surefire.booter.ProviderFactory$ClassLoaderProxy.invoke(ProviderFactory.java:103)
// at $Proxy0.invoke(Unknown Source)
// at org.apache.maven.surefire.booter.SurefireStarter.invokeProvider(SurefireStarter.java:145)
// at org.apache.maven.surefire.booter.SurefireStarter.runSuitesInProcess(SurefireStarter.java:87)
// at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:69)
// Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.4.0.v20120608-r11652): org.eclipse.persistence.exceptions.DatabaseException
em.clear();
// This test does not works =(
}
其他信息:我正在运行eclipselink 2.4.0,mysql-connector-java 5.1.20和Java 7.
你能看出问题出在哪里吗?请帮助我,我已经挣扎了好几天了=((
最好的问候,卡尔
答案 0 :(得分:3)
问题是由循环引用引起的,我认为。这张照片有一个参考(外键)到它的汽车,汽车也有一个参考其封面照片。因此EclipseLink似乎首先插入照片,但由于尚未插入汽车,插入失败。
我只是坚持并冲洗用户(以及它的汽车及其照片),然后只将照片设置为汽车的封面照片:
user.addToCars(car); // Add car to user cars list
car.setUser(user); // ^^^ To keep bi-directional relationship^^^
photo.setCar(car); // Add car as owner to this photo
car.addToPhotos(photo); // ^^^ To keep bi-directional relationship^^^
em.persist(user);
em.flush();
// NOW TEST TO A THE COVERPHOTO
car.setCoverPhoto(photo);
这样的循环引用一般来说是个坏主意。在这种情况下,封面照片至少可以为空。如果不是这种情况,则无法强制执行其中一个外键约束,或者其中一个必须延迟。
另一种解决方案,如果没有太多照片,可以使用布尔值将其中一张标记为封面照片。