全栈之路-Springboot初试
前言
距离2018年结束还有15天,也是我正式步入开发行业的第三个年头,他们都说步入开始后悔,但是仔细想想其实这三年来还是挺好的,有枯燥乏味,有焦躁不安,有自暴自弃,还有在第二天早晨重新焕发坐上清晨的公交车奔向公司。
三年来的时光,先后换了4家公司,第一家公司呆了近2年,而后的更换公司却有些频繁,从重庆再到杭州,虽然我很不想这样,但是这个行业好像就那么如此。
第一年的工作是Android开发,让我对Android有了深入的了解,包括一些硬件基础摄像头,蓝牙,USB接口,还有音频合成等,也打下了不错的java基础。
第二年的工作主要倾向于web前端开发,公司业务逐步拓展,作为一线开发者的我开始学习前端,从html,css,js,再到jquery,vue,微信小程序,也为我打开了前端开发的知识大门。
第三年的工作是有些偏向于管理方向,开始带领公司小团队,同时也更换两家公司,这一年开始学习了javaweb的学习,从年初到年末,先后利用工作之余,学习了Servlet、Jsp、struts2,spring等,最后开始对spring全家桶着迷。
这一路走来,第一家公司跟着公司大佬学习,学会了对代码严谨,还有测试,如今始终还记老板时常对我的念叨(老板是技术出身的):敲代码是一件很神圣的事情,所以要对代码充满敬畏之心,所以修改代码必须细心再细心,提交代码要反复测试。那段时间让我知道了对测试的重要性,还有代码的严谨。我真的很感谢老板,收留了毕业后走投无路的我,也教会了很多知识。第二家公司,担任了公司项目经理,负责公司项目体系策划,开发团队管理,从需求分析到最后项目上线,也让我有了一把应聘别人的经验。所以我觉得开发并不是那么无聊,关键看做开发的人,大概我也是假的开发,不过经常被人吐槽万金油确实不好受。
开始
理论知识
javaweb后端开发,对于正式接触这个技术栈的时候,第一印象就是数据,每天所做就是与数据打交道,从简单上考虑,所有的后端开发无论java,php,pythan,还是其他的语言无非就是四种姿势:
- 增
- 删
- 改
- 查
但是说起来就四个字,但是只要真正接触了后端开发后才发现,这四个字不简单。
增
增,我们要考虑几种业务模块场景:
1.直接增加一条数据(新增用户)
2.查询后增加指定字段(主要为更新字段)
3.新增表(数据新增表,与现有表相关联,列如:用户表,关联表就有用户登录信息表,会员表,权限表等)
4.新增字段(业务需求新增字段,不过最好前期架构好字段)
......
增加数据这业务模块,对于菜鸟而言估计就是要啥数据,就不断新增,于是就导致数据库臃肿,业务链繁琐,于是查询数据缓慢,最后后台服务器不堪重负崩了。
删
删,删除的业务场景同样比繁琐。
1.删除单条信息(常见为注销用户,一般产品这些不会作为直接删除业务)
2.删除多条信息(遍历删除,或则条件删除,常见为注销用户,删除他的所有关联信息)
......删除业务又同时依赖于查询业务模块
改
修改,也是更新,业务逻辑也是在后端业务中比较繁琐的一个部分
1.字段修改(例如更新用户登录状态,商品状态,订单状态)
2.多条记录修改(一般为分类常见,多数为一对多表,多对多表场景)
3.多表修改(查询多表修改,耗时很长,应该避免出现超过3个表同时修改场景,减少服务器负担)
.....
查
查询,是后端数据的重中之中,因为前端数据展示,才有后端数据的可用之处,查询的模块就相对比较多
1.单条记录查询(查询某个业务场景)
2.多条记录查询(查询列表,常见为商品列表,用户列表等)
3.按照某个字段排序查询(升序,降序)
4.分页查询
......
以上就是后端大概业务场景的分析
springboot代码是采用注解方式开发一般分为三部分:@Entity数据库类,JpaRepository接口类,@RestController业务实体类。
- JpaRepository是用于整合业务查询模块。
接下我们根据具体的场景来实现具体的springboot代码。
代码实践
springboot提供整套的封装方法,大量地减少了许多业务场景,接下来我们看看springboot为我们封装的常用方法,CrudRepository源码如下:
import java.util.Optional;
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
//保存对象
<S extends T> S save(S var1);
//根据迭代器来保存
<S extends T> Iterable<S> saveAll(Iterable<S> var1);
//根据id来查询值
Optional<T> findById(ID var1);
//id是否存在
boolean existsById(ID var1);
//使用配置的TypeRepresentationStrategy来加载所有实体,可能会返回一个大的结果
Iterable<T> findAll();
//根据迭代器查找
Iterable<T> findAllById(Iterable<ID> var1);
// 使用配置的TypeRepresentationStrategy,根据策略,此数字可能是近似值
long count();
//根据迭代器查找id来删除给定的实体
void deleteById(ID var1);
//通过调用其entity.remove()方法删除给定实体
void delete(T entity);
//根据迭代器删除对象集合
void deleteAll(Iterable<? extends T> var1);
//删除此类型的所有实体,请小心使用
void deleteAll();
}
- 其中PagingAndSortingRepository 继承于CrudRepository
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll(); //查询全部
List<T> findAll(Sort var1);//排序查询
List<T> findAllById(Iterable<ID> var1);//根据id查询
<S extends T> List<S> saveAll(Iterable<S> var1);
//刷新对数据库的所有挂起更改。
void flush();
//保存实体并立即刷新更改。
<S extends T> S saveAndFlush(S var1);
//删除批处理中的给定实体,这意味着它将创建单个实体[`Query`]
void deleteInBatch(Iterable<T> var1);
//删除批量调用中的所有实体。
void deleteAllInBatch();
//返回对具有给定标识符的实体的引用。
T getOne(ID var1);
<S extends T> List<S> findAll(Example<S> var1);
<S extends T> List<S> findAll(Example<S> var1, Sort var2);
}
以上就是springboot给我们提供的封装常用方法的源码,下面我们来实战运用。
新增&更新
- 在springboot中新增和更新都可以调用CrudRepository 中的save方法来保存数据。
需求:注册用户
场景:注册用户需要用户输入简单的手机号码,密码进行注册登录。并且生成用户特定uid字段。
数据类:
@Entity
public class User {
//表id
@Id
@GeneratedValue
private Integer id;
//用户id
private String uid;
//用户昵称
private String user_name;
//用户电话
private String user_phone
//用户密码
private String user_pwd;
//get&set省略.....
}
JpaRepository接口类:
@Repository
public interface UserRepository extends JpaRepository<User,Integer> {}
业务类:
@RestController
@RequestMapping("/api")
@CrossOrigin
public class UserController {
@Autowired
private UserRepository userRepository;
/**
* 注册用户
* @param user
* @return
*/
@PostMapping(value = "/register")
public RespEntity addUser(User user) {
if (user != null) {
List<User> users = userRepository.queryUser(user.getUserPhone());
if (users == null) {
//判断手机号是否正确
if (!StringUtil.isExistLong(user.getUserPhone())) {
return new RespEntity(RespCode.WARN, "请输入手机号码");
}
//判断密码是否输入正确
if (!StringUtil.isExist(user.getUserPwd(), 8)) {
return new RespEntity(RespCode.WARN, "请输入正确的密码");
}
//获取添加时间
Long addtime = DateUtil.getTime();
//将时间与手机号生成MD5编码
String uid = MD5.md5(String.valueOf(addtime), user.getUserPhoto());
if (uid!= null&&uid.length()>0) {
user.setUid(uid);
//保存数据
userRepository.save(user);
return new RespEntity(RespCode.SUCCESS, user);
}else {
return new RespEntity(RespCode.WARN,"生成用户身份ID失败,请重试");
}
} else {
return new RespEntity(RespCode.WARN, "用户已存在");
}
} else {
return new RespEntity(RespCode.WARN);
}
}
}
以上就实现了注册用户,特别地简单。
- RespEntity是返回封装。
- StringUtil是判断数据字符是否为空工具类。
- queryUser 方法是使用在JpaRepository中的 @Query条件查询实现的,后面会说明。
/**
* 更新用户
*
* @param user
* @return
*/
@PostMapping(value = "/updatauser")
public RespEntity updataUser(User user) {
//查询是否有这个用户
User oldUser = userRepository.findAll().get(user.getId());
if (oldUser != null) {
//如果用户存在就更新
userRepository.save(user);
return new RespEntity(RespCode.SUCCESS);
} else {
return new RespEntity(RespCode.WARN);
}
}
更新功能就这么简单,当然这还不够完善。
注意事项:
- 建立每个表格都要增加时间字段,比如新增时间,更新时间,如果需要做接口访问统计的最好还要新增一个访问接口统计字段。
- 访问数据的时候为了数据安全,都要对其进行身份验证。
删除
需求:删除(注销)用户
场景:根据用户手机号删除用户
- 这里就需要用到JpaRepository功能,需要给他扩展接口方法,我现在需要一个根据用户手机号查询到该条数据的方法,如下:
@Query(value = "select * from user as u where u.user_phone=?",nativeQuery = true)
List<User> queryUser(Long userPhone);
然后再在Controller中调用查询到该用户:
@PostMapping(value = "/deluser")
public RespEntity delUser(@RequestParam("userPhone") Long userPhone) {
//查询是否有该手机用户
List<User> users=userRepository.queryUser(Long.valueOf(userPhone));
if (users!=null&&users.size()>0){
//获取该用户
User user= users.get(0);
if (user!=null){
//调用删除
userRepository.delete(user);
return new RespEntity(RespCode.SUCCESS,"删除成功");
}else{
return new RespEntity(RespCode.WARN,"用户不存在");
}
}else {
return new RespEntity(RespCode.WARN,"用户不存在,请重新输入");
}
}
查询
上面的增删改中,其实也提到几种查询方法:
- 查询整个数据表findAll
repository.findAll();
- 用户 @Query条件查询
@Query(value = "select * from user as u where u.user_phone=?",nativeQuery = true)
List<User> queryUser(Long userPhone);
然后比较常用的还有排序查询,eg:
需求:根据年龄大小排序查询用户
- 首先我们需要在数据表类里新增一个年龄字段:
@Column(name="user_sex")
private Integer userSex;
- @Column(name="user_sex") 代表把该字段作为排序字段,值为user_sex。
/**
* 查询文章列表#热度查询-阅读量
*/
@GetMapping(value = "/like_usersex_list")
public RespEntity findListSortSingleCondition() {
Sort sort = new Sort(Sort.Direction.ASC, "user_sex");
List<User> userlist= userRepository.findAll(sort);
if (userlist!= null && userlist.size() > 0) {
return new RespEntity(RespCode.SUCCESS, userlist);
} else {
return new RespEntity(RespCode.WARN);
}
}
- Sort 对象为排序条件对象,需要提交排序条件+排序按照升序还是降序。
1.Sort.Direction.ASC 升序排列
2.Sort.Direction.DESC 降序排列 - 多条件查询
/**
* 多条件排序
*/
public List<User> findListSortMultiCondition() {
List<Sort.Order> orders = new ArrayList<Sort.Order>();
Sort.Order orderId = new Sort.Order(Sort.Direction.DESC, "id");
Sort.Order orderAge = new Sort.Order(Sort.Direction.DESC, "user_sex");
orders.add(orderId);s
orders.add(orderAge);
Sort sort = new Sort(orders);
return userRepository.findAll(sort);
}
- 除此之外还有分页查询,springboot也提供了详细的分页查询api,就是利用Pageable ,如下:
@Query(value = "select u from article u where u.name like %?1% ")
Page<Article> findUserLikeByPage(String name, Pageable page);
总结
springboot还有很多乐趣,作为初学javaweb的人有很大便捷,至少对于我而言,不再是想以前那么提起后台开发就觉得是一件很高深莫测的东西,有些东西没有去接触尝试,就不要对其产生畏惧,那是自己杀死了自己。
三年前我有了小梦想就是成为全栈工程师,当初有人说,你能够把一项搞懂就行了,但是三年的今天,我已经跨出了那么一小步,虽然这一步很小,未来还很远,但是我还是想沿着这条路走下去,看看这条路未来的路是怎么样子的,万金油也好,我只是想活着自己想要活着的样子罢了。
既然想了,就去做吧,干嘛想那么多有的没的。