我对Jpa和并发访问有疑问。 这里是示例代码
用户
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String firstName;
private String lastName;
...
UserService
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Transactional
public String getFirstName(Long userId) throws InterruptedException {
User user = userRepository.findById(userId).get();
Thread.sleep(5000L); // 5s
// ...
return user.getFirstname();
}
@Transactional
public void updateUser(Long userId, String firstName, String lastName) {
User user = userRepository.findById(userId).get();
user.setFirstname(firstName);
user.setLastname(lastName);
userRepository.save(user);
}
}
如果我同时调用两个端点(在POST之前先获取GET),则getFirstName方法将覆盖更新,因为在事务结束时,即使没有更新,JPA也会进行隐式保存。我的更新将被覆盖,因为在我的getFirstName方法开始时,我使用旧的名字加载了用户
如果不调用存储库的save方法,是否可以禁用同步? 我不想每次都分离实体。而且,我不想将事务置于只读状态(也许我需要在getFirstName方法中更新另一个实体)。
如果我没有进行任何更改,我不希望JPA自动保存我的实体。由于并发访问,这可能是一个问题。
谢谢
编辑(解决)
我使用了归因转换器 用户
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String firstName;
private String lastName;
@Convert(converter = SimpleConverter.class)
private customObject object;
...
@Component
@Converter
public class SimpleConverter implements AttributeConverter<CustomObject, String> {
private static ObjectMapper objectMapper;
@SneakyThrows
@Override
public String convertToDatabaseColumn(CustomObject object) {
if (Objects.isNull(object)) {
return null;
}
return objectMapper.writeValueAsString(object);
}
@SneakyThrows
@Override
public CustomObject convertToEntityAttribute(String data) {
if (StringUtils.isBlank(data)) {
return null;
}
return objectMapper.readValue(data, CustomObject.class);
}
@Autowired
public void setObjectMapper(ObjectMapper objectMapper) {
TutorialConverter.objectMapper = objectMapper;
}
}
我无法解释为什么,但是使用此转换器,即使没有进行任何更改,我的实体也会在每次事务结束时在数据库中进行更新。
我删除了此属性,现在当同时执行两个方法时,getFirstName方法不会覆盖数据
编辑2
CustomObject类还不等于方法...大错误。那解释了这种行为。谢谢!
答案 0 :(得分:1)
通过删除@ Transactional,persistanceContext将不会被刷新。或者,您可以使用@Transactional(readOnly = true)。您可以在此处了解更多信息:https://vladmihalcea.com/spring-read-only-transaction-hibernate-optimization/
答案 1 :(得分:1)
正如M. Deinum指出的那样,您必须在“ getFirstName”中的“ User”对象上进行某些更改才能进行更新,否则,休眠将不会更新数据库中的实体。
话虽这么说,一种更好的做法是从服务中返回整个“用户”对象
public User getUser(Long userId) throws InterruptedException {
return userRepository.findById(userId).orElse(null); // or throw an expection / return optional
}
然后您将使用该服务:
userService.getUser().getFirstname()
这更加灵活,可以隐式防止此类问题