Java|SpringBoot入门项目-基于JPA的App日记后台系统之数据库的创建与JPA的CRUD(二)

提要 本系列文章主要为带领SpringBoot初学者入门,本人也是一名初学者,文章若有不足之处,还望大家提出批评,加以指正!
注意:本系列文章为项目入门篇,非SpringBoot基础入门篇,如无SpringBoot相关基础的童鞋推荐慕课网廖师兄的入门教程

[2小时学会Spring Boot]
[Spring Boot进阶之Web进阶]
本人的开发环境及软件版本(仅供参考)
操作系统 : macOS 10.13.2 JDK : 1.8 Tomcat : 8.5.27 IntelliJ IDEA : 2017.3.2 (Ultimate Edition) MySQL : 5.7.21 Reids : 4.0.7

目录
  • 提要
  • 目录
  • 数据库的配置
    • 数据库的创建
    • 数据库表的建立
      • UserInfoEntity类
      • NoteInfoEntity类
      • 注解说明
  • JPA中Repository接口说明
      • JpaRepository接口
      • NoteRepository接口
      • UserRepository接口
  • 新建Service进行CRUD操作
      • NoteService接口
      • UserService接口
      • NoteService接口的实现类
      • UserService接口的实现类
  • 项目地址
  • 本系列文章目录

数据库的配置 数据库的创建 在上一章节的文章中我有提到本项目中所使用的数据库为MySQL,数据库的图形化管理软件我个人推荐使用Navicat。
现在我们来回顾一下上一章所提到的application.yml文件中MySQL数据库的配置信息。
spring: datasource: url: jdbc:mysql://127.0.0.1/test0110?characterEncoding=utf-8&useSSL=false username: root password: 123456

【Java|SpringBoot入门项目-基于JPA的App日记后台系统之数据库的创建与JPA的CRUD(二)】我们之前也提到了url中的test0110为数据库的名称,所以我们在运行项目之前必须先创建数据库,否则运行会直接报错!
下面我们将通过Navicat这个软件来新建数据库。
Java|SpringBoot入门项目-基于JPA的App日记后台系统之数据库的创建与JPA的CRUD(二)
文章图片

我们通过选中本地/远程的数据库,右键选择新建数据库。
Java|SpringBoot入门项目-基于JPA的App日记后台系统之数据库的创建与JPA的CRUD(二)
文章图片

根据图中所填写的信息进行填写,数据库名可根据个人喜好自行更改。(注意,application.yml文件中的数据库url路径里的数据库名必须与你所创建的数据库名保持一致)
点击确定后数据库即新建完成。
数据库表的建立 本项目中只需建立两张表便可实现需求,分别为UserInfo表和NoteInfo表,下面我们将通过代码来建立表与字段。
首先我们先建立entity包,在包下新建UserInfoEntity类与NoteInfoEntity类。
UserInfoEntity类
@Entity @Table(name = "user_info") @Data public class UserInfoEntity {@Id private String userId; private String userName; private String userPwd; private Integer userSex; private String userProfile; private String avatarUrl = "https://pic2.zhimg.com/50/v2-d757e91a15dde9792a1850aed2f1a1c8_hd.jpg"; public UserInfoEntity() {} }

NoteInfoEntity类
@Entity @Data @DynamicUpdate @Table(name = "note_info") public class NoteInfoEntity {@Id @GeneratedValue private Long noteId; private String userId; private String noteTitle; private String noteContent; @UpdateTimestamp private Date updateTime; public NoteInfoEntity() {} }

注解说明
@Entity 注解表示指定当前的类与数据库中的表进行映射,一个类对应一张表,一个变量对应一个字段。
@Table 注解表示指定当前类对应的表名。
@DynamicUpdate 注解表示当前类中的Date属性会动态更新。
@Data 注解为lombok中的注解,表示自动为你生成get/set方法,让你的代码更加整洁。
@Id 代表为主键。
@GeneratedValue 代表为自增。
@UpdateTimestamp 指示其自动更新。
数据库说明:
Data类中的变量名与数据库中字段名对应规则举栗:userName -> user_name
通过上一章节所提到的application.yml文件中的片段
jpa: hibernate: ddl-auto: update

