` 点击右上角“卧龙会IT技术”关注我们 我们与你一起畅享IT世界!
文|原创:卧龙会 Arivn
先说结论:[size=1em]spring openSession获取到的是hibernate session,并没有实际获取绑定数据库连接。
不到最后一刻,不操作db就不会获取实际的数据库连接。当第一次操作db时,hibernate session 才会绑定数据库物理连接。这样降低了session使用数据库连接时间片段,最大化的利用链接资源。
所以spring连接打开的session是hibernate Session,原创微信公众号:卧龙会IT技术。并不是实际数据库连接,可放心使用(提前Open hibernate Session 对数据库链接无消耗,所以spring filter才能帮助用户提前打卡session) 注:以下是基于hibernate和spring集成的前提下进行的。 一般spring的事物是配置在service上,所以来说service和controller上的情况 1 service 层情况绑定数据库物理连接是在第一次操作数据库的时候发生。操作数据库包括查询,修改,删除,新增,当然还有事物。在service中后续的db操作继续使用第一次绑定的数据库连接,直到service方法退出结束。 - 对于非事物的查询(PROPAGAtiON_SUPPORTS)
不管你在service查询前做了什么复杂的业务,都不会绑定数据库链接
如下代码:
@Override public UserInfo getById(long id) { log.info("操作前sleep 10s,模拟调用外部接口"); this.sleep(10); log.info("执行操作"); //以上时间段查看链接池,数据库链接未被消耗 UserInfo bean= baseDAO.get(UserInfo.class, id);//链接开始被消耗 //以下时间段,由于未退出方法,不会触发spring自动释放链接 //会一直暂用数据库链接 log.info("操作后sleep 10s"); this.sleep(10); log.info("方法结束"); return bean;//退出后释放链接 }- 对于事物。开启事物时打开链接(PROPAGATION_REQUIRED)
进入方法就绑定了数据库链接,所以事物里面的逻辑单元应该精简,不要有太多和事物无关的逻辑。比如提交一个http请求,在请求中会浪费掉数据库链接的时间
@Override public void saveTest(long id) throws BizException { //进入方法就绑定了数据库链接 this.sleep(10); long sn=System.currentTimeMillis();; baseDAO.executeHql("UPDATE UserInfoEx set address=? where id =?" ,new Object[]{sn+"",id});//已经处理完了 log.info("延迟10秒,"+sn); this.sleep(10);//模拟远程调用反馈执行结果 log.info("提交"+sn); //退出后释放链接 }service方法中调用内部其他方法
只会绑定一次数据库连接,第二次操作DB使用的是第一次操作DB绑定的链接,同样会把事物特性传递下来。 @Overridepublic UserInfo getById(long id) { //第一次操作绑定数据库链接 UserInfo bean= baseDAO.get(UserInfo.class, id); updateName(id,bean.getName()+System.currentTimeMillis()); // 不会重新绑定链接,继续使用baseDAO.get(UserInfo.class, id)绑定的链接。 baseDAO.save(bean); //也不会触发真实的保存,因为没有经过代理拦截,(在自己本身调用本身的方法,即原始目标方法),不会触发事物开启。 this.saveOptLog(id); return bean;}[size=1em]注意:上面的代码 baseDAO.save(bean)和 this.saveOptLog(id)并不会触发真实的保存和提交。事物和自动提交是加在service层,getById不会触发事物的开启和自动提交。如果saveOptLog是其他servcie中注入的方法会自动开启的事物,但是继续使用同一连接,如下代码: @Overridepublic UserInfo getById(long id) { //第一次操作绑定数据库链接 UserInfo bean= baseDAO.get(UserInfo.class, id); updateName(id,bean.getName()+System.currentTimeMillis()); baseDAO.save(bean); //1 不会重新绑定链接,logService中从线程变量中获取session,而session中已经由baseDAO.get绑定过物物理连接 //2 会触发真实的保存,因为注入的logService,是代理对象有spring增强,会自动开启事物和提交 logService.saveOptLog(id); return bean;}
- 2 controller 情况
每调用一次service都会绑定一个数据库物理连接,并且执行完后释放掉。
原因:service是spring注入进来的代理service,有事物增强处理。 @RequestMapping("/loginLog")@ResponseBodypublic JsonResponseBean loginLog(@RequestScope JSONObject jsonParams) throws BizException { JsonResponseBean responseBean = new JsonResponseBean(); UserInfo userInfo=userService.getById (jsonParams.getLongValue("id"));//绑定数据连接,执行,并释放 this.sleep(10);//模拟复杂的控制处理,此处不会消耗数据连接资源的时间片。 loginService.saveLoginLog(UserLoginLog.TYPE_PASSWORD, userInfo.getId(), userInfo.getName(), "","","","");//第一次调用,重新绑定连接,执行,释放连接 loginService.saveLoginLog(UserLoginLog.TYPE_PASSWORD, userInfo.getId(), userInfo.getName(), "","","","");//重复调用,重新绑定连接,执行,释放连接 return responseBean;}如果是处理重复业务数据,preparestatement不一定会生效。
原因:controller重复调用service的方法绑定到的数据库连接不一定是同一个,preparestatement是基于同一连接生效的。如果preparestatement要生效需要放在service内处理。 //controller代码public JsonResponseBean loginLog(@RequestScope JSONObject jsonParams) throws BizException { JsonResponseBean responseBean = new JsonResponseBean(); List ids=JsonUtils.toArray(jsonParams.getJSONArray("id"),Long.class); for(long id:ids) { UserInfo userInfo = userService.getById(id); loginService.saveLoginLog(UserLoginLog.TYPE_PASSWORD, userInfo.getId(), userInfo.getName(), "", "", "", "");//每次都是,重新绑定连接,执行,释放连接。 } return responseBean;}
3 控制代码划分规则建议
一般来说service根据模块功能和解耦,里面可能会包含部分的业务逻辑控制代码;controller本身是控制层,也会包含部分逻辑控制代码。那么逻辑控制代码怎么区分是方在service层还是controller呢? 如果是处理比较复杂的业务请求,并且可能消耗的时间比较久(正常情况应该走MQ进行异步处理,但这个地方讨论不能走异步必须面对的情况)。需要把消耗时间的业务逻辑控制上浮,放在controller中处理,即使是同一service。
erivce和controller的规则:
1 根据模块功能划分,并进行解耦
当仁不让,第一规则,优先考虑。 2 serivce层的逻辑粒度尽量小,尽量单一
保持service方法的逻辑简洁性,往往一个方法处理逻辑过多会很容易造成代码混乱,并且不容易维护。 3 在小粒度的方法前提下,控制业务逻辑进行上浮
如果是非事物,可以考虑吧逻辑代码上浮到controller,由controller进行组合调用service接口。
如果是事物业务,必须把代码逻辑放在service层,但也需要保持每个方法的小粒度,再由一个方法来进行业务逻辑层控制,并暴露给contrller使用。
当然非业务的事物情况下,也可以把部分contrller里面共同的逻辑,下潜到service实现。(具体根据service具体业务属性进行划分)
但同上,每个子方法都应该具有小粒度和单一的特性。 为什么要这样做?在上面提到,当第一次操作db后,如果方法一直没执行完并退出,会一直占用链接。这段时间链接是无法被其他人使用的,哪怕链接处于空闲状态。
这样的结果就是100个复杂的业务来了,service层使用了100个数据链进行长时间处理(连接池只提供100个链接),这时其他一个执行查询的小业务请求来了,由于链接全部被占用了,这时这地101个请求就无法处理,只有等。 如果service足够简单,让controller来控制部分业务逻辑(由一个一个小粒度的service方法组成),
A 调用一个service方法结束后会先把链接释放出来;
B 这时其他业务请求才有可能获取到链接,并进行处理,小请求处理完后会释放链接;(能很快的做出相应)
C 之前的contrller继续调用serivce方法,绑定到释放的链接,继续处理。(响应速度延迟不大)
这样100个链接能支持100个以上的请求业务,才能【实现高并发】。否则100个链接提供的并发支持非常有限。
关键思路:- 1 调用小粒度service方法,让使用完后,及时释放,尽量避免不用但一直占用资源的时间段产生
2 让使用db资源的时间片尽可能的小,处理过程中可以让其他需要资源的业务请求也能够参与进来 。原创微信公众号:卧龙会IT技术。
3 让想用资源的能及时用到资源,占用资源但空闲状态的释放资源,【最大化】利用有限的资源。
以上是大多数情况下的处理方式,但不能一篇而论。需要根据功能模块和业务场景选择性的使用。 ---好东西就要分享给你朋友圈---
想知道近来IT行业工资标准,关注微信公众号:卧龙会IT技术
给公从号回复“薪资调查”,我们把IT行业薪资调查报告发给你。
大家喜欢用微博的,关注我们微博 卧龙会微博:@卧龙会IT技术 我们同步更新到微博! `
0
|
|
|
|