我有一个控制器方法来检索用户,然后我已经映射了他们的UserConfig,然后使用该UserConfig我检索MainBrands(用户配置的惰性集合)。
让我澄清一下:
用户实体:
@Entity
@Table(name = "app_user")
public class User extends BaseEntity {
private UserConfig userConfig;
@OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
public UserConfig getUserConfig() {
return userConfig;
}
//more props..
}
UserConfig实体:
@Entity
@Table(name = "user_config")
public class UserConfig extends BaseEntity {
private Set<MainBrand> mainBrands;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(...)
public Set<MainBrand> getMainBrands() {
return mainBrands;
}
//more props..
}
我的UserService:
public interface UserService {
public User getById(Long id);
}
所以我的问题是关于交易注释的“最佳实践”。我不止一次读过,把@Transactional放在Controller级别,这是不好的做法。但在这种情况下,我想在Controller做:
@RequestMapping(method = RequestMethod.GET, value = "/")
public ModelAndView getMainPage(Long userId) {
ModelAndView = new ModelAndView("/home");
//do stuff
User user = userService.getById(userId);
//some stuff with user
modelAndView.addObject("username", user.getUsername());
//...
List<String> brandsNames = new ArrayList<>();
for(MainBrand mainBrand : user.getUserConfig().getMainBrands()){
brandsNames.add(mainBrand.getName());
}
}
如果由于LazyInitializationException而未将@Transactional注释放在Controller级别,那将失败。
所以,这是我所想到的选择:
1)用户调用“UserConfigService”(现在没有创建),如userConfigService.getUserConfigByUserId(userId):这让我觉得如果我已经在User上绑定了上课,为什么我会再打一次?而我只是为这种方法创建一项新服务。
2)将@Transactional注释放在控制器级别:这会给我带来另一个问题,但在这篇文章中并不关心。
3)调用getUserConfig()&amp;在UserService的getUainConfig()。getMainBrands()然后集合初始化:不喜欢因为每当我使用getById时它会初始化集合,即使我不需要它。
那么这个案例的好习惯是什么?在互联网上总是有完美而美丽的例子,但是当我们开始为项目提供一些业务逻辑时,很难有一个干净的代码。
谢谢,对不起我的英语。
答案 0 :(得分:0)
LazyInitializationException 与事务无关,它与对象之间的关系有关,如果对象具有惰性关系,则必须在 userService.getById(userId)中获取MainBrands对象 返回用户之前的查询方法。
事务性注释必须在服务类中,您可以根据需要创建任意数量的服务类。