【问题标题】:Spring MVC Transactional Best Practices for thisSpring MVC 事务最佳实践
【发布时间】:2016-07-01 15:24:31
【问题描述】:

我有一个检索用户的控制器方法,然后我映射了他们的 UserConfig,然后使用该 UserConfig 我检索 MainBrands(UserConfiguration 的惰性集合)。

让我澄清一下:

用户实体:

@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..
}

用户配置实体:

@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..

}

还有我的用户服务:

public interface UserService {

    public User getById(Long id);

}

所以我的问题是关于事务注释的“最佳实践”。我不止一次读过,将@Transactional 放在控制器级别,这是不好的做法。但在这种情况下,我想在 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());
    }
}

如果不将 @Transactional 注释放在控制器级别,那将失败,因为 LazyInitializationException。

所以,这就是我想出来的选择:

1) 与用户一起调用“UserConfigService”(现在尚未创建),例如 userConfigService.getUserConfigByUserId(userId):这让我认为,如果我已经在 User 处进行了绑定类,为什么我会再次调用它?我只是为这种方法创建一个新服务。

2) 将@Transactional 注释放在控制器级别:这对我来说是另一个问题,但在这篇文章中并不关心。

3) 在 UserService 调用 getUserConfig() 和 getUserConfig().getMainBrands() 以便初始化集合:不喜欢,因为每当我使用 getById 时,它甚至会初始化集合如果我不需要它。

那么对于这种情况,什么是好的做法?互联网上总是有完美漂亮的例子,但是当我们开始给项目一些业务逻辑时,很难有一个干净的代码。

谢谢,对不起我的英语。

【问题讨论】:

    标签: java spring hibernate controller transactional


    【解决方案1】:

    LazyInitializationException 与事务无关,它与对象之间的关系有关,如果您的对象具有惰性关系,则必须在 userService.getById(userId) 中获取 MainBrands 对象返回用户之前的查询方法。

    事务注解必须在服务类中,您可以根据需要创建任意数量的服务类。

    【讨论】:

    • 但是如果我在另一个控制器方法中使用相同的服务方法,我不介意是否获取 MainBrands。我会做一个获取连接查询什么的。如果是一个,那就没有问题了。但是如果我获取 50 个用户呢?我会获取 50 次不适合我的查询?
    • 我正在考虑将 getUser 方法更改为 getUserById(Long userId, boolean fetchCollections),但在这种情况下,集合位于嵌套的 UserConfig 对象中......我很困惑
    • 正是这种方式,将获取动态添加到 jpa CriteriaQuery。我应该建议你一个添加获取类似 public void addFetches(Root root, FetchElement fetch){root.fetch(fetch.getName(), fetch.getType());} 的方法并发送作为参数,你想在你的服务方法中获取那些元素,例如,像这样: List fetchList = new ArrayList(0); fetchList.add(new FetchElement(User_.mainbrands.getName(), JoinType.INNER));这仅在jpa中有效。
    猜你喜欢
    • 2015-09-11
    • 2015-04-19
    • 2011-05-18
    • 1970-01-01
    • 2018-09-25
    • 2011-02-04
    • 2014-06-26
    • 2011-05-05
    • 1970-01-01
    相关资源
    最近更新 更多