java.sql.SQLIntegrityConstraintViolationException при обновлении объекта - PullRequest
3 голосов
/ 05 июля 2011

Я новичок в JPA, и у меня возникла следующая проблема, возможно, из-за недостаточного понимания каскада и ассоциаций?У меня есть две сущности, которые находятся в двунаправленной ассоциации OneToMany - ManyToOne:

@Entity
public class TestRun implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    public Long getId() {
        return id;
    }

    public TestRun(){

    }

    public void setId(Long id) {
        this.id = id;
    }

    @ManyToOne(optional=false, cascade={CascadeType.PERSIST, CascadeType.MERGE})
    private Test performedTest;

    public Test getPerformedTest() {
        return performedTest;
    }

    public void setPerformedTest(Test performedTest) {
        this.performedTest = performedTest;
    }
    .....

    @Override
    public boolean equals(Object object) {
        if (!(object instanceof TestRun)) {
            return false;
        }
        TestRun other = (TestRun) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "TestRun[ id=" + id + " ]";
    }
}

И:

  @Entity
    public class Test implements Serializable {
        private static final long serialVersionUID = 1L;
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Integer id;

        @Basic(optional=false) 
        @Column(nullable=false)
         private String name;

        @OneToMany(mappedBy="performedTest", cascade={CascadeType.MERGE, CascadeType.PERSIST})
        private Collection<TestRun> appearsOnTestRuns;

        public Collection<TestRun> getAppearsOnTestRuns() {
            return appearsOnTestRuns;
        }

        public void setAppearsOnTestRuns(Collection<TestRun> appearsOnTestRuns) {
            this.appearsOnTestRuns = appearsOnTestRuns;
        }
        //id based equals and hashcode
    ...
    }

Отношение означает, что у меня есть набор тестов, которые выполняются несколькораз, каждый раз, будучи связанным с TestRun.Чтобы создать TestRun, я использую следующий метод:

public void createTestRun(String name){
    Test testPerformed = ejb.findByName(name); //Returns null iff forTest is not in the DB--> INSERT new Test
    if (testPerformed == null){
        testPerformed = EntityFactory.newTest(name);//I set the name for test
    }
    TestRun run = EntityFactory.newTestRun(testPerformed);
    ejb.persist(run);
}

public class EntityFactory{
    public static TestRun newTestRun(Test test){
        TestRun run = new TestRun();
        run.setPerformedTest(test);
        return run;
    }
    public static Test newTest(String name){
        Test test = new Test();
        test.setName(name);
        return test;
    }
}

, когда createTestRun имеет дело с новым Test (вставка), объект Test корректно сохраняется и TestRun.Но когда тест уже существует, JPA (EclipseLink в моем случае) все равно пытается выполнить INSERT:

....
Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'SQL110705102925930' defined on 'TEST'.
Error Code: -1
Call: INSERT INTO TEST (ID, NAME) VALUES (?, ?)

Что происходит за кулисами?Связана ли проблема со свойством emerge свойства TestOnTestRuns?Почему JPA не понимает, что Test уже существует в БД (в конце концов, свойство id устанавливается во время операции поиска, и equals () использует это значение, чтобы определить, равны ли 2 теста ...) Большое спасибо за вашу помощь!:)

PS: Знаете ли вы хороший учебник / книгу, где эти понятия подробно объясняются?

1 Ответ

0 голосов
/ 05 июля 2011

Прежде всего, вы должны проверить, что весь метод createTestRun (который находит тест, создает его при необходимости, а затем создает экземпляр TestRun и сохраняет его) работает внутри одной транзакции.

И ваш метод newTestRun должен инициализировать обе стороны отношения, что является двунаправленным. Разработчик несет ответственность за поддержание согласованности графа сущностей. Таким образом, следует сделать:

public static TestRun newTestRun(Test test){
    TestRun run = new TestRun();
    run.setPerformedTest(test);
    test.getAppearsOnTestRuns().add(run);
    return run;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...