Spring Boot 快速上手(三)数据操作
来源:互联网 发布:世辉律师事务所 知乎 编辑:程序博客网 时间:2024/05/18 23:15
1.Spring Boot中配置JPA
Spring Boot中访问关系型数据库,可以使用Spring Data JPA建立数据访问层,那么先来介绍下相关的必要配置。
① 添加JPA依赖
于Spring Boot项目的pom.xml文件中添加如下依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency>
② 添加数据库驱动依赖
访问数据库的前提是需要先有一个数据库,本文使用MySQL做演示,故添加MySQL的驱动如下:
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>此处添加数据库驱动并没有指定版本号,因为版本号已在spring-boot-dependencies中指定。Spring Boot的当前最新版本号是1.5.7.RELEASE,MySQL驱动的版本号是5.1.44。
③ 配置数据源
于application.properties文件中添加数据源配置:
## 数据源配置spring.datasource.driverClassName=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=UTF-8spring.datasource.username=rootspring.datasource.password=root这里使用的是本地的demo库,后面将在此库中创建数据库表。
④ 配置JPA
于application.properties文件中添加JPA配置:
## JPA配置spring.jpa.show-sql=truespring.jackson.serialization.indent_output=true第一条配置开启控制台中打印sql语句,第二条配置美化输出的json字符串。此外,Spring Boot中默认Hibernate是JPA的实现者。
⑤ 其他配置
application.properties文件中维持如下配置:
# 访问路径server.context-path=/demo# 端口号server.port=8088那么,当前的访问路径是http://localhost:8088/demo/;如果不希望添加该配置,默认访问路径是http://localhost:8080/。
2.使用JPA操作数据库
① 创建数据库表
于本地MySQL数据库的demo库中创建学生表student,该表包含4个字段:id(学生编号),name(姓名),age(年龄),nat(国籍);建表sql如下:
CREATE TABLE `student` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `name` varchar(100) DEFAULT NULL COMMENT '姓名', `age` int(4) DEFAULT NULL COMMENT '年龄', `nat` varchar(200) DEFAULT NULL COMMENT '国籍', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='学生表'
② 创建实体类
创建与student数据表字段一致的实体类Student:
package net.xxpsw.demo.springboot.student.entity;import javax.persistence.Entity;import javax.persistence.Id;@Entitypublic class Student {@Idprivate Long id;private String name;private Integer age;private String nat;public Student() {super();}public Student(Long id, String name, Integer age, String nat) {super();this.id = id;this.name = name;this.age = age;this.nat = nat;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getNat() {return nat;}public void setNat(String nat) {this.nat = nat;}}其中,@Entity指明该实体类与数据库表相映射,@Id指明该属性映射数据库表的主键。
③ 创建数据访问接口
创建数据访问接口StudentRepository,该接口继承JpaRepository<T, ID extends Serializable>:
package net.xxpsw.demo.springboot.student.dao;import org.springframework.data.jpa.repository.JpaRepository;import net.xxpsw.demo.springboot.student.entity.Student;public interface StudentRepository extends JpaRepository<Student, Long> {}查看JpaRepository<T, ID extends Serializable>的源码,源码结构如下:
@NoRepositoryBeanpublic interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {List<T> findAll();List<T> findAll(Sort sort);List<T> findAll(Iterable<ID> ids);<S extends T> List<S> save(Iterable<S> entities);void flush();<S extends T> S saveAndFlush(S entity);void deleteInBatch(Iterable<T> entities);void deleteAllInBatch();T getOne(ID id);@Override<S extends T> List<S> findAll(Example<S> example);@Override<S extends T> List<S> findAll(Example<S> example, Sort sort);}@NoRepositoryBean指明此接口不是业务使用的接口,从JpaRepository<T, ID extends Serializable>的源码可以看到,该接口继承了PagingAndSortingRepository<T, ID>和QueryByExampleExecutor<T>两个接口,继续观察这两个接口的源码结构:
@NoRepositoryBeanpublic interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {Iterable<T> findAll(Sort sort);Page<T> findAll(Pageable pageable);}
public interface QueryByExampleExecutor<T> {<S extends T> S findOne(Example<S> example);<S extends T> Iterable<S> findAll(Example<S> example);<S extends T> Iterable<S> findAll(Example<S> example, Sort sort);<S extends T> Page<S> findAll(Example<S> example, Pageable pageable);<S extends T> long count(Example<S> example);<S extends T> boolean exists(Example<S> example);}
从上述源码中,我们可以一窥Spring Data JPA所提供的一系列数据访问接口。
这里需要注意的是QueryByExampleExecutor<T>,该接口在Spring Data JPA的1.10.x及其后续版本才出现,因此使用时需注意版本的限制。④ 访问数据库
接下来,创建StudentController演示数据库的访问:
package net.xxpsw.demo.springboot.student;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import net.xxpsw.demo.springboot.student.dao.StudentRepository;@RestController@RequestMapping("student")public class StudentController {@Autowiredprivate StudentRepository studentRepository;}
a. 保存信息
StudentController中添加如下方法:
/*** @Description: 保存* @return Student */@RequestMapping("save0")public Student save0() {Student student = studentRepository.save(new Student(1L, "Xie", 25, "China"));return student;}启动Spring Boot,于浏览器地址栏输入http://localhost:8088/demo/student/save0,页面返回结果显示如下:
{ "id" : 1, "name" : "Xie", "age" : 25, "nat" : "China"}
控制台打印sql如下:
Hibernate: insert into student (age, name, nat, id) values (?, ?, ?, ?)使用MySQL客户端连接本地数据库,查询sql:SELECT * FROM `demo`.`student`; 结果如下:
id name age nat ------ ------ ------ -------- 1 Xie 25 China
b. 批量保存
StudentController中新增批量保存方法:
/*** @Description: 保存(批量)* @return List<Student> */@RequestMapping("save1")public List<Student> save1() {List<Student> params = new ArrayList<Student>();params.add(new Student(2L, "Mike", 25, "USA"));params.add(new Student(3L, "Ice", 26, "UK"));params.add(new Student(4L, "Geoff", 26, "France"));params.add(new Student(5L, "Ivan", 26, "Russia"));params.add(new Student(6L, "David", 26, "USA"));List<Student> students = studentRepository.save(params);return students;}
启动Spring Boot,于浏览器地址栏输入http://localhost:8088/demo/student/save1,页面返回结果显示如下:
[ { "id" : 2, "name" : "Mike", "age" : 25, "nat" : "USA"}, { "id" : 3, "name" : "Ice", "age" : 26, "nat" : "UK"}, { "id" : 4, "name" : "Geoff", "age" : 26, "nat" : "France"}, { "id" : 5, "name" : "Ivan", "age" : 26, "nat" : "Russia"}, { "id" : 6, "name" : "David", "age" : 26, "nat" : "USA"} ]控制台打印sql如下:
Hibernate: insert into student (age, name, nat, id) values (?, ?, ?, ?)Hibernate: insert into student (age, name, nat, id) values (?, ?, ?, ?)Hibernate: insert into student (age, name, nat, id) values (?, ?, ?, ?)Hibernate: insert into student (age, name, nat, id) values (?, ?, ?, ?)Hibernate: insert into student (age, name, nat, id) values (?, ?, ?, ?)可以看到,此处虽然一个接口内新增了5条数据,但却执行了5次单条插入的sql。
数据库客户端查询sql:SELECT * FROM `demo`.`student`; 结果如下:
id name age nat ------ ------ ------ -------- 1 Xie 25 China 2 Mike 25 USA 3 Ice 26 UK 4 Geoff 26 France 5 Ivan 26 Russia 6 David 26 USA
c. 删除数据
StudentController中新增删除方法如下:
/*** @Description: 删除 */@RequestMapping("delete")public void delete(long id) {studentRepository.delete(id);}启动Spring Boot,于浏览器地址栏输入http://localhost:8088/demo/student/delete?id=6,
控制台打印sql如下:
Hibernate: delete from student where id=?数据库客户端查询sql:SELECT * FROM `demo`.`student`; 结果如下:
id name age nat ------ ------ ------ -------- 1 Xie 25 China 2 Mike 25 USA 3 Ice 26 UK 4 Geoff 26 France 5 Ivan 26 Russia可以看到,id为6的数据已从数据库表中删除。
d.查询数据
StudentController中新增查询方法如下:
/*** @Description: 查询* @return Student */@RequestMapping("find0")public Student find0(long id) {Student student = studentRepository.findOne(id);return student;}启动Spring Boot,于浏览器地址栏输入http://localhost:8088/demo/student/find0?id=2,页面返回结果显示如下:
{ "id" : 2, "name" : "Mike", "age" : 25, "nat" : "USA"}控制台打印sql如下:
Hibernate: select student0_.id as id1_0_0_, student0_.age as age2_0_0_, student0_.name as name3_0_0_, student0_.nat as nat4_0_0_ from student student0_ where student0_.id=?
e.查询列表
StudentController中新增查询方法如下:
/*** @Description: 查询列表* @return List<Student> */@RequestMapping("find1")public List<Student> find1() {List<Student> students = studentRepository.findAll();return students;}启动Spring Boot,于浏览器地址栏输入http://localhost:8088/demo/student/find1,页面返回结果显示如下:
[ { "id" : 1, "name" : "Xie", "age" : 25, "nat" : "China"}, { "id" : 2, "name" : "Mike", "age" : 25, "nat" : "USA"}, { "id" : 3, "name" : "Ice", "age" : 26, "nat" : "UK"}, { "id" : 4, "name" : "Geoff", "age" : 26, "nat" : "France"}, { "id" : 5, "name" : "Ivan", "age" : 26, "nat" : "Russia"} ]控制台打印sql如下:
Hibernate: select student0_.id as id1_0_, student0_.age as age2_0_, student0_.name as name3_0_, student0_.nat as nat4_0_ from student student0_
f.查询列表(排序)
StudentController中新增查询方法如下:
// 排序条件private Sort sort() {List<Order> orders = new ArrayList<Order>();orders.add(new Order(Direction.DESC, "age"));orders.add(new Order(Direction.DESC, "id"));return new Sort(orders);}/*** @Description: 查询列表(排序)* @return List<Student> */@RequestMapping("find2")public List<Student> find2() {List<Student> students = studentRepository.findAll(sort());return students;}启动Spring Boot,于浏览器地址栏输入http://localhost:8088/demo/student/find2,页面返回结果显示如下:
[ { "id" : 5, "name" : "Ivan", "age" : 26, "nat" : "Russia"}, { "id" : 4, "name" : "Geoff", "age" : 26, "nat" : "France"}, { "id" : 3, "name" : "Ice", "age" : 26, "nat" : "UK"}, { "id" : 2, "name" : "Mike", "age" : 25, "nat" : "USA"}, { "id" : 1, "name" : "Xie", "age" : 25, "nat" : "China"} ]控制台打印sql如下:
Hibernate: select student0_.id as id1_0_, student0_.age as age2_0_, student0_.name as name3_0_, student0_.nat as nat4_0_ from student student0_ order by student0_.age desc, student0_.id desc
g.查询列表(筛选)
StudentController中新增查询方法如下:
// 筛选条件private Example<Student> example() {Student student = new Student();student.setAge(26);return Example.of(student);}/*** @Description: 查询列表(筛选)* @return List<Student> */@RequestMapping("find3")public List<Student> find3() {List<Student> students = studentRepository.findAll(example());return students;}启动Spring Boot,于浏览器地址栏输入http://localhost:8088/demo/student/find3,页面返回结果显示如下:
[ { "id" : 3, "name" : "Ice", "age" : 26, "nat" : "UK"}, { "id" : 4, "name" : "Geoff", "age" : 26, "nat" : "France"}, { "id" : 5, "name" : "Ivan", "age" : 26, "nat" : "Russia"} ]控制台打印sql如下:
Hibernate: select student0_.id as id1_0_, student0_.age as age2_0_, student0_.name as name3_0_, student0_.nat as nat4_0_ from student student0_ where student0_.age=26
h.查询列表(分页)
StudentController中新增查询方法如下:
/*** @Description: 查询列表(分页)* @return Page<Student> */@RequestMapping("find4")public Page<Student> find4() {Page<Student> pageStudents = studentRepository.findAll(new PageRequest(0, 2));return pageStudents;}启动Spring Boot,于浏览器地址栏输入http://localhost:8088/demo/student/find4,页面返回结果显示如下:
{ "content" : [ { "id" : 1, "name" : "Xie", "age" : 25, "nat" : "China" }, { "id" : 2, "name" : "Mike", "age" : 25, "nat" : "USA" } ], "last" : false, "totalElements" : 5, "totalPages" : 3, "number" : 0, "size" : 2, "sort" : null, "first" : true, "numberOfElements" : 2}可以看到,返回信息中除了数据库表内容,还包含了分页参数。
控制台打印sql如下:
Hibernate: select student0_.id as id1_0_, student0_.age as age2_0_, student0_.name as name3_0_, student0_.nat as nat4_0_ from student student0_ limit ?
i.查询列表(排序 + 筛选 + 分页)
StudentController中新增查询方法如下:/*** @Description: 查询列表(排序 + 筛选 + 分页)* @return Page<Student> */@RequestMapping("find5")public Page<Student> find5() {Page<Student> pageStudents = studentRepository.findAll(example(), new PageRequest(0, 2, sort()));return pageStudents;}启动Spring Boot,于浏览器地址栏输入http://localhost:8088/demo/student/find5,页面返回结果显示如下:
{ "content" : [ { "id" : 5, "name" : "Ivan", "age" : 26, "nat" : "Russia" }, { "id" : 4, "name" : "Geoff", "age" : 26, "nat" : "France" } ], "last" : false, "totalElements" : 3, "totalPages" : 2, "number" : 0, "size" : 2, "sort" : [ { "direction" : "DESC", "property" : "age", "ignoreCase" : false, "nullHandling" : "NATIVE", "ascending" : false, "descending" : true }, { "direction" : "DESC", "property" : "id", "ignoreCase" : false, "nullHandling" : "NATIVE", "ascending" : false, "descending" : true } ], "first" : true, "numberOfElements" : 2}控制台打印sql如下:
Hibernate: select student0_.id as id1_0_, student0_.age as age2_0_, student0_.name as name3_0_, student0_.nat as nat4_0_ from student student0_ where student0_.age=26 order by student0_.age desc, student0_.id desc limit ?
⑤ 使用@Query查询
Spring Data JPA还支持使用@Query注解在接口的方法上实现查询。a. 数据查询
在接口StudentRepository中添加如下方法:
@Query("SELECT S FROM Student S WHERE S.age > :age ")public List<Student> queryAll(@Param("age") Integer age);该方法用于查询年龄大于某个年龄值的所有学生数据,在StudentController中添加如下方法:
/*** @Description: 数据查询* @param age 年龄* @return List<Student> */@RequestMapping("query0")public List<Student> query0(int age) {List<Student> students = studentRepository.queryAll(age);return students;}启动Spring Boot,于浏览器地址栏输入http://localhost:8088/demo/student/query0?age=25,页面返回结果显示如下:
[ { "id" : 3, "name" : "Ice", "age" : 26, "nat" : "UK"}, { "id" : 4, "name" : "Geoff", "age" : 26, "nat" : "France"}, { "id" : 5, "name" : "Ivan", "age" : 26, "nat" : "Russia"} ]
可以看到,数据库中年龄大于25的三个学生都被查找出来了。
控制台打印sql如下:
Hibernate: select student0_.id as id1_1_, student0_.age as age2_1_, student0_.name as name3_1_, student0_.nat as nat4_1_ from student student0_ where student0_.age>?
b.多表查询
现在,我们来新建一张学生的成绩表,然后对两张表做联合查询,找出符合条件的学生信息。于本地数据库的demo库中新建数据库表score,建表sql如下:
CREATE TABLE `score` ( `id` bigint(20) NOT NULL COMMENT 'ID', `student_id` bigint(20) DEFAULT NULL COMMENT '学生ID', `score` int(4) DEFAULT NULL COMMENT '成绩', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='成绩表'向score表中新增几条数据:
INSERT INTO `demo`.`score` (`id`, `student_id`, `score`) VALUES ('1', '1', '90'); INSERT INTO `demo`.`score` (`id`, `student_id`, `score`) VALUES ('2', '3', '85'); INSERT INTO `demo`.`score` (`id`, `student_id`, `score`) VALUES ('3', '5', '95');新建实体类Score映射数据库表score:
package net.xxpsw.demo.springboot.student.entity;import javax.persistence.Entity;import javax.persistence.Id;@Entitypublic class Score {@Idprivate Integer id;private Integer studentId;private Integer score;public Score() {super();}public Score(Integer id, Integer studentId, Integer score) {super();this.id = id;this.studentId = studentId;this.score = score;}public Integer getStudentId() {return studentId;}public void setStudentId(Integer studentId) {this.studentId = studentId;}public Integer getScore() {return score;}public void setScore(Integer score) {this.score = score;}}新建查询结果类StudentScore,包含字段:id(学生ID),name(姓名),score(成绩):
package net.xxpsw.demo.springboot.student.entity;public class StudentScore {private Long id;private String name;private Integer score;public StudentScore() {super();}public StudentScore(Long id, String name, Integer score) {super();this.id = id;this.name = name;this.score = score;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getScore() {return score;}public void setScore(Integer score) {this.score = score;}}在接口StudentRepository中添加如下方法:
@Query("SELECT new net.xxpsw.demo.springboot.student.entity.StudentScore(S.id,S.name,C.score) "+ "FROM Student S, Score C " + "WHERE S.id = C.studentId AND S.age > :age ")public List<StudentScore> queryScoreAll(@Param("age") Integer age);该方法用于学生表与成绩表的联合查询,检索出年龄大于某个年龄值的所有学生信息,在StudentController中添加如下方法:
/** * @Description: 多表查询 * @param age 年龄 * @return List<StudentScore> */@RequestMapping("query1")public List<StudentScore> query1(int age) {List<StudentScore> studentScores = studentRepository.queryScoreAll(age);return studentScores;}启动Spring Boot,于浏览器地址栏输入http://localhost:8088/demo/student/query1?age=25,页面返回结果显示如下:
[ { "id" : 3, "name" : "Ice", "score" : 85}, { "id" : 5, "name" : "Ivan", "score" : 95} ]
可以看到,所有符合条件的学生信息都已经被检索出来了,并且学生信息中包含且只包含 id(学生ID)、name(姓名)和score(成绩)。
此时控制台打印sql如下:
Hibernate: select student0_.id as col_0_0_, student0_.name as col_1_0_, score1_.score as col_2_0_ from student student0_ cross join score score1_ where student0_.id=score1_.student_id and student0_.age>?
- Spring Boot 快速上手(三)数据操作
- Spring Boot 快速上手(一)快速搭建
- [转]Spring Boot 揭秘与实战(一) 快速上手
- Spring Boot 快速上手(二)基本配置
- Spring Boot 快速上手(四)事务使用
- Spring Boot 快速上手(五)集成Redis
- Spring Boot 快速上手(六)集成MongoDB
- Spring Boot 快速上手(七)集成ActiveMQ
- Spring Boot 快速上手(八)集成Thymeleaf
- Python快速上手(三)
- Spring boot(三)
- 快速上手RaphaelJS--RaphaelJS_Starter翻译(三)
- 快速上手RaphaelJS--RaphaelJS_Starter翻译(三)
- 快速上手RaphaelJS--RaphaelJS_Starter翻译(三)
- UnityShader快速上手指南(三)
- swift3.1 快速上手系列(三)
- 快速上手Spring
- Spring Cloud快速上手
- spring 深入理解 google Guice 依赖注入
- 让visual studio飞起来
- Spring线程池ThreadPoolTaskExecutor
- 反序列化工具
- 建议Linux操作系统初学者的学习线路图
- Spring Boot 快速上手(三)数据操作
- VS配置lib库问题
- C/C++ 生成[X,Y]内的随机数
- 基于opencv查看图像各像素点的RGB值
- HDU 5025:Saving Tang Monk (BFS + 状压)
- c++引用
- AppCompatActivity和Activity的主题设置问题
- Spring Boot入门学习笔记(一)
- mtk6735 [Audio App]如何内置FM频道