可实现运行项目后根据标注了@Entity注解的类进行表与字段的更新,如数据库中无对应的表,则会自动创建。
JPA中Repository接口说明 数据库的表与字段都已经创建完毕了,接下来我们该干什么呢?没错,接下来我们将要对数据库进行增删查改相关操作。
我们先创建repository包,在repository包下分别创建NoteRepository接口和UserRepository接口,两个接口都需继承JpaRepository接口,让我们来先看一下JpaRepository接口里是什么内容。
JpaRepository接口
@NoRepositoryBean public interface JpaRepository extends PagingAndSortingRepository, QueryByExampleExecutor { List findAll(); List findAll(Sort var1); List findAll(Iterable var1); List save(Iterable var1); void flush(); S saveAndFlush(S var1); void deleteInBatch(Iterable var1); void deleteAllInBatch(); T getOne(ID var1); List findAll(Example var1); List findAll(Example var1, Sort var2); }

我们可以看到JpaRepository接口又继承于PagingAndSortingRepository接口与QueryByExampleExecutor接口,其中PagingAndSortingRepository接口中包含了两个findAll()方法
//用于迭代 Iterable findAll(Sort var1); //用于分页 Page findAll(Pageable var1);

其中返回类型为Page的我们项目后面会使用到,用于分页功能。Iterable在此不做讲解,如有兴趣的童鞋可自行查找相关资料。通过这里我们便可以知道jpa就是通过repository来对数据库进行CRUD操作的。
NoteRepository接口
//JpaRepository中包含两个输入参数,一个是泛型,对应指定的entity,一个是Id的类型 public interface NoteRepository extends JpaRepository { //查找指定用户的日记列表,并根据更新时间倒序排序后以分页的形式进行获取 Page findByUserIdOrderByUpdateTimeDesc(String userId, Pageable pageable); }

这里需要说明的是findByUserIdOrderByUpdateTimeDesc这个方法,在JpaRepository接口中进行数据的CRUD的时候,默认的几个方法便是findOne(id),findAll(),save(),update()和delete()等,如果需要根据用户自定义的条件进行查询的话,需要自己在该接口中写对应的方法。然而这个方法是有规矩可寻的,并不是乱写的。
findByUserIdOrderByUpdateTimeDesc这个方法的目的就是为了查找某个用户下的所有日记,并以更新时间倒序排序后的格式输出。那么findByUserId这个就是根据userId来进行查找,如果你写成userid是不会通过的,比如根据你给定的字段名来进行定义,OrderByUpdateTimeDesc的含义就是根据updateTime这个字段来倒序(desc)排序后进行输出。其他方法同理,如有不懂之处可网上自行搜索相关资料。
UserRepository接口
//JpaRepository中包含两个输入参数,一个是泛型,对应指定的entity,一个是Id的类型 public interface UserRepository extends JpaRepository {}

