springdata-jpa (一)单表操作
1.简介
Spring Data 项目的目的是为了简化构建基于 Spring 框架应用的数据访问,包括关系型数据库库、非关
系型数据库、Map-Reduce 框架、云数据服务等。Spring Data JPA是Spring Data下面的一个子项目,其提供了对JPA的操作支持。
JPA(Java Persistence API)是当年的Sun官方提出的Java持久化规范。它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据。它的出现主要是为了简化现有的持久化开发工作和整合ORM技术,结束现在Hibernate,TopLink,JDO等 ORM框架各自为营的局面。值得注意的是,JPA是在现有Hibernate,TopLink,JDO等ORM框架的基础上发展而来的。JPA在JAVA EE 5的时候也加入了其规范之中。
注意,JPA是一套规范,而不是产品。Hibernate是一个ORM框架,它实现了JPA的规范。
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能,使用 Spring Data JPA 可以极大提高开发效率!
Spring Data JPA让我们解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现。
2、使用
springboot中使用Spring Data JPA,需要引入其对应的starter,maven会自动引入JPA及其相应的实现,它默认采用的是hibernate的实现。pom.xml中导入springdata-jpa其对应的starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
application.properties文件
server.port=9999
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.65.129:3316/work?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.password=123456
spring.datasource.username=root
#如果需要,可以进行自动建表
spring.jpa.hibernate.ddl-auto=update
#如果需要,可以执行指定的sql文件进行建表
#spring.datasource.schema=sql/test.sql
#如果需要,可以显示执行的sql语句
spring.jpa.show-sql=true
#如果需要,可以格式化sql语句
spring.jpa.properties.hibernate.format_sql=true
springboot中对spring Data JPA的配置参数:
Spring Data JPA中采用泛型接口的形式,预先定义好了一些基本的CURD的方法。泛型中需要提供操作的实体类型以及对应的主键类型。
注意,我们无需实现这些方法,Spring Data JPA框架中已经实现了,我们只需在依赖注入后,直接使用即可。
dao层接口UserRepository
package com.hzx.springdatajpa.repository;
import com.hzx.springdatajpa.bean.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User,Long> {
}
实体类User
@Entity
@Table(name = "t_user")
public class User implements Serializable {
// oracle数据库主键的生成策略(sequence)
// @Id
// @GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "myGenerator")
// @SequenceGenerator(name = "myGenerator",sequenceName = "my_seq")
@Id
@GeneratedValue
private Long id;
@Column(nullable = false,unique = true)
private String name;
@Column(nullable = false)
private Integer age;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Gender gender;
public User(Long id, String name, Integer age, Gender gender) {
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
}
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 Gender getGender() {
return gender;
}
public void setGender(Gender gender) {
this.gender = gender;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", gender=" + gender +
'}';
}
}
@Entity 表示当前类是JPA中的一个实体类
@Table(name=“t_user”)表示当前实体类对应数据库中的表t_user
@Id表示当前属性对应t_user表中的主键列
@GeneratedValue表示该属性对应的t_user表中列值是自动增长的
@Column(nullable = false,unique = true)表示当前属性对应t_user表中的列值是非空唯一的
@Enumerated(EnumType.STRING)表示当前属性对应枚举类型的String形式,而不是原始编号
注意,如果是Oracle数据库的话,可以使用序列来产生主键列的值,代码在注释部分
测试类:
@SpringBootTest
class SpringDataJpaApplicationTests {
@Autowired
public UserRepository userRepository;
@Test
void contextLoads() {
User zhangsan = new User("zhangsan", 20, Gender.MAN);
User lii = new User("lii", 21, Gender.MAN);
User wangwu = new User("wangwu", 22, Gender.MAN);
userRepository.save(zhangsan);
userRepository.save(lii);
userRepository.save(wangwu);
System.out.println("-----------------------------");
List<User> all = userRepository.findAll();
all.forEach(System.out::println);
System.out.println("------------------------------");
User user = userRepository.findById(12L).orElse(null);
System.out.println("更新前:"+user);
user.setName("hzx");
userRepository.save(user);
System.out.println("更新后:"+user);
System.out.println("-------------------------------");
userRepository.deleteAllInBatch();
}
}
在运行的时候JPA会自动建表,因为在application.properties中设置了如下属性
spring.jpa.hibernate.ddl-auto=update
dao层接口中,只继承了JPARepository接口,并没有定义其他方法,但是基本的操作依然可以使用。
3、查询
Spring Data JPA中提供了强大的查询功能,我们只需要在dao层接口中按规则,定出对应的查询方法,那么Spring Data JPA会根据我们所定义的 方法名 ,自动生成对应的sql语句,例如:
public interface UserRepository extends JpaRepository<User,Long> {
User findByName(String name);
List<User> findByAge(int age);
List<User> findByNameOrAge(String name,int age);
List<User> findByNameLike(String name);
User findByNameIgnoreCase(String name);
List<User> findByAgeOrderByNameDesc(int age);
//first和top的效果是一样的,默认取第一条数据
User findFirstByOrderByAgeAsc();
User findFirstByOrderByAgeDesc();
User findTopByOrderByAgeAsc();
User findTopByOrderByAgeDesc();
//first和top后面都可以跟数字,表示取查询数据的前N条数据
List<User> findFirst2ByGender(Gender gender);
}
当在测试类中调用我们自己在接口中定义的findByName方法的时候,会自动生成对应的sql语句:
@Test
public void test_findByName(){
User hzx = userRepository.findByName("hzx");
System.out.println(hzx);
}
控制台中输出的sql为:
当在测试类中调用我们自己在接口中定义的 findFirst2ByGender 方法的时候,会自动生成对应的sql语句:
@Test
public void test_findFirst2ByGender(){
List<User> first2ByGender = userRepository.findFirst2ByGender(Gender.MAN);
first2ByGender.forEach(System.out::println);
}
控制台中输出的sql为:
可以输出sql语句,因为在application.properties中配置了对应的属性:
spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true
在方法中所支持的关键字有很多,
官网说明
如下:
Keyword
方法名中支持的关键字
Sample
方法名示例
JPQL snippet
JPA生成的sql中的片段(查询条件部分 )
Keyword | Sample | JPQL snippet |
---|---|---|
|
|
|
|
|
|
,
|
,
,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
只要按照以上规则在接口中定义方法,那么Spring Data JPA会自动生成对应的 条件查询 语句
4、排序
Spring Data JPA中可以使用 Sort 对查询的结果进行排序,让 Sort 作为查询方法的参数即可。父接口中已经有定好的方法的参数含有 Sort ,例如 JpaRepository 中的 List findAll(Sort sort);也可以自己在接口中自定义方法的参数中添加 Sort 参数:
public interface UserRepository extends JpaRepository<User,Long> {
List<User> findByGender(Gender gender,Sort sort);
List<User> findFirst20ByGender(Gender gender, Sort sort);
}
@Test
public void test_sort1(){
List<User> age = userRepository.findAll(Sort.by(Sort.Direction.DESC, "age"));
age.forEach(System.out::println);
}
生成的sql语句为:
@Test
public void test_sort2(){
List<User> age = userRepository.findByGender(Gender.MAN, Sort.by(Sort.Direction.ASC, "age"));
age.forEach(System.out::println);
}
生成的sql语句为:
@Test
public void test_sort3(){
List<User> age = userRepository.findFirst20ByGender(Gender.MAN, Sort.by(Sort.Direction.ASC, "age"));
age.forEach(System.out::println);
}
生成的sql语句为:
Sort中的by方法声明为:可以接受多个排序的参数
public static Sort by(Sort.Direction direction, String... properties)
5、分页
Spring Data JPA中分页功能和排序的使用方式类似,只需要在查询方法的参数列表中添加一个Pageable 类型的参数即可。
父接口中已经有定好的方法的参数含有 Pageable ,例如 PagingAndSortingRepository 中的
Page findAll(Pageable pageable);
也可以自己在接口中自定义方法的参数中添加 Pageable 参数:
public interface UserRepository extends JpaRepository<User,Long> {
Page<User> findByGender(Gender gender, Pageable pageable);
Page<User> findFirst25ByGender(Gender gender, Pageable pageable);
List<User> findTop25ByGender(Gender gender, Pageable pageable);
}
方法的返回类型是可以是Page类型的,也可以是List集合类型,只是Page类型的返回值可以提供更多的分页相关信息。
@Test
public void test_pageable1(){
int page=0;
int size=2;
PageRequest pageable = PageRequest.of(page, size);
Page<User> pageObj = userRepository.findAll(pageable);
System.out.println("总页数: "+pageObj.getTotalPages());
System.out.println("总数据量: "+pageObj.getTotalElements());
System.out.println("当前的页码: "+pageObj.getNumber());
System.out.println("每页条数: "+pageObj.getSize());
System.out.println("当前页实际条数: "+pageObj.getNumberOfElements());
System.out.println("当前页内容: "+pageObj.getContent());
System.out.println("分页查询的排序规则为: "+pageObj.getSort());
System.out.println("当前是否为第一页: "+pageObj.isFirst());
System.out.println("当前是否为最后一页: "+pageObj.isLast());
System.out.println("当前是否有上一页: "+pageObj.hasPrevious());
System.out.println("当前是否有下一页: "+pageObj.hasNext());
System.out.println("返回上一页Pageable对象为: "+pageObj.previousPageable());
System.out.println("返回下一页Pageable对象为: "+pageObj.nextPageable());
}
生成的sql语句为:
运行结果为:
6、注解
Spring Data JPA中可以在接口中自定义方法上面使用 @Query 注解,在该注解中可以使用JPQL或SQL来指定此方法被调用的时候需要执行的sql语言是什么。
JPQL(JavaPersistence Query Language)是一种面向对象的查询语言,它在ORM框架中最终会翻译成为sql进行执行。在hibernate框架中,这种语句叫做HQL(Hibernate Query Language)。
JPQL是面向对象的查询语言,在查询语句中,出现的不是表的名字、字段的名字,而是类的名字和类中属性的名字,因为在ORM框架中,类和表,属性和字段都做好了映射关系,所以JPQL最后是可以根据映射关系转换sql语句的。
JPQL的特点:
-
语句中不能出现表名,列名,只能出现java的类名,属性名,并且区分大小写
-
语句中出现的关键字和sql语句中的是一样的意思,不区分大小写
-
语句中不能写select * 而是要写select 类的别名,或者写select 具体的属性名
@Query("select u from User where u.name = ?1")
User findByUsername(String name);
该语句中的?1代表的是第一个参数,?2代表第二个参数
也可以配合@Param注解使用命名参数的方式:
@Query("select u from User u where u.name = :name or u.age = :age")
User findByNameOrAge(@Param("name") String name,
@Param("age") int age);
@Query 注解还可以支持更新和删除的语句,但是需要结合 @Modifying 注解,以及事务管理的注解@Transactional
@Transactional
@Modifying
@Query("update User u set u.name = ?1 where u.id = ?2")
int updateNameByUserId(String name,Long id);
@Transactional
@Modifying
@Query("delete from User u where u.id = ?1")
void deleteByUserId(Long id);
以上代码在https://gitee.com/huangzhixiong2021/spring-data-jpa.git 中,有不足或者错误的地方欢迎指正。