1.用户的注册功能。相当于在做数据的插入操作
insert into t_user (username,password) values (值列表)
2.在用户注册时要首先去查询当前的用户名是否存在,如果存在则不能进行注册。相当于是一条查询语句
select * from t_user where username=?
定义Mapper接口。在项目的目录下创建mapper包,在这个包下根据不同的功能模块来创建mapper接口。创建一个UserMapper的接口。要在接口中定义这两个抽象方法。
public interface UserMapper { /** * 插入用户的数据 * @param user 用户的数据 * @return 受影响的行数(增,删,改,都用受影响行数作为返回值) */ Integer insert(User user); /** * 根据用户名来查询用户的数据 * @param username 用户名 * @return 如果找到对应的用户则返回这个用户的数据,如果没有则返回null */ User findByUsername(String username); }
1.定义xml映射文件,与对应的接口进行关联。所有的映射文件需要放在resources目录下,在这个目录下创建一个mapper文件夹,然后在这个文件夹下存放Mapper映射文件
2.创建接口对应的映射文件,遵循和接口的名称保存一致即可。创建一个UserMapper.xml文件。
3.配置接口中的方法对应上SQL语句上。需要借助标签来完成,insert\update\delete\select,对应的是SQL语句的增删改查操作。
4.resultMap 定义匹配不同命名规则的属性,并把resultMap的id传入到标签的resultMap属性里面
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.cy.store.mapper.UserMapper"> <resultMap id="UserEntityMap" type="com.cy.store.entity.User"> <id column="uid" property="uid"></id> <result column="is_delete" property="isDelete"></result> <result column="created_user" property="createdUser"></result> <result column="created_time" property="createdTime"></result> <result column="modified_user" property="modifiedUser"></result> <result column="modified_time" property="modifiedTime"></result> </resultMap> <insert id="insert" useGeneratedKeys="true" keyProperty="uid"> INSERT INTO t_user(username, password, salt, phone, email, gender, avatar, isDelete,createdUser, createdTime, modifiedUser, modifiedTime) VALUES (#{username}, #{password}, #{salt}, #{phone}, #{email}, #{gender}, #{avatar}, #{isDelete},#{createdUser}, #{createdTime}, #{modifiedUser}, #{modifiedTime}) </insert> <select id="findByUsername" resultMap="UserEntityMap"> SELECT * FROM t_user WHERE username = #{username} </select> </mapper>
每个独立的层编写完毕后都需要写单元测试方法,来测试当前功能。在test包结构下创建一个mapper包,在这个包下再创建持久层的功能测试。
@SpringBootTest @RunWith(SpringRunner.class) public class UserMapperTests { @Autowired private UserMapper userMapper; @Test public void insert() { User user = new User(); user.setUsername("tim"); user.setPassword("123"); Integer rows = userMapper.insert(user); System.out.println(rows); } @Test public void findByUsername(){ User user = userMapper.findByUsername("tim"); System.out.println(user); } }
1.RuntimeException异常:作为这类异常的子类,然后再去定义具体的异常类型来继承这个异常。业务层异常的基类,ServiceException异常。这个异常继承RuntimeException异常。异常机制的建立。
public class ServiceException extends RuntimeException{ public ServiceException() { super(); } public ServiceException(String message) { super(message); } public ServiceException(String message, Throwable cause) { super(message, cause); } public ServiceException(Throwable cause) { super(cause); } protected ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
根据业务层不同的功能来详细定义具体的异常的类型,统一的去继承ServiceException异常类。
2.业务异常:用户在进行注册时候可能会产生用户名被占用的错误,抛出一个异常:UsernameDepulitedException异常
public class UsernameDuplicatedException extends ServiceException{ public UsernameDuplicatedException() { super(); } public UsernameDuplicatedException(String message) { super(message); } public UsernameDuplicatedException(String message, Throwable cause) { super(message, cause); } public UsernameDuplicatedException(Throwable cause) { super(cause); } protected UsernameDuplicatedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
3.其他异常:正在执行数据插入操作的时候,服务器宕机。处于正在执行插入的过程中所产生的异常InsertException
1.在service包下创建一个IUserService接口。
public interface IUserService { /** * 用户注册方法 * @param user 用户的数据对象 */ void reg(User user); }
2.创建一个实现类UserServiceImpl类,需要实现这个接口,并且实现抽象方法
@Service public class UserServiceImpl implements IUserService { @Autowired private UserMapper userMapper; @Override public void reg(User user) { String username = user.getUsername(); User result = userMapper.findByUsername(username); if (result != null){ throw new UsernameDuplicatedException("用户名被占用"); }else { String oldPassword =user.getPassword(); String salt = UUID.randomUUID().toString().toUpperCase(); String md5Password = getMD5Password(oldPassword, salt); user.setPassword(md5Password); user.setSalt(salt); user.setIsDelete(0); user.setCreatedUser(user.getUsername()); user.setModifiedUser(user.getUsername()); Date date = new Date(); user.setCreatedTime(date); user.setModifiedTime(date); Integer rows = userMapper.insert(user); if(rows != 1){ throw new InsertException("在用户注册过程中产生了未知异常"); } } } private String getMD5Password(String password,String salt){ for (int i = 0; i < 3; i++) { password=DigestUtils.md5DigestAsHex((salt+password+salt).getBytes(StandardCharsets.UTF_8)).toUpperCase(); } return password; } }
3.在单元测试包下创建一个UserServiceTests类,在这个类中添加单元测试的功能。
@SpringBootTest @RunWith(SpringRunner.class) public class UserServiceTests { @Autowired private IUserService userService; @Test public void reg(){ try { User user=new User(); user.setUsername("yuanxin03"); user.setPassword("123"); userService.reg(user); System.out.println("ok"); }catch (ServiceException e){ System.out.println(e.getClass().getSimpleName()); System.out.println(e.getMessage()); } } }
状态码,状态描述信息,数据。这部分功能封装在一个类中,将这类作为方法返回值,返回给前端浏览器。
public class JsonResult<E> implements Serializable { // 状态码 private Integer state; // 描述信息 private String message; // 数据 private E data; }
根据当前的业务功能模块,进行请求的设计
请求路径:/users/reg 请求参数:User user 请求类型:Post 请求结果:JsonResult<void>
1.创建控制层对应的类UserController类,依赖于业务层的接口。
2.异常捕获
在控制层抽离父类,在这个父类中统一的去处理关于异常的相关操作。编写一个BaseController类,统一处理异常。
1.在register页面中编写发送请求的方法,点击事件来完成。选中对应的按钮(\(("选择器")),再去添加点击的事件,\).ajax()函数发送异步请求。
2.JQuery封装了一个函数,称之为$ajax()函数,通过对象调用ajax()函数,可以异步加载相关的请求。依靠的是JavaScript提供的一个对象XHR(XmlHttpResponse),封装了这个对象。
3.ajax()的使用方式。需要传递一个方法体作为方法的参数来使用:
$.ajax({ url:"", type:"", data:"", dataType:"", success:function(){ }, error:function(){ } });
4.ajax()函数参数的含义:
5.js代码可以独立在一个js的文件里或声明在一个script标签中
依据用户提交的用户名和密码做select查询。
select * from t_user where username=?
1.用户名对应的密码错误,密码匹配失败的异常:PasswordNotMatchException,运行时异常,业务异常。
2.用户名没有找到:UsernameNotFoundException,运行时异常,业务异常。
3.异常的编写
1.直接在IUserService接口中编写抽象方法,login(String username,String password)。将当前登录成功的用户数据以当前用户对象的形式返回。状态管理:可以将数据保存在cookie或者session中,可以避免重复度很高的数据多次频繁操作数据进行获取(用户id-在session中,用户的头像-cookie中)。
2.在实现类中实现父接口的抽象方法
3.在测试类中测试业务层登录的方法是否可以执行通过。
4.如果一个类没有手动创建直接将这个类复制到项目,idea找不到这个类。解决:重新构建
业务层抛出的异常是什么,需要在统一异常处理类中进行统一的捕获和处理,如果业务层抛出的异常类型在已经在统一异常处理类中曾经处理过,则不需要重复处理
请求路径:/users/login 请求参数:String username, String password 请求类型:Post 请求结果:JsonResult<User>
在UserController类中编写处理请求的方法。
@RequestMapping("/login") public JsonResult<User> login(String username,String password){ User login = userService.login(username, password); return new JsonResult<>(OK,login); }
1.在login.html页面中依据前面所设置的请求来发送ajax请求。
2.访问页面进行用户的登录操作。
session对象主要存在服务器端,可以用于保存服务器的临时数据的对象,所保存的数据可以在整个项目中都可以通过访问来获取,把session的数据看作一个共享的数据源。首次登陆的时候所获取的用户数据,转移到session对象即可。
session.getAttrbute("key")可以将获取session中的数据的这种行为进行封装,封装在BaseController类中。
封装session对象中数据的获取,数据的设置。(用户登录成功后,进行数据的设置,设置到全局的session对象)
在父类中封装两个数据:获取uid和获取username对应的两个方法。用户头像暂不考虑,封装在cookie中
在登录的方法中将数据封装在session对象中。服务器本身自动创建有session对象,已经是一个全局的session对象。SpringBoot直接使用session对象,直接将HttpSession类型的对象作为请求处理方法的参数,会自动将全局的session对象注入到请求处理方法的session形参上
@RequestMapping("/login") public JsonResult<User> login(String username, String password, HttpSession session){ User login = userService.login(username, password); session.setAttribute("uid",login.getUid()); session.setAttribute("username",login.getUsername()); System.out.println(getUidFromSession(session)); System.out.println(getUsernameFromSession(session)); return new JsonResult<>(OK,login); }
拦截器:首先将所有的请求统一拦截到拦截器中,开发者可以在拦截器中定义过滤的规则,如果不满足系统的设置的过滤规则,统一的处理是重新去打开login.html页面(重定向和转发),推荐使用重定向。
在SpringBoot项目中实现拦截器的定义和使用。SpringBoot是依靠SpringMVC来完成的。SpringMVC提供了一个接口HandlerInterceptor接口,用于表示定义一个拦截器。首先,自定义一个类,再用这个类去实现这个接口。
1.自定义一个类,用这个类实现HandlerInterceptoer接口。
/** * 定义一个拦截器 */ public class LoginInterceptor implements HandlerInterceptor { /** * 检测全局session对象中是否有uid数据,如果有则放行,如果没有则重定向到登录页面 * @param request 请求对象 * @param response 响应对象 * @param handler 处理器 * @return 如果返回值为true表示放行当前的请求,如果返回值为false则表示拦截当前的请求 * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object uid = request.getSession().getAttribute("uid"); if (uid==null){ response.sendRedirect("/web/login.html"); return false; } return true; } }
2.注册过滤器:添加白名单(哪些资源可以在不登录的情况下访问:login.html\register.html\login\reg\index.html\product.html)、添加黑名单(登录时才可以访问的页面资源)
3.注册过滤技术:借助WebMvcConfigure接口,可以将用户定义的拦截器进行注册,才可以保证拦截器能够生效和使用。定义一个类,然后让这个类实现WebMvcConfigure接口。配置信息,建议存放在项目的config包结构下。
public class LoginInterceptorConfigurer implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { WebMvcConfigurer.super.addInterceptors(registry); } }
需要用户提交原始密码和新密码,再根据当前登录的用户进行信息的修改操作。
根据用户的uid修改用户的password值。
update t_user set password=?,modified_user?=modified_time=?where uid=?
根据uid查询用户的数据。在修改密码之前,首先要保证当前这用户的数据存在(is_delete),检测是否被标记为删除、检测输入的原始密码是否正确。
select * from t_user where uid=?
1 用户输入的原密码错误 PasswordNotMatch异常
2 用户已删除
3 uid找不到用户
4 update更新时产生的未知的异常,UpadateException
执行用户修改密码的核心方法
UpdateException需要配置在统一的异常处理方法中。
/users/changPassword POST String oldPassword,String newPassword JsonResult<void>
@RequestMapping("/change_password") public JsonResult<Void> changePassword(String oldPassword,String newPassword,HttpSession session){ Integer uid = getUidFromSession(session); String username = getUsernameFromSession(session); userService.changePassword(uid,username,oldPassword,newPassword); return new JsonResult<>(OK); }
1.更新用户信息的SQL语句
update t_user set phone=?,email=?,gender=?,modified_user=?,modified_time=? where uid=?
2.根据用户名来查询用户的数据
select * from t_user where uid=?
更新用户的信息方法的定义。
/** * 更新用户的数据信息 * @param user 用户的数据 * @return 返回值为受影响的行数 */ Integer updateInfoByUid(User user);
UserMapper.xml文件
<update id="updateInfoByUid"> UPDATE t_user SET <if test="phone!=null">phone=#{phone},</if> <if test="email!=null">email=#{email},</if> <if test="gender!=null">gender=#{gender},</if> modified_user=#{modifiedUser}, modified_time=#{modifiedTime} WHERE uid=#{uid} </update>
@Test public void updateInfoByUid(){ User user=new User(); user.setUid(11); user.setPhone("1232"); user.setEmail("98670@qq.com"); user.setGender(1); Integer integer = userMapper.updateInfoByUid(user); System.out.println(integer); }
设计两个功能:
当打开页面时,获取用户的信息并填充到对应的文本框中。
检测用户是否点击了修改按钮,如果检测到则执行修改用户信息的操作。
打开页面的时候可能找不到用户数据,点击删除按钮之前需要再次的去检测用户的数据是否存在
主要有两个功能的模块,对应的是两个抽象的方法的设计。
/** * 根据用户的uid查询用户数据 * @param uid * @return 用户的数据 */ User getByUid(Integer uid); /** * 修改用户信息 * @param session * @param user */ void changeInfo(Session session,User user);
在UserServiceImpl类中添加两个抽象方法的具体实现。
@Override public User getByUid(Integer uid) { User result = userMapper.findByUid(uid); if (result==null||result.getIsDelete()==1){ throw new UsernameNotFoundException("用户数据不存在"); }else { User user=new User(); user.setEmail(result.getEmail()); user.setPhone(result.getPhone()); user.setGender(result.getGender()); return user; } } @Override public void changeInfo(Integer uid,String username, User user) { User result = userMapper.findByUid(uid); if (result==null||result.getIsDelete()==1){ throw new UsernameNotFoundException("用户数据不存在"); }else { user.setUid(uid); user.setModifiedUser(username); user.setModifiedTime(new Date()); Integer integer = userMapper.updateInfoByUid(user); if (integer!=1){ throw new UpdateException("更新数据时产生未知异常"); } } }
@Test public void getByUid(){ System.out.println(userService.getByUid(11)); } @Test public void changeInfo(){ User user = new User(); user.setPhone("1234"); user.setEmail("56565@qq.com"); user.setGender(0); userService.changeInfo(11,"denlu",user); }
1.设置-打开页面就发送当前用户的查询。
/users/get_by_uid GET HttpSession session JsonResult<User>
2.点击修改按钮发送用户的数据修改操作请求的设计。
/users/change_info POST User user,HttpSession session JsonResult<Void>
1.在打开userdata.html页面自动发送ajax请求,查询到的数据填充到这个页面。
2.在检测到用户点击了修改按钮之后发送了一个ajax请求(change_info)。
将对象文件保存在操作系统上,然后再把这个文件路径给记录下来,因为在记录路径的时候是非常的便捷和方便的。将来如果要打开这个文件,可以根据路径去找到这个文件。所以数据库里只要保存文件存储路径即可。将保存的静态资源(图片,文件,其他资源文件)放在某台电脑上,再把这台电脑作为一台单独的服务器使用。
对应是一个用户avatar的sql语句
update t_user set avatar=?,modified_user=?,modified_time=? where uid=?
UserMapper接口中来定义这个给抽象方法用于修改用户头像
UserMapper.xml文件中编写映射的SQL语句。
1.用户数据不存在,找不到对应的用户的数据。
2.更新的时候,各种未知的异常产生。
/** * 根据uid修改头像 * @param uid 用户的id * @param avatar 用户的头像路径 * @param username 用户名 */ void changeAvatar(Integer uid,String avatar,String username);
@Override public void changeAvatar(Integer uid, String avatar, String username) { User user = userMapper.findByUid(uid); if(user==null||user.getIsDelete().equals(1)){ throw new UsernameNotFoundException("用户数据不存在"); } Integer integer = userMapper.updateAvatarByUid(uid, avatar, username, new Date()); if(integer!=1){ throw new UpdateException("更新用户头像产生未知的异常"); } }
父类:FileUploadException 泛指文件上传的异常 FileEmptyException 文件为空的异常 FileSizeException 文件大小超出限制 FileTypeException 文件类型异常 FileUploadIOException 文件读写的异常 FileStateException 文件状态异常
五个构造方法显式声明出来,再去继承相关的父类
在基类BaseController类中进行编写和统一处理。
if (e instanceof FileEmptyException){ result.setState(6000); result.setMessage("文件空异常"); }else if (e instanceof FileSizeException){ result.setState(6001); result.setMessage("文件大小异常"); }else if (e instanceof FileTypeException){ result.setState(6002); result.setMessage("文件类型异常"); }else if (e instanceof FileStateException){ result.setState(6003); result.setMessage("文件状态异常"); }else if (e instanceof FileUploadException){ result.setState(6004); result.setMessage("文件上传异常"); }
/users/change_avatar POST(GET请求提交数据2KB) HttpSession session,MutipartFile file JsonResult<String>
/** * MultipartFile接口是SpringMVC提供的一个接口,这个接口为我们包装了获取文件类型的数据(任何类型的file都可以接收),SpringBoot整合了SpringMVC,只需要在处理请求的方法参数列表上声明一个参数类型的MutipartFile的参数,然后SpringBoot自动将传递给服务的文件数据赋值给这个参数 * @param session * @param file * @return */ @RequestMapping("change_avatar") public JsonResult<String> changeAvatar(HttpSession session, @RequestParam("file") MultipartFile file) { if (file.isEmpty()){ throw new FileEmptyException("文件为空"); } if (file.getSize()>AVATAR_MAX_SIZE){ throw new FileSizeException("文件超出限制"); } if (!AVATAR_TYPE.contains(file.getContentType())){ throw new FileTypeException("文件类型不支持"); } String parent=session.getServletContext().getRealPath("upload"); File dir = new File(parent); if (!dir.exists()){ dir.mkdirs();//创建当前的目录 } String originalFilename = file.getOriginalFilename(); int index = originalFilename.lastIndexOf("."); String suffix=originalFilename.substring(index);//获取后缀(.XXX) String filename = UUID.randomUUID().toString().toUpperCase()+suffix;//拼接随机生成的文件名和前面获取到的字符串 File dest = new File(dir, filename);//在存储路径下写入同名空文件,传回空文件 try { file.transferTo(dest);//头像文件写入空文件中 } catch (FileStateException e) { throw new FileStateException("文件状态异常"); } catch (IOException e) { throw new FileUploadIOException("文件读写异常"); } Integer uidFromSession = getUidFromSession(session); String usernameFromSession = getUsernameFromSession(session); String avatar="/upload/"+filename; userService.changeAvatar(uidFromSession,avatar,usernameFromSession); return new JsonResult<>(OK,avatar); }
在upload页面中编写上传头像代码
说明:如果直接使用表单进行文件的上传,需要给表单显示的添加一个属性enctype="multipart/form-data"声明出来,不会将目标文件的数据结构做修改再上传,不同于字符串
SpringMVC默认为1MB文件可以进行上传,需要手动修改SpringMVC默认上传文件的大小。
方式1:直接在配置文件中进行配置
spring.servlet.multipart.max-file-size=10MB
方式2:采用Java代码的形式来设置文件的上传大小的限制。主类中进行配置,可以定义一个方法,必须使用@Bean修,返回值为MutipartConfigElement。在类的前面添加一个@Configuration进行修饰
@Bean MultipartConfigElement getMultipartConfigElement(){ MultipartConfigFactory multipartConfigFactory = new MultipartConfigFactory(); multipartConfigFactory.setMaxFileSize(DataSize.of(10, DataUnit.MEGABYTES)); return multipartConfigFactory.createMultipartConfig(); }
在页面中通过ajax请求来t提交文件,提交完成后,后端返回json串,从中解析出data中的数据,设置到img头像标签的src属性上
serialize()//将表单数据自动拼接成key=value的结构进行提交给服务器,一般提交是普通的控件类型中的数据(text\password\radio\checkbox)等等 FormdData类//将表单中数据保持原有结构进行数据的提交 --- new FormData($("#form")[0]); ajax默认处理数据时按照字符串的形式进行处理,以及默认会采用字符串的形式进行提交数据。所以要关闭这两个默认的功能。 --- processData:false, --- contentType:false,
更新头像成功后,将服务器返回的头像路径保存在客户端cookies对象中,然后每次检查用户打开上传头像页面,这个页面中通过ready()方法来自动检测去读取cookie中头像并设到src属性上。
1.设置cookie中的值
2.在upload页面中引入cookie.js文件
3.在upload.html页面中通过ready()自动读取cookie中的数据。
CREATE TABLE t_address ( aid INT AUTO_INCREMENT COMMENT '收货地址id', uid INT COMMENT '归属的用户id', name VARCHAR(20) COMMENT '收货人姓名', province_name VARCHAR(15) COMMENT '省-名称', province_code CHAR(6) COMMENT '省-行政代号', city_name VARCHAR(15) COMMENT '市-名称', city_code CHAR(6) COMMENT '市-行政代号', area_name VARCHAR(15) COMMENT '区-名称', area_code CHAR(6) COMMENT '区-行政代号', zip CHAR(6) COMMENT '邮政编码', address VARCHAR(50) COMMENT '详细地址', phone VARCHAR(20) COMMENT '手机', tel VARCHAR(20) COMMENT '固话', tag VARCHAR(6) COMMENT '标签', is_default INT COMMENT '是否默认:0-不默认,1-默认', created_user VARCHAR(20) COMMENT '创建人', created_time DATETIME COMMENT '创建时间', modified_user VARCHAR(20) COMMENT '修改人', modified_time DATETIME COMMENT '修改时间', PRIMARY KEY (aid) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
private Integer aid; private Integer uid; private String name; private String provinceName; private String provinceCode; private String cityName; private String cityCode; private String areaName; private String areaCode; private String zip; private String address; private String phone; private String tel; private String tag; private Integer isDefault;
当前收货地址功能模块:列表的展示,修改,删除,设置默认,新增收货地址。
开发顺序:新增-展示-设置默认-删除-修改
1.新增-对应的是插入语句
insert into t_address(除了aid外字段列表) values(字段值列表)
2.一个用户的收货地址最多只能有20条数据对应。在插入用户数据前,先做查询操作。收货地址逻辑控制方面的一个异常。
select count(*) t_address where uid=?
1.创建一个新的接口Address,在这个接口中来定义上面两个SQL语句抽象方法的定义。
/** * 插入用户的收货地址数据 * @param address 收货地址数据 * @return 受影响的行数 */ Integer insert(Address address); /** * 根据用户的uid统计用户记录的收货地址数量 * @param uid 用户的uid * @return 当前用户的收货地址总数 */ Integer countByUid(Integer uid);
resource下的AddressMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.cy.store.mapper.AddressMapper"> <resultMap id="AddressEntityMap" type="com.cy.store.entity.Address"> <id column="aid" property="aid"/> <result column="province_code" property="provinceCode"/> <result column="province_name" property="provinceName"/> <result column="city_code" property="cityCode"/> <result column="city_name" property="cityName"/> <result column="area_code" property="areaCode"/> <result column="area_name" property="areaName"/> <result column="is_default" property="isDefault"/> <result column="created_user" property="createdUser"></result> <result column="created_time" property="createdTime"></result> <result column="modified_user" property="modifiedUser"></result> <result column="modified_time" property="modifiedTime"></result> </resultMap> <insert id="insert" useGeneratedKeys="true" keyProperty="aid"> INSERT INTO t_address(uid, name, province_name, province_code, city_name, city_code,area_name, area_code, zip, address, phone, tel, tag, isDefault,created_user, created_time, modified_user, modified_time) VALUES (#{uid},#{name},#{provinceName},#{provinceCode},#{cityName},#{cityCode},#{areaName},#{areaCode},#{zip},#{address},#{phone},#{tel},#{tag},#{isDefault},#{createdUser},#{createdTime},#{modifiedUser},#{modifiedTime}) </insert> <select id="countByUid" resultType="java.lang.Integer"> SELECT count(*) from t_address WHERE uid=#{uid} </select> </mapper>
2.在test下的mapper文件夹下创建AddressMapperTests的测试类
如果用户是第一插入用户的收货地址,规则:当用户插入的地址是第一条,需要将当前地址作为默认收货地址,如果查询到统计总数为0,则将当前地址的is_default值设置为1。查询统计的结果为0不代表异常。查询结果大于20,这时需要抛出业务控制的异常AddressCountLimitException异常。自行创建这个异常。
1.创建一个IAddressService接口,在接口中定义业务的抽象方法