新建Service进行CRUD操作 到此我们的准备工作已经完毕,接下来便是本章内容的重头戏了!调用JpaRepository接口进行CRUD!小伙伴们是不是也很激动啊!?
先别激动,拿稳鼠标,放稳键盘,深呼吸一口!我们先新建一个service包,在service包下新建两个接口,NoteService接口和UserService接口。
NoteService接口
public interface NoteService { //获取用户下的所有日记列表(分页获取) Page noteList(HttpServletRequest request, Pageable pageable) throws Exception; //更新某个指定的日记 NoteInfoVO updateNote(HttpServletRequest request, UserNoteDTO userNoteDTO) throws Exception; // 添加日记 NoteInfoVO addNote(HttpServletRequest request, UserNoteDTO userNoteDTO) throws Exception; //删除日记 void delNote(HttpServletRequest request, UserNoteDTO userNoteDTO) throws Exception; }

UserService接口
public interface UserService { //用户注册 UserInfoVO userRegister(UserInfoEntity userInfoEntity) throws Exception; //用户登录 UserInfoVO userLogin(UserLoginEntity loginEntity, HttpServletResponse servletResponse) throws Exception; //更新密码 void updatePwd(HttpServletRequest request, UserInfoDTO userInfoDTO) throws Exception; //更新个人简介 void updateProfile(HttpServletRequest request, UserInfoDTO userInfoDTO) throws Exception; //获取个人信息 UserInfoVO userInfo(HttpServletRequest request) throws Exception; //用户退出登录(可忽略) void userLogout(HttpServletRequest request) throws Exception; }

看到这里可能有些小伙伴会有点懵逼,这传入的参数都是些啥啊?这里我给大家解释一下,HttpServletRequest这个参数是为了获取请求Cookie中的token和userId参数,用于用户权限校验,后续的文章中我会给大家逐步讲解token加密与cookie的解析等等(其实很简单)
NoteService接口的实现类
新建NoteServiceImpl类实现NoteService接口
//此类与controller层直接交互,进行逻辑处理 @Service @Slf4j public class NoteServiceImpl implements NoteService {@Autowired private NoteRepository noteRepository; @Autowired private IUserRedisService userRedisService; @Autowired private UserRepository userRepository; /** * 获取日记 * * @param pageable * @return * @throws Exception */ @Override public Page noteList(HttpServletRequest request, Pageable pageable) throws Exception { //判断用户是否存在 checkUser(getUserId(request)); //判断用户token checkToken(getToken(request), getUserId(request)); Page noteInfoPage = noteRepository.findByUserIdOrderByUpdateTimeDesc(getUserId(request), pageable); List noteInfoVOList = NoteEntity2NoteDTOConverter.convert(noteInfoPage.getContent()); return new PageImpl<>(noteInfoVOList, pageable, noteInfoPage.getTotalElements()); }/** * 更新日记 * * @param request * @param userNoteDTO * @return * @throws Exception */ @Override public NoteInfoVO updateNote(HttpServletRequest request, UserNoteDTO userNoteDTO) throws Exception {//判断用户是否存在 checkUser(getUserId(request)); //标题必传 if (StringUtils.isEmpty(userNoteDTO.getNoteTitle())) { throw new NoteException(ResultEnum.NOTE_TITLE_NON_ERROR); }//内容必传 if (StringUtils.isEmpty(userNoteDTO.getNoteTitle())) { throw new NoteException(ResultEnum.NOTE_CONTENT_NON_ERROR); }//noteId必传 if (StringUtils.isEmpty(userNoteDTO.getNoteId()) || userNoteDTO.getNoteId() == 0) { throw new NoteException(ResultEnum.NOTE_ID_NON_ERROR); }//判断用户token checkToken(getToken(request), getUserId(request)); NoteInfoEntity newInfoEntity = new NoteInfoEntity(); Util.copyPropertiesIgnoreNull(userNoteDTO, newInfoEntity); NoteInfoEntity infoEntity = noteRepository.save(newInfoEntity); //数据转换 NoteInfoVO infoDTO = new NoteInfoVO(); Util.copyPropertiesIgnoreNull(infoEntity, infoDTO); log.error("这是NoteInfoEntity=>", infoEntity); log.error("NoteInfoVO=>", infoDTO); return infoDTO; }/** * 添加日记 * * @param request * @param userNoteDTO * @return * @throws Exception */ @Override public NoteInfoVO addNote(HttpServletRequest request, UserNoteDTO userNoteDTO) throws Exception {//判断用户是否存在 checkUser(getUserId(request)); //标题必传 if (StringUtils.isEmpty(userNoteDTO.getNoteTitle())) { throw new NoteException(ResultEnum.NOTE_TITLE_NON_ERROR); }//内容必传 if (StringUtils.isEmpty(userNoteDTO.getNoteTitle())) { throw new NoteException(ResultEnum.NOTE_CONTENT_NON_ERROR); }//判断用户token checkToken(getToken(request), getUserId(request)); NoteInfoEntity newInfoEntity = noteRepository.findOne(userNoteDTO.getNoteId()); Util.copyPropertiesIgnoreNull(userNoteDTO, newInfoEntity); NoteInfoEntity infoEntity = noteRepository.save(newInfoEntity); //数据转换 NoteInfoVO infoDTO = new NoteInfoVO(); Util.copyPropertiesIgnoreNull(infoEntity, infoDTO); log.error("这是NoteInfoEntity=>", infoEntity); log.error("NoteInfoVO=>", infoDTO); return infoDTO; }/** * 删除日记 * * @param request * @param userNoteDTO * @throws Exception */ @Override public void delNote(HttpServletRequest request, UserNoteDTO userNoteDTO) throws Exception {//判断用户是否存在 checkUser(getUserId(request)); //noteId必传 if (StringUtils.isEmpty(userNoteDTO.getNoteId()) || userNoteDTO.getNoteId() == 0) { throw new NoteException(ResultEnum.NOTE_ID_NON_ERROR); }//判断用户token checkToken(getToken(request), getUserId(request)); noteRepository.delete(userNoteDTO.getNoteId()); }/** * 判断用户token * * @throws Exception */ private void checkToken(String token, String userId) throws Exception {Util.checkToken(token, userId, userRedisService); }/** * 获取Token * * @param request * @return */ private String getToken(HttpServletRequest request) { Cookie cookie = CookieUtil.getCookieByName(request, "token"); //token必传 if (null == cookie) { throw new NoteException(ResultEnum.TOKEN_NON_ERROR); } return cookie.getValue(); }/** * 获取userId * * @param request * @return */ private String getUserId(HttpServletRequest request) { Cookie cookie = CookieUtil.getCookieByName(request, "userId"); //userId必传 if (null == cookie) { throw new NoteException(ResultEnum.USERID_NON_ERROR); } return cookie.getValue(); }/** * 判断用户是否存在 * * @param userId * @return */ private void checkUser(String userId) { if (userRepository.findOne(userId) == null) { throw new NoteException(ResultEnum.USER_NOT_EXIST); } } }

UserService接口的实现类
新建UserServiceImpl类实现UserService接口
//此类与controller层直接交互,进行逻辑处理 @Service public class UserServiceImpl implements UserService {@Autowired private UserRepository userRepository; @Autowired private IUserRedisService userRedisService; /** * 用户注册 * * @param userInfoEntity * @return * @throws Exception */ @Override public UserInfoVO userRegister(UserInfoEntity userInfoEntity) throws Exception {//用户id必传 if (StringUtils.isEmpty(userInfoEntity.getUserId())) { throw new NoteException(ResultEnum.USERID_NON_ERROR); }//用户密码必传 if (StringUtils.isEmpty(userInfoEntity.getUserPwd())) { throw new NoteException(ResultEnum.PASSWORD_NON_ERROR); } //用户名必传 if (StringUtils.isEmpty(userInfoEntity.getUserName())) { throw new NoteException(ResultEnum.USERNAME_NON_ERROR); }if (null != userRepository.findOne(userInfoEntity.getUserId())) { throw new NoteException(ResultEnum.REGISTER_ERROR_HAD); }UserInfoVO result = new UserInfoVO(); //获取请求对象 UserInfoEntity infoEntity = userRepository.save(userInfoEntity); // 对象转换 BeanUtils.copyProperties(infoEntity, result); return result; }/** * 用户登录 * * @param loginEntity * @param servletResponse * @return * @throws Exception */ @Override public UserInfoVO userLogin(UserLoginEntity loginEntity, HttpServletResponse servletResponse) throws Exception {//用户id必传 if (StringUtils.isEmpty(loginEntity.getUserId())) { throw new NoteException(ResultEnum.USERID_NON_ERROR); }//用户密码必传 if (StringUtils.isEmpty(loginEntity.getUserPwd())) { throw new NoteException(ResultEnum.PASSWORD_NON_ERROR); }// 获取UserInfo对象 UserInfoEntity infoEntity = getUserInfo(loginEntity.getUserId()); if (!infoEntity.getUserPwd().equals(loginEntity.getUserPwd())) { throw new NoteException(ResultEnum.LOGIN_ERROR); }//生成token Map map = new HashMap<>(); map.put("id", infoEntity.getUserId()); map.put("date", System.currentTimeMillis()); map.put("name", infoEntity.getUserName()); String token = SecurityUtil.authentication(map); //保存用户token到redis userRedisService.saveToken(infoEntity.getUserId(), token); UserInfoVO userInfoVO = new UserInfoVO(); BeanUtils.copyProperties(infoEntity, userInfoVO); CookieUtil.addCookie(servletResponse, "token", token, 10); return userInfoVO; }/** * 修改密码 * * @param request * @param userInfoDTO * @throws Exception */ @Override public void updatePwd(HttpServletRequest request, UserInfoDTO userInfoDTO) throws Exception {//判断用户是否存在 checkUser(getUserId(request)); //用户密码必传 if (StringUtils.isEmpty(userInfoDTO.getOldPwd())) { throw new NoteException(ResultEnum.PASSWORD_NON_ERROR); }//用户新密码必传 if (StringUtils.isEmpty(userInfoDTO.getNewPwd())) { throw new NoteException(ResultEnum.PASSWORD_NEW_NON_ERROR); }//校验token checkToken(getToken(request), getUserId(request)); //校验旧密码 UserInfoEntity infoEntity = getUserInfo(userInfoDTO.getUserId()); if (!userInfoDTO.getOldPwd().equals(infoEntity.getUserPwd())) { throw new NoteException(ResultEnum.PASSWORD_CHECK_ERROR); }//设置新密码 infoEntity.setUserPwd(userInfoDTO.getNewPwd()); userRepository.save(infoEntity); }/** * 更新个人资料 * * @param request * @param userInfoDTO * @throws Exception */ @Override public void updateProfile(HttpServletRequest request, UserInfoDTO userInfoDTO) throws Exception {//判断用户是否存在 checkUser(getUserId(request)); //个人简介必传 if (StringUtils.isEmpty(userInfoDTO.getUserProfile())) { throw new NoteException(ResultEnum.PROFILE_NON_ERROR); }// 校验token checkToken(getToken(request), getUserId(request)); //修改profile UserInfoEntity infoEntity = getUserInfo(userInfoDTO.getUserId()); infoEntity.setUserProfile(userInfoDTO.getUserProfile()); //修改数据库 userRepository.save(infoEntity); }/** * 获取用户信息 * * @param request * @return * @throws Exception */ @Override public UserInfoVO userInfo(HttpServletRequest request) throws Exception {//判断用户是否存在 checkUser(getUserId(request)); // 校验token checkToken(getToken(request), getUserId(request)); //获取用户信息 UserInfoEntity infoEntity = getUserInfo(getUserId(request)); UserInfoVO vo = new UserInfoVO(); //拷贝数据 BeanUtils.copyProperties(infoEntity, vo); return vo; }/** * 退出,清除token * * @param request * @throws Exception */ @Override public void userLogout(HttpServletRequest request) throws Exception { userRedisService.removeToken(getUserId(request)); }/** * 判断用户token * * @param token * @param userId * @throws Exception */ private void checkToken(String token, String userId) throws Exception { Util.checkToken(token, userRedisService.getToken(userId)); }/** * 获取UserInfo实体 * * @param userId * @return */ private UserInfoEntity getUserInfo(String userId) { return userRepository.findOne(userId); }/** * 获取Token * * @param request * @return */ private String getToken(HttpServletRequest request) { Cookie cookie = CookieUtil.getCookieByName(request, "token"); //token必传 if (null == cookie) { throw new NoteException(ResultEnum.TOKEN_NON_ERROR); } return cookie.getValue(); }/** * 获取userId * * @param request * @return */ private String getUserId(HttpServletRequest request) { Cookie cookie = CookieUtil.getCookieByName(request, "userId"); //userId必传 if (null == cookie) { throw new NoteException(ResultEnum.USERID_NON_ERROR); } return cookie.getValue(); }/** * 判断用户是否存在 * * @param userId * @return */ private void checkUser(String userId) { if (userRepository.findOne(userId) == null) { throw new NoteException(ResultEnum.USER_NOT_EXIST); } }}

这两个类中用到了一些token的校验、redis的获取等等,不懂的地方大家不要慌。相关知识在后续的文章中会为大家一一讲解,在这里入门的童鞋们还是先做个大致的了解,等看完该系列所有文章后一定会让你彻底理解。
本篇文章到此就结束了,在下一章的内容中我将会为大家介绍利用Exception来处理全局的错误代码以及系统异常。
项目地址 本项目的完整项目代码的github地址
https://github.com/BigWolfDean/springboot-simple-project
如果本项目对大家有一些帮助的话,麻烦给个star,fork一下,谢谢!
本系列文章目录 SpringBoot入门项目-基于JPA的App日记后台系统之项目的搭建与配置(一)
SpringBoot入门项目-SpringBoot入门项目-基于JPA的App日记后台系统之数据库的创建与JPA的CRUD(二)
SpringBoot入门项目-基于JPA的App日记后台系统之利用Exception处理自定义错误信息(三)
SpringBoot入门项目-SpringBoot入门项目-基于JPA的App日记后台系统之Controller层的编写(四)
SpringBoot入门项目-基于JPA的App日记后台系统之利用Redis与Cookie处理用户权限校验(五)

    推荐阅读