简体   繁体   中英

Spring MVC Transactional Best Practices for this

I have a controller method which retrieves an User, then I've got mapped their UserConfig, and then with that UserConfig I retrieve the MainBrands (lazy collection of UserConfiguration).

Let me clarify this:

User Entity:

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

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

}

And my UserService:

public interface UserService {

    public User getById(Long id);

}

So my question is about "best practices" of transactional annotations. I have read more than once, that put @Transactional at Controller level, is bad practice. But in this case I wanna do at 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());
    }
}

That will fail if don't put the @Transactional annotation at Controller level, because of LazyInitializationException.

So, that's the choices that I've thinked out:

1) With the user make a call to an "UserConfigService" (it's not created now) like userConfigService.getUserConfigByUserId(userId): that's make me think that if I already have the binding at User class, why I would call it again? And I am just creating a new service only for this method.

2) Put the @Transactional annotation at controller level: which makes another problem for my, but it doesn't care in this post.

3) Call the getUserConfig() & getUserConfig().getMainBrands() at UserService so then the collection get initialized: don't like because whenever I use the getById it will initialize the collection even if I do not need it.

So what it would be a good practice for this case? On internet there are always perfect and beautiful examples, but when we start to give some business logic to the project, it turns hard to have a clean code.

Thanks, and sorry for my english.

LazyInitializationException is not related to transactional , it is related to relationship between objects, if your object has a lazy relation,you must fetch your MainBrands objects in your userService.getById(userId) query method before you return your user.

Transactional annotation must be in service class, you can create as many service classes as you need.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM