【问题标题】:Statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint语句被中止,因为它会导致唯一或主键约束中的重复键值
【发布时间】:2016-04-12 16:19:09
【问题描述】:

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

字符类代码

@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')

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

    此外,我建议在测试完成后清理您的数据库中的所有测试插入的条目。您可以使用注解为 @After@AfterClass 的方法轻松地做到这一点

    最好的问候

    J.亚当

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-07
      • 2016-09-21
      • 1970-01-01
      • 2011-06-26
      • 2014-02-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多