语句已中止,因为它会在唯一或主键约束中导致重复键值

时间:2016-04-12 16:19:09

标签: java jpa java-ee derby jboss-arquillian

运行测试时出错。我得到一个 SQLIntegrityConstraintViolationException

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 'SQL160412141534310' defined on 'CHARACTERS'.

Character类的代码

@Entity
@Table(name = "CHARACTERS")
@NamedQueries({
@NamedQuery(name = Character.FIND_ALL, query = "SELECT c FROM Character c")
})
public class Character implements Serializable {

public static final String FIND_ALL = "Character.findAll";

private static final long serialVersionUID = 1L;

@Id 
private int id;

@Column(length = 50)
private String name;

public Character() {
}

public Character(int id, String name) {
    this.id = id;
    this.name = name;
}

public Character(String name) {
    this.name = name;
}

/* Getter and setter below */

CharactersBean类

@Stateful
@TransactionAttribute(TransactionAttributeType.NEVER)
public class CharactersBean implements Serializable {

@PersistenceContext(type = PersistenceContextType.EXTENDED)
EntityManager em;

public void save(Character e) {
    em.persist(e);
}

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void commitChanges() {

}

public List<Character> get() {
    return em.createNamedQuery(Character.FIND_ALL, Character.class).getResultList();
}

测试类 当它执行 should_update_characters_after_transaction_flush 时,会抛出上述异常。

@RunWith(Arquillian.class)
public class ExtendedPersistenceContextTest {

@PersistenceContext
EntityManager em;

@EJB
CharactersBean bean;

@Deployment
public static WebArchive deploy() {
    return ShrinkWrap.create(WebArchive.class)
            .addPackage("org.javaee7.jpa.extended.pc")
            .addAsResource("META-INF/persistence.xml")
            .addAsResource("META-INF/create.sql")
            .addAsResource("META-INF/drop.sql")
            .addAsResource("META-INF/load.sql");
}

@Before
public void setup() {
    Character wil = new Character(8, "Wil Wheaton");
    bean.save(wil);

    for (Character c : bean.get()) {
        if ("Raj".equals(c.getName())) {
            c.setName("Rajesh Ramayan");
            bean.save(c);
        }
    }
}

@Test
@InSequence(1)
public void should_not_persist_changes_without_transaction_flush() {
    List<Character> characters = em.createNamedQuery(Character.FIND_ALL, Character.class).getResultList();
    Character raj = em.find(Character.class, 6);

    assertThat(characters, hasSize(7));
    assertThat(raj.getName(), is(equalTo("Raj")));
}

@Test
@InSequence(2)
public void should_update_characters_after_transaction_flush() {
    //when
    bean.commitChanges();

    //then
    List<Character> characters = em.createNamedQuery(Character.FIND_ALL, Character.class).getResultList();
    Character rajesh = em.find(Character.class, 6);
    Character wil = em.find(Character.class, 8);

    assertThat(characters, hasSize(8));
    assertThat(rajesh.getName(), is(equalTo("Rajesh Ramayan")));
    assertThat(wil.getName(), is(equalTo("Wil Wheaton")));
}

输入Derby数据库中的数据。

INSERT INTO CHARACTERS("ID", "NAME") VALUES (1, 'Penny')
INSERT INTO CHARACTERS("ID", "NAME") VALUES (2, 'Sheldon')
INSERT INTO CHARACTERS("ID", "NAME") VALUES (3, 'Amy')
INSERT INTO CHARACTERS("ID", "NAME") VALUES (4, 'Leonard')
INSERT INTO CHARACTERS("ID", "NAME") VALUES (5, 'Bernadette')
INSERT INTO CHARACTERS("ID", "NAME") VALUES (6, 'Raj')
INSERT INTO CHARACTERS("ID", "NAME") VALUES (7, 'Howard')

1 个答案:

答案 0 :(得分:4)

这与JUnit测试框架的运作方式有关: 总共有5种注释标记方法应在特定事件中执行:

  • @BeforeClass:此方法将在执行任何测试之前执行一次
  • @Before:此方法将在调用实际测试方法之前执行,而每个测试方法新执行
  • @Test:这是一种实际的测试方法
  • @After:此方法将在每个测试方法完成执行后执行,以清理下一个测试
  • @AfterClass:此方法将在所有测试方法完成后执行,以关闭打开的数据库连接或其他内容......

在你的考试课上,你试着在每次考试之前坚持Wil Wheaton先生。 这是第一次工作,但第二次,该条目已经保留。试图用完全相同的ID来保持它然后违反你唯一的约束。

这里的简单解决方案是将@Before替换为@BeforeClass注释,因此该条目只会被持久化一次,或者添加一个带有@After注释的方法,该注释将从数据库中删除该条目,所以你可以重新插入它。

此外,我建议在完成后清除数据库中由测试插入的任何条目。您可以使用带注释的@After@AfterClass

方法轻松完成此操作

最好的问候

J.Adam