最近,在慕课网上自学了springboot的一些初级用法,也在使用途中,了解了一些相关的知识,接下来记下我的学习成果:
本次记录的是使用springboot+jpa对mysql数据库的简单增删改查,在新建了项目后,这里我们以学生为例,对其信息进行相关的操作。
由于需要与mysql进行交互,先引入相关依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
由于jpa是通过对象映射自动为我们建好表,我们只需建好库即可:
接下来配置连接数据库需要的属性:
新建项目时会默认生成一个properties文件,这里我们新建一个yml文件,更简洁地来配置:
server:
port: 8081
servlet:
context-path: /students
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/students?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
username: root
password: 123456
jpa:
hibernate:
ddl-auto: update
show-sql: true
其中需要注意的是配置数据库url时,我曾经碰到过一个错误
在网上搜索资料后,发现问题的所在是:
由于mysql服务默认的时间使用的是美国时间,和中国时差8小时,导致系统时间和本机时间不匹配,比较轻松的一个解决办法是,在url后加上这么一串:&serverTimezone=GMT%2B8
其次,jpa的几个配置属性也需要注意:
ddl-auto:有四个参数可选,分别是:
create:启动时先删除数据库中的表,再创建表,退出时不删除表
create-top:启动时先删除数据库中的表,再创建表,退出时删除表
update:如果启动时表存在但格式不一致则更新表,会保留原始数据
validate:启动时自检,如果表结构不一致会报错
所以一般我们在第一次启动项目时,可以选择create参数自动创建表,以后再改成update参数
show-sql:可选true或false,表示是否打印执行的sql语句在控制台,为了方便测试建议开启
新建实体类:
@Entity
public class Student {
@Id
@GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
private Integer id;
private String name;
private String grade;
}
这里声明了学生的三个属性,id、姓名以及年级
值得注意的是其中的注解:
@Entity:是JPA中用来指定实体类的注解,同样它也会在数据库中自动创建相应的表,默认表名会与类名相同,也可以在注解后添加(name=“表名”)来自定义生成的表名
@Id:用于指定属性为自增列
@GeneratedValue:指定主键的生成策略,不同的数据库会有不同默认策略,如mysql默认情况下会指定为 auto increment(自动增长)
接下来新建一个JpaRepository接口,何为JpaRepository?我个人的理解,就像是以前使用jdbc操作数据库时,我们需要什么功能时就需要编写好相关的sql语句,并通过手写一系列代码实现sql的操作,而JpaRepository将这一些步骤为我们封装起来了,只需要调用相关的方法即可简单实现,下面会有相关的展示
新建一个接口,继承JpaRepository
public interface LuckymoenyRepository extends JpaRepository<Luckymoney,Integer> {
}
其中JpaRepository的泛型格式为JpaRepository<T,ID>,T为实体类型,ID为主键的数据类型
接下来新建业务逻辑层,也就是Service层
@Service
public class StudentService {
@Autowired
StudentRepository repository;
}
JpaRepository内置了许多默认方法用于完成简单的数据库CURD,调用后他们会自动生成sql语句,接下来有几种常用的:
findAll():全查方法
findOne():条件查询,括号中放入查询的条件,返回值为Optional对象(springboot2之前返回值为基本数据类型)
save():保存方法,可以用于执行增加或修改操作
delete():删除方法,括号中放入删除条件执行删除操作
deleteall():删除全部的方法
接下来新建控制层:
@RestController
public class StudentController {
@Autowired
StudentService service;
}
service层依次写入增删改查方法:
/**
* 查询所有学生
*/
public List<Student> listAll(){
return repository.findAll();
}
/**
* 新增学生
*/
public Student create(String name,
String grade){
Student student =new Student();
student.setName(name);
student.setGrade(grade);
return repository.save(student);
}
/**
* 根据ID查询学生
*/
@GetMapping("/luckymoneys/{id}")
public Student findById(Integer id){
return repository.findById(id).orElse(null);
}
/**
* 修改学生姓名
*/
public Student update(Integer id,
String name){
Optional<Student> optional= Optional.ofNullable(repository.findById(id).orElse(null));
if (optional.isPresent()){
Student student=optional.get();
student.setName(name);
return repository.save(student);
}
return null;
}
/**
* 删除某个学生信息
*/
public void delete(@PathVariable("id") Integer id){
Optional<Student> optional= Optional.ofNullable(repository.findById(id).orElse(null));
if (optional.isPresent()){
Student student=optional.get();
repository.delete(student);
}
}
由于一般的业务逻辑,修改时会先显示需要修改的原始数据,我写了一个findById方法,即通过Id查找对象。这里需要注意的是根据id查询以及修改方法,这两个方法要求返回的对象为Optional对象,Optional对象是Java8新特性之一,他可以用于判断对象是否为空值作出相应操作,而避免空指针的报错。
controller层写入增删改查代码:
@RestController
public class StudentController {
@Autowired
StudentService service;
/**
* 查询所有学生
*/
@GetMapping("/listall")
public List<Student> listAll(){
return service.listAll();
}
/**
* 新增学生
*/
@PostMapping("/create")
public Student create(@RequestParam("name")String name,
@RequestParam("grade")String grade){
Student student=new Student();
student.setName(name);
student.setGrade(grade);
return service.create(student.getName(),student.getGrade());
}
/**
* 根据ID查询学生
*/
@GetMapping("/findById/{id}")
public Student findById(@PathVariable("id") Integer id){
return service.findById(id);
}
/**
* 修改学生姓名
*/
@PutMapping("/update/{id}")
public Student update(@PathVariable("id")Integer id,
@RequestParam("name")String name){
Student student=new Student();
student.setId(id);
student.setName(name);
return service.update(student.getId(),student.getName());
}
@PutMapping("/delete/{id}")
public void delete(@PathVariable("id")Integer id){
service.delete(id);
}
这里有几个注解需要说明一下:
首先是@RestController:用过spring mvc的想必都对@controller以及@requestmapping注解非常熟悉了,@controller是用于标注控制器的注解,说明该类是一个控制器,当我们将请求以json格式返回时,须配合@responsebody一起使用,而@RestController相当于@controller+@responsebody的作用
其次是:
@GetMapping、@PutMapping、@PostMapping
在使用Spring mvc时,我们常用@requestMapping处理请求,该注解有一个属性method用于指定提交方式,你可以指定它使用post或是get方式提交,不写时两种都可支持,而@GetMapping、@PostMapping可以理解为将@requestMapping分成了两种提交方式
@PutMapping:使用put提交方式,那何为put提交?
一般主要拿它和post请求进行对比,首先两者都是用于更新资源,而两者最大区别是幂等操作,所谓幂等,指的是对一个同样的操作执行多次效果相同,post请求每次处理时会更新或者新建资源,是非幂等的,比如例子中我们新建一个学生信息,每次执行一次新增操作都会新建一个学生信息,而put请求是幂等的,比如我们修改一个学生信息,每次修改都会覆盖上一次的信息,所以put一般用于修改信息而post请求用于新增信息比较合适
最后是@PathVariable和@RequestParam,这两个注解都用于请求是传参,个人对这两个注解的理解是:
@PathVariable,正如它的名字一样,路径变量,将变量直接写在路径中,比较简洁,比如我们查询一个id为2的学生信息,这里使用postman工具进行测试:
控制器中是这样写的:
@GetMapping("/findById/{id}")
不难看出,url中直接写入2,就可以完成传参,而使用@RequestParam是这样:
控制器中是这样写的:
@GetMapping("/findById")
除此之外,两者可以定义的属性还有不同:
@PathVariable可以定义value、name、required三个属性,而@RequestParam除此之外还多一个defaultValue(默认值),由此也可以得出两者适用的环境:当传参必须时可以使用@PathVariable而不一定需要时可以使用@RequestParam
综上所述,两者最大的区别在于:
1.@PathVariable将参数写在url中,而@RequestParam将参数放在请求时的参数中,两者接收方式不同
2.两者可定义的属性不同,也决定了两者的使用环境不同