我正在尝试使用 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)运行缓慢的原因吗?
我很困惑,因为我的代码无法正常工作!谁能告诉我为什么?