个人技术总结
个人技术总结
目录
SpringBoot技术概述
SpringBoot是后端开发轻量级的框架之一,主要用于网页后端开发。而相比于Spring框架,SpringBoot通过“约定大于配置”的方法简化了应用搭建和开发过程,对于小白来说上手比较快。SpringBoot起源于Spring,对于刚接触的人来说,比较难理解的就是其IOC与AOP。
技术详述
一个软件通常都需要存储与检索数据,遵循业务逻辑与规则并对数据库进行事务管理、日志管理等。而Spring通过IOC与AOP两种方式去实现两种事务管理与日志管理功能。简要的来说,AOP(面向切面编程)就是将分布在各个函数中的事务管理与日志管理代码抽取出来,然后在代码编译或运行时织入相应的切面。而IOC(控制反转)是Spring管理其基本单位Bean的方式,对象的创建由程序通过依赖注入的方式实现,而不是由开发人员创建。
首先说明软件运行的环境,IntelliJ IDEA 2020.3.2、MySQL 8.0、SpringBoot 2.5.2
相较于Spring来说,SpringBoot极大幅度的简化了配置流程,开发人员需要配置的文件没有那么多了。其基本步骤大致如下:
- 在application.properties中配置数据源(使用的持久层框架不同,相应配置也不同)
- 创建相应的实体类与数据库表相对应
- 编写相应Dao层、Service层与Controller层代码处理请求。
下面以的用户表查询请求为例,使用持久层框架为JPA(假设用户表有主键id、account与password三个字段):
一、配置数据源代码:
server.port=8082
spring.datasource.url=jdbc:mysql://localhost:3306/tmall?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
spring.datasource.username =root
spring.datasource.password =123456
比较关键的是spring.datasource.url的属性,并且由于IDEA与MySQL版本问题很容易会出现两个问题。这个在后面的问题模块讲解。
二、实体类代码
我们需要编写相应实体类User与数据库中的User表相映射:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;//主键ID
private String account;//账号
//返回json数据时,忽略密码字段
@JsonIgnore
private String password;//密码
}
其中@Entity是对实体的注释,需要配合@Id使用,否则会报错。
三、Dao层代码
然后我们可以编写用户类的Dao层接口,UserDao,例如根据账号查找一个用户:
@Repository
public interface UserDao extends JpaRepository<User,Integer> {
/**
* 根据账号查询用户
* @param account
* @return User
*/
@Query(value = "SELECT * from user where user.account_num = ?1",nativeQuery = true)
User findByAccount_num(String account);
}
@Repository注解说明这个类属于持久层。
使用JPA时,Dao层需要继承JpaRepository<User,Integer>,其中User代表实体类名,Integer代表了主键类型。当然,一般查询数据比较简单时,可以直接使用findBy***方法,通过分析方法名来实现查询。而查询数据比较复杂时使用@Query注解。这里仅为演示@Query用法。
四、业务层代码
然后我们就可以开始着手编辑Service层代码,实现相关的功能,例如查询一个用户:
@Service
public class UserService {
@Autowired
UserDao userDao;
/**
* 根据账号查询用户
* @param account
* @return
*/
public User findUserByAccount(String account){
return userDao.findByAccount_num(account);
}
}
@Service注解说明这个类属于业务层代码。 在UserService使用@Autowired注解依赖注入UserDao对象实例,然后使用UserDao的相关方法。这就是Spring的IOC的实现方式之一。
五、控制层代码
之后,我们需要编写相关控制层代码来处理前端的请求,例如查找用户信息:
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
/**
* 查询用户信息
* /findOneUser/{account}
* @param account
* @return
*/
@RequestMapping(value = "/findOneUser/{account}", method = RequestMethod.GET)
public JSONObject findUserInfo(@PathVariable(value = "account") String account){
User user = userService.findUserByAccount(account);
JSONObject jsonObject = new JSONObject();
jsonObject.put("msg", "操作成功");
jsonObject.put("code",Constant.SUCCESS);
jsonObject.put("data", user);
return jsonObject;
}
}
在Spring中,控制层有两种注解,一种是@Controller,使用该注解时,视图解析器可以解析return 的jsp,html页面,并且跳转到相应页面;若返回json等内容到页面,则需要加@ResponseBody注解。而@RestController注解只返回json数据,不能返回jsp,html页面。同样的,也需要在UserController中使用@Autowired注入UserService实例。
方法上的注解 @RequestMapping用于映射URL,当其用于类定义处时,规定初步的请求映射,相对于web应用的根目录;而用于方法定义处时,进一步细分请求映射,相对于类定义处的URL,以上面代码为例,前端调用查询用户URL为/user/findOneUser/{account}。其中{account}为路径变量,我们使用这个变量去数据库里查询用户。当然,这里接受前端参数可以使用多种方式,例如使用JSONObject对象等等,就不多赘述。通过业务层代码获取相关用户数据后,使用JSONObject返回给前端即可。
其处理请求流程大致为:
1.用户通过浏览器向服务器发送请求,请求会被Spring MVC的前端控制器DispatcherServlet所拦截;
2.DispatcherServlet拦截到请求后,会调用HandlerMapping处理器映射器;
3.处理器映射器根据请求URL找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
4.DispatcherServlet会通过返回信息选择合适的HandlerAdapter(处理器适配器)
5.HandlerAdapter会调用并执行Handler(处理器),这里的处理器指的就是程序中编写的Controller类,也被称之为后端控制器;
6.Controller执行完成后,会返回一个ModelAndView对象,该对象中会包含视图名或包含模型和视图名;
7.HandlerAdapter将ModelAndView对象返回给DispatcherServlet;
8/DispatcherServlet会根据ModelAndView对象选择一个合适的ViewReslover(视图解析器);
9.ViewReslover解析后,会向DispatcherServlet中返回具体的View(视图);
10.DispatcherServlet对View进行渲染(即将模型数据填充至视图中);
11.视图渲染结果会返回给客户端浏览器显示。
总结:
以上就是使用SpringBoot实现后端对数据库操作的大致流程,其项目代码大致分为三类entity实体类,service业务类与controller控制器类。当然,实际的项目中还会有其他的代码类,例如常量类、异常处理类等等,由于篇幅原因就不过多展开。
遇到问题
一、数据库连接问题
-
报错信息:com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value is unrecognized……这种情况主要是由于时区问题,IDEA中配置数据库时默认使用的时区是UTC,所以要么修改IDEA的时区为Asia/Shanghai,要么在 JDBC 的连接 url 部分(spring.datasource.url)加上serverTimezone=UTC。
-
现在网上大部分教程使用的都是MySQL5.3的版本,其 JDBC 的连接 url 部分基本就是jdbc:mysql://localhost:3306/tmall就可(tmall代表数据库名)。但MySQL 5.5.45+、5.6.26+和5.7.6+以后的版本在进行连接时会默认需要SSL连接,不然会提示警告信息Establishing SSL connection without server's identity verification is not recommended,解决方法就是在JDBC 的连接 url 部分添加useSSL=false
-
驼峰命名导致实体类部分字段值为null。这个是由于JPA默认的命名方式导致的问题。JPA默认使用的的是ImprovedNamingStrategy策略,即JPA对数据表默认的命名规则为两个单词之间以下划线分割。虽然实体字段和数据库字段一致,但是 JPA 的生成的sql语句还是把小驼峰转化为 下划线了所以查不到。比如实体类中有属性字段createTime,数据表中字段也为createTime,但由于命名策略问题,实体类中createTime 会被 JPA 创建为字段 create_time,导致找不到相关字段,于是其值为null。解决方法就是将命名策略修改为PhysicalNamingStrategyStandardImpl。即在application.properties中添加spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
二、第三方依赖jar包的问题
1. 打包问题
SpringBoot都是使用Maven来管理相应依赖文件,并且使用Maven来进行打包。但需要导入第三方的依赖包时,Maven默认不会将第三方依赖包打包进相应的jar文件的。解决方法如下:
首先我们应该在Maven中添加第三方依赖,在src中添加lib文件夹存放第三方jar包,pom.xml代码如下
<dependency>
<groupId>域名</groupId>
<artifactId>项目名</artifactId>
<version>版本号</version>
<scope>system</scope>
<systemPath>${basedir}/src/lib/文件名字.jar</systemPath>
</dependency>
其中{basedir}是自带变量,指的是当前项目的绝对路径。
然后我们需要在pom.xml中添加一条属性:
<build>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</build>
2.签名错误
β冲刺时,我们给App添加了每日定时向用户推送菜谱的功能,使用的就是友盟的第三方jar包。但很奇怪的是,在进行测试时,发现在idea中运行时是成功的,用户在手机上可以获取消息,但使用命令行运行相应jar包时就会出现签名错误的问题,导致消息推送不了。这实在是让人非常困惑。最终解决方式还是查看了签名错误的相关实例,其中有一个引起了我的注意,就是这个jar包需要在utf-8 zh_cn的编码下才能正常运行,而通常服务器和windowcmd的编码不是,所以在计算签名时才会出错。解决方法就是在运行jar包时,添加-Dfile.encoding=utf-8命令。
总结
通过这次实践,我对SpringBoot有了进一步的了解,而项目中遇到的种种问题我在网络资料与同学帮助下也一一克服,收获还是非常大的。一开始遇到这些不熟悉的问题还是非常棘手的,就不如驼峰命名导致的null问题,一开始完全没有想到会是JPA默认命名策略的问题。最终还是在组内同学的帮助下才解决问题。
参考博客
-
[更新 Spring Data JPA 之数据表名的命名规则为驼峰命名法 - 雨尘365 - 博客园 (cnblogs.com)](更新 Spring Data JPA 之数据表名的命名规则为驼峰命名法 - 雨尘365 - 博客园 (cnblogs.com))
-
[详解springboot解决第三方依赖jar包的问题](详解springboot解决第三方依赖jar包的问题_java_脚本之家 (jb51.net))
-
[Springboot中如何引入本地jar包,并通过maven把项目成功打包成jar包部署]((14条消息) Springboot中如何引入本地jar包,并通过maven把项目成功打包成jar包部署_艾特老司机-CSDN博客_includesystemscope)