2019_07_08stor收货地址设置默认和删除 第八天

41. 收货地址-设为默认-持久层

(a) 规划SQL语句

如果需要把某条收货地址设置为默认,需要执行的SQL语句大致是:

update t_address set is_default=1,modified_user=?,modified_time=? where aid=?

除此以外,还需要将原有的默认地址设置为非默认,由于原默认收货地址数据的id可能是未知的,可以“将该用户的所有收货地址设置为非默认,然后再把指定的那条设置为默认”即可,所以,“将该用户的所有收货地址设置为非默认”的SQL语句大致是:

update t_address set is_default=0 where uid=?

在操作数据之前,还是应该对数据进行检查,例如:检查收货地址数据是否存在,对应的SQL查询是:

select aid from t_address where aid=?

以上查询时,查询的字段并不重要,最终只需要判断查询结果是否为null即可,即:用于判断将要被设置为默认的收货地址数据是否存在。

除此以外,由于参数aid是客户端提交的,应该视为不可靠数据,该aid对应的数据可能是不存在的,另外,也可能是他人的数据,所以,在查询时,还应该将uid也查询出来,用于和Session中的uid对比,以判断即将需要操作的数据的归属是否正常:

select uid from t_address where aid=?

(b) 接口与抽象方法

AddressMapper.java接口中,声明3个抽象方法:

Integer updateDefault(
	@Param("aid") Integer aid, 
	@Param("username") String username, 
	@Param("modifiedTime") Date modifiedTime);

Integer updateNonDefault(Integer uid);

Address findByAid(Integer aid);

© 配置映射

映射:

<!-- 将指定的收货地址设置为默认 -->
<!-- Integer updateDefault(
		@Param("aid") Integer aid, 
		@Param("username") String username, 
		@Param("modifiedTime") Date modifiedTime) -->
<update id="updateDefault">
	UPDATE
		t_address
	SET
		is_default=1,
		modified_user=#{username},
		modified_time=#{modifiedTime}
	WHERE
		aid=#{aid}
</update>

<!-- 将指定用户的收货地址全部设置为非默认 -->
<!-- Integer updateNonDefault(Integer uid) -->
<update id="updateNonDefault">
	UPDATE
		t_address
	SET
		is_default=0
	WHERE
		uid=#{uid}
</update>

<!-- 根据收货地址id查询收货地址详情 -->
<!-- Address findByAid(Integer aid) -->
<select id="findByAid"
	resultMap="AddressEntityMap">
	SELECT 
		*
	FROM 
		t_address 
	WHERE 
		aid=#{aid}
</select>

单元测试:

@Test
public void updateDefault() {
	Integer aid = 30;
	String username = "哈哈";
	Date modifiedTime = new Date();
	Integer rows = mapper.updateDefault(aid, username, modifiedTime);
	System.err.println("rows=" + rows);
}

@Test
public void updateNonDefault() {
	Integer uid = 7;
	Integer rows = mapper.updateNonDefault(uid);
	System.err.println("rows=" + rows);
}

@Test
public void findByAid() {
	Integer aid = 30;
	Address result = mapper.findByAid(aid);
	System.err.println(result);
}

42. 收货地址-设为默认-业务层

(a) 规划异常

此次设为默认的主要操作是Update操作,则可能抛出UpdateException

在操作之前,应该检查被设置为默认的数据是否存在,所以,可能抛出AddressNotFoundException

在检查时,还应该检查数据归属是否正确,即用户操作的是不是自己的数据,如果不是,则抛出AccessDeniedException

(b) 接口与抽象方法

IAddressService接口中添加:

void setDefault(Integer aid, Integer uid, String username) throws AddressNotFoundException, AccessDeniedException, UpdateException;

© 实现抽象方法

先从AddressMapper.java接口中复制新的抽象方法并粘贴到业务层实现类AddressServiceImpl中,并私有化实现这些方法:

然后,重写IAddressService接口中的抽象方法:

public void setDefault(Integer aid, Integer uid, String username) throws AddressNotFoundException, AccessDeniedException, UpdateException {
	// 根据参数aid查询数据
	// 判断查询结果是否为null
	// 是:AddressNotFoundException

	// 判断查询结果中的uid与参数uid是否不同
	// 是:AccessDeniedException

	// 将该用户的所有收货地址设置为非默认

	// 将指定的收货地址设置为默认
}

具体实现为:

@Override
@Transactional
public void setDefault(Integer aid, Integer uid, String username)
		throws AddressNotFoundException, AccessDeniedException, UpdateException {
	// 根据参数aid查询数据
	Address result = findByAid(aid);
	// 判断查询结果是否为null
	if (result == null) {
		// 是:AddressNotFoundException
		throw new AddressNotFoundException(
			"设置默认收货地址失败!尝试访问的数据不存在!");
	}

	// 判断查询结果中的uid与参数uid是否不同
	if (!result.getUid().equals(uid)) {
		// 是:AccessDeniedException
		throw new AccessDeniedException(
			"设置默认收货地址失败!不允许访问他人的数据!");
	}

	// 将该用户的所有收货地址设置为非默认
	updateNonDefault(uid);

	// 将指定的收货地址设置为默认
	updateDefault(aid, username, new Date());
}

