实现数据库增删改查操作、分页操作、动态查询操作、控制台打印SQL参数。
pom.xml如下所示
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
runtime
org.projectlombok
lombok
org.springframework.boot
spring-boot-starter-test
test
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/springboot-learning?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
# 使SQL语句打印在控制台
spring.jpa.show-sql=true
# 格式化SQL语句
spring.jpa.properties.hibernate.format_sql=true
#每次运行程序,没有表则会新建表格,表内有数据不会清空,只会更新
spring.jpa.hibernate.ddl-auto=update
# 设置数据表的引擎为InnoDB,默认为MyISAM
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
package com.example.springbootchapter3.entity;
import lombok.Data;
import javax.persistence.*;
import java.util.Date;
/**
* Created by lzc
* 2019/6/1 11:26
*/
@Data
@Entity
public class Student {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY) // 设置数据库中id自增
private Long id;
// 学生姓名
private String studentName;
// 学生姓名
private String gender;
// 班级名称
private String className;
// 学生年龄
private Integer age;
// 学生所在城市
private String cityName;
// 创建时间
private Date createTime;
// 更新时间
private Date updateTime;
}
可以看到Student 类中没有get方法和set方法,实体类上有@Data注解,使用该注解后,我们就不需要写get、set方法了。使用该注解还需要安装lombok插件,插件安装方法:Lombok详细教程及idea中lombok插件的安装。
package com.example.springbootchapter3.repository;
import com.example.springbootchapter3.entity.Student;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* Created by lzc
* 2019/6/1 14:48
*/
public interface StudentRepository extends JpaRepository {
}
org.springframework.data.jpa.repository.JpaRepository
从上面的代码可以看出,StudentRepository 继承了JpaRepository,查看JpaRepository的源码可以发现,JpaRepository为我们提供了很多现成接口供我们使用。
一行代码也不用写。那么针对 StudentRepository 这个实体类,已经拥有下面的功能
这里为了方便就不编写service层了。
使用JpaRepository提供的方法操作数据库,使用测试类进行测试,如下所示
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class SpringbootChapter3ApplicationTests {
@Autowired
private StudentRepository studentRepository;
// 增加
@Test
public void test1() {
Student student = new Student();
student.setStudentName("lisi");
student.setAge(20);
student.setCityName("桂林");
student.setClassName("计科153");
student.setGender("女");
studentRepository.save(student);
}
// 删除
@Test
public void test2() {
// 通过id删除,如果id不存在,将会抛异常
// 删除之前可以通过studentRepository.existsById(1L)来判断id是否存在,如果存在,则删除
studentRepository.deleteById(1L);
}
// 修改
@Test
public void test3() {
// 通过id查询
Student result = studentRepository.findById(4L).orElse(null); // 当查询结果不存在时则返回null
result.setAge(18);
studentRepository.save(result);
}
// 简单查询:查询所有记录
@Test
public void test4() {
List studentList = studentRepository.findAll();
studentList.stream().forEach(s -> log.info(s.toString()));
}
// 简单查询:分页查询
@Test
public void test5() {
Page studentPage = studentRepository.findAll(PageRequest.of(1,2));
List studentList = studentPage.getContent();
studentList.stream().forEach(s -> log.info(s.toString()));
log.info("【TotalPages】" + studentPage.getTotalPages());
log.info("【totalElements】" + studentPage.getTotalElements());
log.info("【Number】" + studentPage.getNumber());
log.info("【Size】" + studentPage.getSize());
log.info("【NumberOfElements】" + studentPage.getNumberOfElements());
}
// 简单查询:分页查询+排序(要么升序,要么降序)
@Test
public void test6() {
Page studentPage = studentRepository.findAll(PageRequest.of(0,10,Sort.Direction.ASC,"age"));
List studentList = studentPage.getContent();
studentList.stream().forEach(s -> log.info(s.toString()));
log.info("【TotalPages】" + studentPage.getTotalPages());
log.info("【totalElements】" + studentPage.getTotalElements());
log.info("【Number】" + studentPage.getNumber());
log.info("【Size】" + studentPage.getSize());
log.info("【NumberOfElements】" + studentPage.getNumberOfElements());
}
// 简单查询:分页查询+排序(既有升序,又有降序)
@Test
public void test7() {
Sort sort = new Sort(Sort.Direction.DESC,"age"); // 年龄降序
sort = sort.and(new Sort(Sort.Direction.ASC,"className")); // 班级升序
Page studentPage = studentRepository.findAll(PageRequest.of(0,10,sort));
List studentList = studentPage.getContent();
studentList.stream().forEach(s -> log.info(s.toString()));
log.info("【TotalPages】" + studentPage.getTotalPages());
log.info("【totalElements】" + studentPage.getTotalElements());
log.info("【Number】" + studentPage.getNumber());
log.info("【Size】" + studentPage.getSize());
log.info("【NumberOfElements】" + studentPage.getNumberOfElements());
}
}
虽然JpaRepository为我们提供了很多操作方法,但是很多我们想要的查询功能还是没有。然而JPA 提供了非常优雅的方式来解决。
例如通过实体类的studentName属性进行查询,在StudentRepository接口中声明如下方法即可
Student findByStudentName(String studentName);
还可以进行多个属性进行查询,例如根据实体类的studentName和cityName进行查询,在StudentRepository接口中声明如下方法即可
Student findByStudentNameAndCityName(String studentName, String cityName);
根据实体类的属性进行分页查询
Page findByClassName(String className, Pageable pageable);
下表描述了JPA支持的关键字以及包含该关键字的方法
Keyword | Sample | JPQL snippet |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
通过编写SQL方式的方式进行查询,如下所示。如果是update/delete操作,需要加上 @Transactional和@Modifying注解。
// 使用Spring Data JPA提供的SQL规则
@Query("select s from Student s where s.studentName = ?1")
Student getByStudentName1(String studentName);
// 使用原生SQL
@Query(value = "select * from student where student_name = ?1", nativeQuery = true)
Student getByStudentName2(String studentName);
// 这里需要加上事物
@Transactional
@Modifying
@Query("update Student s set s.age = ?2 where s.id = ?1")
Integer updateAgeById(Long id, Integer count);
JPA为我们提供了Example类实现动态查询
具体用法看如下测试方法
// JPA动态查询
@Test
public void test11() {
Student student = new Student();
student.setCityName("南宁");
student.setClassName("计科152");
Example example = Example.of(student);
// SQL语句 select * from student where city_name = '南宁' and class_name = '计科152'
List studentList = studentRepository.findAll(example);
studentList.forEach(s -> log.info(s.toString()));
}
@Test
public void test12() {
Student student = new Student();
student.setCityName("南宁");
student.setClassName("计科152");
Example example = Example.of(student);
// SQL语句 select * from student where city_name = '南宁' and class_name = '计科152' order by age desc limit 0,2
Page studentPage = studentRepository.findAll(example, PageRequest.of(0,2, Sort.Direction.DESC,"age"));
List studentList = studentPage.getContent();
studentList.stream().forEach(s -> log.info(s.toString()));
log.info("【TotalPages】" + studentPage.getTotalPages());
log.info("【totalElements】" + studentPage.getTotalElements());
log.info("【Number】" + studentPage.getNumber());
log.info("【Size】" + studentPage.getSize());
log.info("【NumberOfElements】" + studentPage.getNumberOfElements());
}
@Test
public void test13() {
Student student = new Student();
student.setCityName("南宁");
student.setClassName("计科");
// 设置属性的查询规则,
// 有ignoreCase(),caseSensitive(),contains(),endsWith(),startsWith(),exact(),storeDefaultMatching(),regex()
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("className",startsWith());
Example example = Example.of(student, matcher);
List studentList = studentRepository.findAll(example);
studentList.forEach(s -> log.info(s.toString()));
}
在实体类上添加 @EntityListeners(AuditingEntityListener.class)注解,在创建时间字段上添加 @CreatedDate注解,在更新时间字段上添加 @LastModifiedDate,时间类型可以为:DateTime、Date、Long、long、JDK8日期和时间类型
然后还需要在启动类加上@EnableJpaAuditing注解。
前面的学习中,执行数据库操作时,控制台只打印出SQL语句而没有SQL参数,为了看到SQL参数,可在application.properties配置文件中作如下配置
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=trace
代码地址:https://github.com/923226145/SpringBoot-learning/tree/master/springboot-chapter3