Spring Framework,在处理数据时增强性能

时间:2014-09-17 22:39:58

标签: hibernate jsp spring-mvc jpa database-performance

应用

Spring-MVC应用程序与hibernate4集成,以及spring安全性。 1个控制器,两个视图,MySQL DB。

方案

用户实体,其中一些列表类似于用户角色 userPosts 之间的关系。< / p>

该应用程序具有与弹簧安全性集成的简单控制器。

用户登录后,可以查看自己的帖子或发布新帖子。

登录过程需要数据库命中,检索角色(Lazily fetched),以及帖子(Lazily fetched),数据被填满在用户实体实例中,然后通过控制器立即在模型中传递。

问题是:

在创建新的Post JSP(简单表单)中,我正在访问ModelAttribute( loggedUser )以获取当前登录用户的实例,我想,无需再次获取用户并建立所有的存储库会话都重新开始。但听起来没有办法这样做,因为我注意到存储的用户实体数据(我登录后存储)丢失了实体内的所有列表!同时转发到视图。这意味着任何新请求都需要再次填充ModelAttribute。

我尝试的事情:

我尝试启用User类的类变量并将其设置为在DB上执行操作,以存储检索到的数据,然后在Model.addAttribute中分配它,但没有运气,因为只有那些列表(List User.userRoles,List User.userPosts)不断变空!

我尝试使用ModelAndView.addObject,但又引发了一个新问题,它与将实体分配给字符串有关吗?!

我花了将近三天的时间来分析并找出处理这些请求的机制  但听起来我必须始终按下DB,因为我的实体的Lazily加载列表总是被刷新!

我的问题是:

1 - 有没有办法在服务/存储库中保存检索到的实体数据,包括列表,同时将其填入ModelAttribute / model.addAttribute中? (请注意,我不想包含用于取消延迟抓取的过滤器)

2-为什么使用sessionAttribute成功存储实体数据但丢失了那些延迟加载的项目?如果我们认为在调用新的控制器方法时会话被清除,则会清除列表。

3-是否有任何建议的场景可以保存性能,或者最小化从DB获取数据的命中量?我错过了一些关键的设计/逻辑良好实践吗?

感谢您的时间。

更新

好的,谢谢 jmvivo M. Deinum 为您的答案。我可以解决这个问题,现在一旦从数据库中获取数据,我就可以共享它,无需在重定向/新控制器调用时再次命中数据库。

解决方案:

使用Map模型存储已记录的用户,并将其传递给不同的页面。

观察和注释:

使用实体,服务和存储库创建的对象都类似于一个数据模型,该模型必须暂时保留并在调用方法的范围完成后清除所有已用资源。这就是用户实体在将某些数据部分转发到其他控制器或其他方法之后将其清除的原因。

换句话说,我只需要创建一个用户类或一些对象来获取jpa检索到的数据的副本,这样dao对象在DAO被清除后对我的读取数据没有影响。

这个分离DAO层和实现层的概念缺失了,我想通过这个小实践,我第一次理解它远离理论。 另一个观察是,由于我可以重定向到jsp页面,我也可以调用映射的控制器方法将任何结果传递给它,而不需要使用Spring的调度程序来执行此操作(这只是为了节省不必要的重新映射处理): / p>

@requestMapping("/home")
public String home(Map..){
// if authorised

//get user from map no need to call service to fill user based on principal name
SomeUserClass user = map.get("userfromDB") // user will be here :)
//.. some logic


return "home-page";
} 

@requestMapping("/post")
public String createUserPost(Map.. map, Model model, Principal p){

// access DB here and store result into map
SomeUserClass user = service.prepareAUserWithUserName(p.getName());

// save user copy in map, after the scope ends or another controller method happens the user will lose all lazily loaded data, but the map will be maintained by the chain.
map.put("userfromDB", user);


// if I want to redirect to home I can:

// call 1-home(Map)

// or   2- return the result of home function after setting model again 
// or simply return the page without the need for extra processing as result would be the same
// return "home-page"

//.. some logic
return "home-page";

} 

这对我的应用程序的性能有很大影响。 我希望这会对某人有所帮助。

2 个答案:

答案 0 :(得分:0)

你看看Spring cache abstraction吗?这将处理服务层级别的缓存。例如:

@Cacheable({ "books", "isbns" })
public Book findBook(ISBN isbn) {...}
  

在上面的代码段中,方法findBook与名为books的缓存相关联。每次调用该方法时,都会检查缓存以查看调用是否已经执行且不必重复

祝你好运!

答案 1 :(得分:0)

我建议从用户中删除收集帖子。并非一切都必须是双向关系。所有双向关系的问题在于您的模型可能变得过于复杂,而hibernate会开始生成大型查询。接下来你要在你的应用程序中分离某些东西(想象一下你会有一个通用的安全库,在那种情况下如何将用户链接到帖子?)。

删除后,您将拥有一个service / dao方法来检索给定用户的帖子。然后在您的控制器中,您只需执行查找并将其添加到模型中。

public String showPosts(Model model) {
    String username = // Use Spring Security to get the username
    List<Posts> posts = postService.findPostsForUser(username);
    model.addObject("posts", posts);        
}

或者,如果你真的想将它存储在http会话中。

public String showPosts(HttpSession session) {
    String username = // Use Spring Security to get the username
    List<Posts> posts = postService.findPostsForUser(username);
    session.addAttribute("posts", posts);     
}

除此之外,您还可以在服务上使用弹簧缓存抽象来缓存特定时期的结果。

public class MyPostService implements PostService {

     @Cacheable("posts")
     public List<Posts> postService(String username) { ... }

}

此处的关键是,当用户修改帖子/添加新帖子等时,您不会忘记使缓存无效。