本周日正常上课
Spring事务
事务(Transaction)通常用于保证业务的完整性,通常,会有某些业务(需要执行的某个任务目标)需要执行多条SQL语句才可以完成,而在多条SQL语句的执行过程中,可能出现前序的执行成功,而后续的执行失败,就会导致数据不安全(数据没有根据我们设定的业务规则来发生变化),为了解决这个问题,就必须使用事务,从而保证多条SQL语句全部执行成功,或者全部执行失败!
可以认为:只要某个业务涉及2次或以上的增、删、改操作(例如2次修改,或1次增加与1次修改等),都必须使用事务来保障业务的完整性!而查询操作与事务无关,因为查询操作不会对数据进行修改,则不会影响到数据的安全性!
在Spring中,使用事务的流程:
涉及的增、删、改操作,必须能判断成功与否,并且,在失败时,抛出某种
RuntimeException
(可以是RuntimeException
,也可以是它的子孙类异常);业务对应的方法(调用了2次或更多次增、删、改的方法)必须添加
@Transactional
,该注解表示该方法的执行过程是有事务保护的,即执行之前开启了事务,如果全部成功则提交事务,中途出现任何错误会回滚事务。开启
@Transactional
注解的驱动,即默认情况下,该注解是无效,因为无法识别!所以,在spring-dao.xml
中添加配置(每次开发项目时只需要配置1次即可):
Spring的事务处理
Spring的事务处理大致是:
开启事务
try {
调用带有@Transactional注解的方法
提交事务
} catch (RuntimeException e) {
回滚事务
}
Spring的事务管理是基于Spring AOP的!
@Transactional注解还可以添加在类的声明之前!但是,不推荐这样使用中!
提醒:复习事务的ACID、隔离、传播!
小结
在业务层,只要执行的是增、删、改操作,必须获取受影响的行数,并且,受影响的行数值不符合预期时,必须抛出某种RuntimeException
!
之所以开发过程中,自定义的的业务异常都需要继承自RuntimeException
,一方面是为了满足Spring管理事务的要求,另一方面是为了通过Spring管理异常(参见BaseController
)!
===============================================
6.2. 收货地址-查询列表
6.2.1. 收货地址-查询列表-持久层
在接口文件中添加新的抽象方法
List<Address> getList(Integer uid);
在配置文件中配置新的映射
SELECT * FROM t_address
WHERE uid=?
ORDER BY is_default DESC, id DESC
6.2.2. 收货地址-查询列表-业务层
在业务层声明与持久层同名的方法,并实现。
完成后测试。
6.2.3. 收货地址-查询列表-控制器层
设计获取当前用户的收货地址列表的请求:
请求路径:/address/list.do
请求类型:GET
请求参数:无,HttpSession.uid
响应方式:ResponseResult<List<Address>>
是否拦截:是,登录拦截器,无须再次调整配置
然后,添加处理请求的方法:
@RequestMapping("/list.do")
@ResponseBody
public ResponseResult<List<Address>> getList(
HttpSession session) {
// 获取uid
// 调用业务层方法获取列表
// 创建返回值对象
// 将列表封装到返回值对象中
// 执行返回
}
6.2.4. 收货地址-查询列表-前端页面
(对原文件改动较多,请参考视频回放)
6.3. 收货地址-设置默认收货地址
6.3.1. 收货地址-设置默认收货地址-持久层
在接口中声明2个抽象方法:
Integer setNonDefault(Integer uid);
Integer setDefault(
@Param("uid") Integer uid,
@Param("id") Integer id);
配置以上2个抽象方法的映射:
UPDATE t_address SET is_default=0 WHERE uid=?
UPDATE t_address SET is_default=1 WHERE id=? AND uid=?
6.3.2. 收货地址-设置默认收货地址-业务层
在业务层接口中添加:
/**
* 设置某用户的某条收货地址为默认收货地址
* @param uid 用户id
* @param id 被设置为默认收货地址的数据的id
*/
void setDefaultAddress(Integer uid, Integer id);
在实现类中,先添加与持久相同的2个方法(不过,权限设置为private即可),然后,重写以上接口中的方法
6.3.3. 收货地址-设置默认收货地址-控制器层
6.3.4. 收货地址-设置默认收货地址-前端页面
6.4. 收货地址-删除
6.5. 收货地址-修改地址信息
<script type="text/javascript">
function showList() {
var url = "../address/list.do";
$.ajax({
"url": url,
"type": "GET",
"dataType": "json",
"success": function(json) {
$("#address_list").empty();
var addresses = json.data;
for (var i=0; i < addresses.length; i++) {
console.log(addresses[i].recvName + ", " + addresses[i].recvDistrict + ", " + addresses[i].recvAddress + ", " + addresses[i].isDefault);
var html = '<div class="aim_content_one aim_active">'
+ '<span class="dzmc dzmc_active">#{tag}</span>'
+ '<span class="dzxm dzxm_normal">#{name}</span>'
+ '<span class="dzxq dzxq_normal">#{address}</span>'
+ '<span class="lxdh lxdh_normal">#{phone}</span>'
+ '<span class="operation operation_normal">'
+ ' <span class="aco_change">修改</span>|<span class="aco_delete">删除</span>'
+ '</span>'
+ '<span class="swmr swmr_normal"></span>'
+ '</div>';
html = html.replace("#{tag}", addresses[i].recvTag);
html = html.replace("#{name}", addresses[i].recvName);
html = html.replace("#{address}", addresses[i].recvDistrict + addresses[i].recvAddress);
html = html.replace("#{phone}", addresses[i].recvPhone);
if (addresses[i].isDefault != 1) {
html = html.replace("dzmc_active", "dzmc_normal");
html = html.replace(" aim_active", "");
}
$("#address_list").append(html);
}
},
"error": function(xhr, textStatus, errorThrown) {
// xhr:XMLHttpRequest类型的对象
// - responseText:响应的文本
// - readyState:状态,值为0~4
// - status:响应码
console.log("状态码:" + xhr.readyState);
console.log("响应码:" + xhr.status);
console.log("响应文本:" + xhr.responseText);
console.log("textStatus=" + textStatus);
console.log("errorThrown=" + errorThrown);
}
});
}
showList();
</script>