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

Statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint

运行 测试时出错。我得到一个 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'.

字符代码 class

@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 class

@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();
}

测试class 当它执行 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')

这与 JUnit 测试框架的运行方式有关: 在某个事件中一共需要执行5个注解标记方法:

  • @BeforeClass: 在执行任何测试之前,此方法将执行一次
  • @Before:该方法将在调用实际测试方法之前执行,并且对于每个测试方法一个新的
  • @Test: 这是实际的测试方法
  • @After:该方法会在每个测试方法执行完毕后执行,为下一次测试做清理工作
  • @AfterClass: 此方法将在所有测试方法完成后执行,以关闭打开的数据库连接或其他...

在您的测试中 class 您尝试在每次重新测试之前坚持 Wil Wheaton 先生。 这是第一次工作,但在第二次,条目已经保留。尝试使用完全相同的 ID 保留它会违反您的唯一约束。

这里的简单解决方案是将 @Before 替换为 @BeforeClass 注释,这样该条目将只保留一次,或者添加一个带有 @After 注释的方法,该方法将从您的条目中删除该条目数据库,以便您可以重新插入它。

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

的方法轻松做到这一点

此致

J.Adam