完成后,单元测试:

@Test
public void setDefault() {
	try {
		Integer aid = 250;
		Integer uid = 7;
		String username = "呵呵";
		service.setDefault(aid, uid, username);
		System.err.println("OK.");
	} catch (ServiceException e) {
		System.err.println(e.getClass().getName());
		System.err.println(e.getMessage());
	}
}

43. 收货地址-设为默认-控制器层

(a) 统一处理异常

需要处理2个新的异常!

(b) 设计请求

设计“收货地址-设为默认”的请求方式:

请求路径:/addresses/{aid}/set_default
请求参数:@PathVariable("aid") Integer aid, HttpSession session
请求方式:POST
响应数据:JsonResult<Void>

http://localhost:8080/addresses/18/set_default

© 处理请求

@RequestMapping("{aid}/set_default")
public JsonResult<Void> setDefault(
	@PathVariable("aid") Integer aid, 
	HttpSession session) {
	// 从Session中获取uid和username
	Integer uid = getUidFromSession(session);
	String username = getUsernameFromSession(session);
	// 调用业务层对象执行设置默认
	addressService.setDefault(aid, uid, username);
	// 返回
	return new JsonResult<>(SUCCESS);
}

44. 收货地址-设为默认-前端界面

45. 收货地址-删除-持久层

(a) 规划SQL语句

当执行删除时,需要执行的SQL语句大致是:

delete from t_address where aid=?

在执行删除之前,还应该检查数据是否存在和数据归属是否正确,这项检查功能已经存在,无需再开发或调整。

如果删除的是“默认”的收货地址,如果这已经是最后一条收货地址,则无需进行后续的任何处理,可以在删除之后,再次通过此前的countByUid()进行查询,从而得知刚才删除的是不是最后一条收货地址。

如果删除的是“默认”的收货地址,且还有更多的收货地址,则应该把剩余的收货地址中的某一条设置为默认,可以制定规则“将最近修改的那条设置为默认”,设置为默认可以使用此前的updateDefault()直接完成,而“找出最近修改的收货地址”需要执行的SQL语句大致是:

select * from t_address where uid=? order by modified_time desc limit 0,1

(b) 接口与抽象方法

则需要在持久层接口中添加2个抽象方法:

Integer deleteByAid(Integer aid);

Address findLastModified(Integer uid);

© 配置映射

映射:

<!-- 根据收货地址数据的id删除收货地址 -->
<!-- Integer deleteByAid(Integer aid) -->
<delete id="deleteByAid">
	DELETE FROM
		t_address
	WHERE
		aid=#{aid}
</delete>

<!-- 查询某用户最后修改的收货地址数据 -->
<!-- Address findLastModified(Integer uid) -->
<select id="findLastModified"
	resultMap="AddressEntityMap">
	SELECT
		*
	FROM
		t_address
	WHERE
		uid=#{uid}
	ORDER BY
		modified_time DESC
	LIMIT
		0,1
</select>

测试:

@Test
public void deleteByAid() {
	Integer aid = 20;
	Integer rows = mapper.deleteByAid(aid);
	System.err.println("rows=" + rows);
}

@Test
public void findLastModified() {
	Integer uid = 7;
	Address result = mapper.findLastModified(uid);
	System.err.println(result);
}

46. 收货地址-删除-业务层

(a) 规划异常

删除时:DeleteException;

删除前:AddressNotFoundException,AccessDeniedException;

删除后:UpdateException。

(b) 接口与抽象方法

void delete(Integer aid, Integer uid, String username) throws AddressNotFoundException, AccessDeniedException, DeleteException, UpdateException;

© 实现抽象方法

私有化实现持久层中新增的方法:

/**
 * 根据收货地址数据的id删除收货地址
 * @param aid 收货地址数据的id
 * @throws DeleteException 删除数据异常
 */
private void deleteByAid(Integer aid) {
	Integer rows = addressMapper.deleteByAid(aid);
	if (rows != 1) {
		throw new DeleteException(
			"删除收货地址失败!出现未知错误!");
	}
}

/**
 * 查询某用户最后修改的收货地址数据
 * @param uid 用户的id
 * @return 该用户最后修改的收货地址数据
 */
private Address findLastModified(Integer uid) {
	return addressMapper.findLastModified(uid);
}

重写业务接口中的抽象方法:

@Transactional
public void delete(Integer aid, Integer uid, String username) throws AddressNotFoundException, AccessDeniedException, DeleteException, UpdateException {
	// 根据参数aid查询数据
	// 判断查询结果是否为null:AddressNotFoundException

	// 判断查询结果中的uid与参数uid是否不同:AccessDeniedException

	// 执行删除

	// 判断此前的查询结果中的isDefault是否为0:return

	// 根据参数uid统计收货地址数量
	// 判断数量为0:return

	// 根据参数uid查询最后修改的收货地址

	// 根据查询到的最后修改的收货地址中的aid执行设置默认
}

实现:

