SpringBoot(三)Spring Data JPA操作数据库

学习内容

实现数据库增删改查操作、分页操作、动态查询操作、控制台打印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 接口。其中 T 是数据库实体类,ID 是数据库实体类的主键。

从上面的代码可以看出,StudentRepository 继承了JpaRepository,查看JpaRepository的源码可以发现,JpaRepository为我们提供了很多现成接口供我们使用。

一行代码也不用写。那么针对 StudentRepository 这个实体类,已经拥有下面的功能

SpringBoot(三)Spring Data JPA操作数据库_第1张图片

SpringBoot(三)Spring Data JPA操作数据库_第2张图片

SpringBoot(三)Spring Data JPA操作数据库_第3张图片

这里为了方便就不编写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

And

findByLastnameAndFirstname

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

Is,Equals

findByFirstname,findByFirstnameIs,findByFirstnameEquals

… where x.firstname = ?1

Between

findByStartDateBetween

… where x.startDate between ?1 and ?2

LessThan

findByAgeLessThan

… where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

… where x.age <= ?1

GreaterThan

findByAgeGreaterThan

… where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

… where x.age >= ?1

After

findByStartDateAfter

… where x.startDate > ?1

Before

findByStartDateBefore

… where x.startDate < ?1

IsNull

findByAgeIsNull

… where x.age is null

IsNotNull,NotNull

findByAge(Is)NotNull

… where x.age not null

Like

findByFirstnameLike

… where x.firstname like ?1

NotLike

findByFirstnameNotLike

… where x.firstname not like ?1

StartingWith

findByFirstnameStartingWith

… where x.firstname like ?1(parameter bound with appended %)

EndingWith

findByFirstnameEndingWith

… where x.firstname like ?1(parameter bound with prepended %)

Containing

findByFirstnameContaining

… where x.firstname like ?1(parameter bound wrapped in %)

OrderBy

findByAgeOrderByLastnameDesc

… where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

… where x.lastname <> ?1

In

findByAgeIn(Collection ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection ages)

… where x.age not in ?1

True

findByActiveTrue()

… where x.active = true

False

findByActiveFalse()

… where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

… where UPPER(x.firstame) = UPPER(?1)

自定义查询

通过编写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()));
	}

JPA配置实体类创建时间、更新时间自动赋值

在实体类上添加 @EntityListeners(AuditingEntityListener.class)注解,在创建时间字段上添加 @CreatedDate注解,在更新时间字段上添加 @LastModifiedDate,时间类型可以为:DateTime、Date、Long、long、JDK8日期和时间类型

然后还需要在启动类加上@EnableJpaAuditing注解。

打印SQL参数

前面的学习中,执行数据库操作时,控制台只打印出SQL语句而没有SQL参数,为了看到SQL参数,可在application.properties配置文件中作如下配置

logging.level.org.hibernate.type.descriptor.sql.BasicBinder=trace

 

代码地址:https://github.com/923226145/SpringBoot-learning/tree/master/springboot-chapter3

你可能感兴趣的:(SpringBoot)