gpt4 book ai didi

java - 为什么在同一实体上调用 getOne() 后 "findById()"返回代理?

转载 作者:行者123 更新时间:2023-11-30 07:40:00 24 4
gpt4 key购买 nike

在我的网络应用程序中,在服务布局中,我使用“餐厅”实体的代理(“餐厅”字段上的 FetchType.Lazy)。

  User user = userRepository.get(userId);
/*
Getting proxy here, not restaurant object
*/
Restaurant userRestaurantRef = user.getRestaurant();

if (userRestaurantRef != null){
restaurantRepository.decreaseRating(userRestaurantRef.getId());
}

restaurantRepository.increaseRating(restaurantId);
/*
"getReference" invokes "getOne()"
*/
user.setRestaurant(restaurantRepository.getReference(restaurantId));
userRepository.save(user);

在测试中通过 Controller 调用此方法后,所有其他 RestaurantRepository 的获取方法(例如 findById())返回也代理。

但是,如果我在我的服务方法之前调用“findById()”方法,就没问题。

例如:

mockMvc.perform(put(REST_URL + RESTAURANT1_ID)
.param("time", "10:30")
.with(userHttpBasic(USER)))
.andExpect(status().isNoContent());

Restaurant restaurant = restaurantRepository.get(RESTAURANT1_ID);

“餐厅”是代理

Restaurant restaurantBefore = restaurantRepository.get(RESTAURANT1_ID);

mockMvc.perform(put(REST_URL + RESTAURANT1_ID)
.param("time", "10:30")
.with(userHttpBasic(USER)))
.andExpect(status().isNoContent());

Restaurant restaurantAfter = restaurantRepository.get(RESTAURANT1_ID);

“restaurantAfter”是真实对象

“get()”进入存储库:

    @Override
public Restaurant get(int id) {
return repository.findById(id).orElse(null);
}

最佳答案

您是否在方法或服务类本身上有 @Transactional 注释?

这可以解释观察到的行为。

当一个方法在事务中执行时,从数据库获取或合并/保存到数据库的实体被缓存直到事务结束(通常是方法结束)。这意味着任何对具有相同 ID 的实体的调用都将直接从缓存中返回,而不会访问数据库。

这里有一些关于 Hibernate 的缓存和代理的文章:

回到你的例子:

  • 先调用findById(id),然后调用getOne(id)为两者返回相同的实体对象
  • 先调用 getOne(id) 然后 findById(id) 为两者返回相同的代理

那是因为它们共享相同的id并且在同一个事务中执行。

关于 getOne() 的文档指出它可以返回一个实例而不是引用 (HibernateProxy),因此让它返回一个实体可以预计:

T getOne(ID id)

Returns a reference to the entity with the given identifier.

Depending on how the JPA persistence provider is implemented this is very likely to always return an instance and throw an EntityNotFoundException on first access. Some of them will reject invalid identifiers immediately.

Parameters: id - must not be null.

Returns: a reference to the entity with the given identifier.

另一方面,关于 findById() 的文档没有任何关于它可以返回任何东西的提示,除了实体的 Optional 或空的 Optional:

Optional findById(ID id)

Retrieves an entity by its id.

Parameters: id - must not be null.

Returns: the entity with the given id or Optional#empty() if none found

我花了一些时间寻找更好的解释,但没有找到,所以我不确定它是 findById() 实现中的一个错误还是只是一个错误(好)记录的功能。

作为解决问题的方法,我建议:

  1. 不要在同一交易方法中两次收购同一实体。 :)
  2. 避免在不需要时使用@Transactional。交易也可以手动管理。以下是关于该主题的一些好文章:
  3. 在使用其他方法(重新)加载之前分离第一个加载的实体/代理:
import javax.persistence.EntityManager;
import org.springframework.transaction.annotation.Transactional;

@Transactional
@Service
public class SomeServiceImpl implements SomeService {

private final SomeRepository repository;
private final EntityManager entityManager;

// constructor, autowiring

@Override
public void someMethod(long id) {
SomeEntity getOne = repository.getOne(id); // Proxy -> added to cache

entityManager.detach(getOne); // removes getOne from the cache

SomeEntity findById = repository.findById(id).get(); // Entity from the DB
}
  1. 类似于第三种方法,但不是从缓存中删除单个对象,而是使用 clear() 方法一次删除所有对象:
import javax.persistence.EntityManager;
import org.springframework.transaction.annotation.Transactional;

@Transactional
@Service
public class SomeServiceImpl implements SomeService {

private final SomeRepository repository;
private final EntityManager entityManager;

// constructor, autowiring

@Override
public void someMethod(long id) {
SomeEntity getOne = repository.getOne(id); // Proxy -> added to cache

entityManager.clear(); // clears the cache

SomeEntity findById = repository.findById(id).get(); // Entity from the DB
}

相关文章:


编辑:

这是一个simple project展示问题或功能(取决于观点)。

关于java - 为什么在同一实体上调用 getOne() 后 "findById()"返回代理?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58509408/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com