DataIntegrityViolationException和事务不起作用

时间:2019-07-25 15:37:40

标签: java spring-boot spring-data-jpa h2

我正在尝试使用 Java 8 Spring Boot,H2(内存DB) JPA 创建一个简单的搜索系统。

我的目标是显示用户搜索的关键字的统计信息。为此,我使用saveHistory通过@Transactional方法进行了处理。 这是我的实体和服务来源。


@Entity
@Data
@Builder
@Table(name="keyword")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class KeywordVO {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int keywordSeq;

    @Column(unique=true)
    private String keyword;

    @Column(columnDefinition = "integer default 1")
    private int cnt;

}

@Entity
@Getter
@Builder
@Table(name="search_history")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class SearchHistoryVO {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int searchSeq;

    @ManyToOne
    @JoinColumn(name="userSeq")
    private UserVO user;

    @ManyToOne
    @JoinColumn(name="keywordSeq")
    private KeywordVO keyword;

    private String datetime;

}

@Entity
@Data
@Builder
@Table(name="user")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class UserVO {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int userSeq;

    @Column(unique=true)
    private String username;

    private String password;

}

@Transactional
public void saveHistory(String query, String username) {
    try {
        KeywordVO targetKeyword; 
        KeywordVO keyword = keywordRepo.findByKeyword(query);
        if(keyword == null) {
            KeywordVO newKeyword = KeywordVO.builder()
                                            .keyword(query)
                                            .cnt(1)
                                            .build()
            keywordRepo.save(newKeyword);
            targetKeyword = newKeyword;
        } else {
            keyword.setCnt(keyword.getCnt() + 1);
            targetKeyword = keyword;
        }

        historyRepo.save(SearchHistoryVO.builder()
                                        .user(userRepo.findByUsername(username))
                                        .keyword(targetKeyword)
                                        .datetime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                                        .build());
    } catch(Exception e) {
        throw new RuntimeException();
    }
}

看起来有点复杂,但这很简单。如果关键字不在数据库中,则插入关键字;如果关键字不在数据库中,则更新关键字(计数+ 1)。之后,使用该关键字序列(关键字实体的ID)保存历史记录。 为了进行测试,我使用了如下的test方法。

public void test() {
    final int PPL_COUNT = 15;
    IntStream.range(0, PPL_COUNT).forEach(i -> userService.signup(UserVO.builder()
                                                                .username(String.format("user%04d", i))
                                                                .password(String.format("pass%04d", i))
                                                                .build()));

    ExecutorService executor = Executors.newFixedThreadPool(20);
    IntStream.range(0, 200).forEach(i -> executor.submit(() -> {
        try {
            historyService.saveHistory(
                        String.format("query%d", new Random().nextInt(20)),
                        String.format("user%02d", i % PPL_COUNT));
        } catch(Exception e) {
            e.printStackTrace();
        }
    }));
}

我的结果出乎意料。

  

首先,关键字的顺序根本不是顺序的。

here is the result of keyword table

如您所见,序列增加而没有任何规则。 为什么会出现这个结果?

  

第二,@ Transactional无效。

我希望它们是相同的,但是插入的“历史记录”行计数始终大于“关键字”表中的计数总和。 如果交易正常,则计数相同。不是吗?

  

第三,发生DataIntegrityViolationException。

线程将抛出此异常(约束[“ PUBLIC.KEYWORD(KEYWORD)值4”;“ PUBLIC.UK_PS9OKH3RAHHF7Q9O9N01CXSPI_INDEX_F ON; SQL语句。)

是JPA generateValue(identity)运行缓慢的原因吗?

我很困惑,因为我的代码无法正常工作!谁能告诉我为什么?

0 个答案:

没有答案