0

"User" エンティティは、"Car" の 1 対多の双方向関係です。"Car" エンティティは、"CarPhoto" エンティティ ("Photo" エンティティの継承) の 1 対多の双方向関係です。「Car」エンティティには、「CarPhoto」との 1 対 1 の関係もあります。

したがって、CarPhotos は車の写真であり、車は、これらの写真のいずれかがカバー写真になるかどうかも示します (1 つの写真がアルバムの前面に表示されるアルバムのように考えてください)。

この問題は、カバー写真が設定されている場合に発生します。

以下の私のコードとエラーメッセージを見てください:

@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 を実行しています。

どこに問題があるか分かりますか?私を助けてください、私は今これに何日も苦労しています =(((

よろしく、カール

4

1 に答える 1

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);

このような循環参照は、一般的に悪い考えです。この場合、カバー写真は少なくとも null にすることができます。そうでない場合、外部キー制約の 1 つを適用できなかったか、またはそれらの 1 つを延期する必要がありました。

写真が多すぎない場合の別の解決策は、ブール値を使用してそのうちの 1 つをカバー写真としてフラグ付けすることです。

于 2013-05-16T21:03:27.857 に答える