@Override
@Transactional
public void delete(Integer aid, Integer uid, String username)
		throws AddressNotFoundException, AccessDeniedException, DeleteException, UpdateException {
	// 根据参数aid查询数据
	Address result = findByAid(aid);
	// 判断查询结果是否为null
	if (result == null) {
		// 是:AddressNotFoundException
		throw new AddressNotFoundException(
			"删除收货地址失败!尝试访问的数据不存在!");
	}

	// 判断查询结果中的uid与参数uid是否不同
	if (!result.getUid().equals(uid)) {
		// 是:AccessDeniedException
		throw new AccessDeniedException(
			"删除收货地址失败!不允许访问他人的数据!");
	}

	// 执行删除
	deleteByAid(aid);

	// 判断此前的查询结果中的isDefault是否为0:return
	if (result.getIsDefault() == 0) {
		return;
	}

	// 根据参数uid统计收货地址数量
	Integer count = countByUid(uid);
	// 判断数量为0:return
	if (count == 0) {
		return;
	}

	// 根据参数uid查询最后修改的收货地址
	Address lastModifiedAddress = findLastModified(uid);

	// 根据查询到的最后修改的收货地址中的aid执行设置默认
	updateDefault(lastModifiedAddress.getAid(), username, new Date());
}

测试:

@Test
public void delete() {
	try {
		Integer aid = 26;
		Integer uid = 7;
		String username = "呵呵";
		service.delete(aid, uid, username);
		System.err.println("OK.");
	} catch (ServiceException e) {
		System.err.println(e.getClass().getName());
		System.err.println(e.getMessage());
	}
}

47. 收货地址-删除-控制器层

(a) 统一处理异常

处理:DeleteException

(b) 设计请求

设计“收货地址-删除”的请求方式:

/resources/id/command

请求路径:/addresses/{aid}/delete
请求参数:@PathVariable("aid") Integer aid, HttpSession session
请求方式:POST
响应数据:JsonResult<Void>

http://localhost:8080/addresses/18/delete

© 处理请求

@RequestMapping("{aid}/delete")
public JsonResult<Void> delete(
	@PathVariable("aid") Integer aid, 
	HttpSession session) {
	// 从Session中获取uid和username
	Integer uid = getUidFromSession(session);
	String username = getUsernameFromSession(session);
	// 调用业务层对象执行删除
	addressService.delete(aid, uid, username);
	// 返回
	return new JsonResult<>(SUCCESS);
}

48. 收货地址-删除-前端界面

49. 主页-热销排行-实体类

创建cn.tedu.store.entity.Product实体类,继承自BaseEntity

public class Product extends BaseEntity {
	// ...
}

50. 主页-热销排行-持久层

(a) SQL

应该按照数据的priority降序排列,取出排名最靠前的4样商品,则:

select * from t_product order by priority desc limit 0, 4

(b) 接口与抽象方法

创建cn.tedu.store.mapper.ProductMapper接口,声明抽象方法:

List<Product> findHotList();

© 配置映射

复制得到ProductMapper.xml配置文件,修改根节点的namespace属性值对应以上接口,然后配置以上抽象方法的映射:

<resultMap ..

<select ..

创建测试类,编写并执行单元测试:

...

51. 主页-热销排行-业务层

(a) 规划异常

(b) 接口与抽象方法

创建cn.tedu.store.service.IProductService接口,然后添加抽象方法:

List<Product> getHotList();

© 实现

创建cn.tedu.store.service.impl.ProductServiceImpl,实现IProductService接口,添加@Service注解,在类中声明@Autowired private ProductMapper productMapper;持久层对象。

私有化实现持久层接口中的方法:

重写IProductService接口中的抽象方法:

public List<Product> getHotList() {
	List<Product> list = findHotList();
	for (Product item : list) {
		// 把item中所有不必要的字段设置为null
	}
	return list;
}

创建测试类,编写并执行单元测试:

...

----------------------------------------------

基于spring-jdbc的事务(Transaction)

事务是数据库中可以保证多项数据操作要么全部成功,要么全部失败的机制,以此可以保障数据安全!

基于spring-jdbc的事务处理方式就是在业务方法之前添加@Transactional注解即可!

在spring-jdbc中,处理事务大致是:

开启事务
try {
	调用带@Transactional注解的业务方法
	提交:commit
} catch (RuntimeException e) {
	回滚:rollback
}

所以,要实现spring-jdbc对事务的管理,不仅仅只是添加@Transactional注解而已,还需要保证:在视为“失败”时,必须抛出RuntimeException或其子孙类异常的对象!

另外,@Transactional注解还可以添加在类之前,表示该类中所有的业务方法都是有事务保障的,但是并不推荐这样处理!

小结:

  1. 什么时候需要使用事务:当某项业务涉及2次或更多的增、删、改操作时,需要使用事务,例如:需要执行2次Update操作,或1次Insert操作加上1次Update操作等;

  2. 如何保证事务能正确执行:首先,每次增、删、改操作都必须获取受影响的行数,并判断行数是否是预期值,如果不是预期值,必须抛出RuntimeException或其子孙类异常的对象,然后,在业务方法之前添加@Transactional注解即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员西柚柚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值