首先介绍必要配置及规范,再讲解具体使用
pom.xml配置:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
application.properties配置:
########################################################
### Spring datasource -- Datasource configuration.
########################################################
spring.datasource.url = jdbc:mysql://localhost:3306/test1?characterEncoding=UTF-8
spring.datasource.username = root
spring.datasource.password = 123456
spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver
########################################################
### Java Persistence Api -- Spring jpa configuration.
########################################################
# Specify the DBMS
spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.show-sql = true
# Hibernate ddl auto (create, create-drop, update, validate, none)
spring.jpa.hibernate.ddl-auto = update
# Naming strategy
#[org.hibernate.cfg.ImprovedNamingStrategy #org.hibernate.cfg.DefaultNamingStrategy]
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
# stripped before adding them to the entity manager)
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
启动类:
@SpringBootApplication
@ComponentScan("com.jpa")//开启spring注解扫描
@EnableJpaRepositories(basePackages = {"com.jpa.repository"})//扫描JPA资源库
@EntityScan(basePackages = {"com.jpa.entity"})//扫描实体类
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
实体类:
@Data//编译时生成get/set、toString等方法(lombok)
@NoArgsConstructor//编译时生成无参构造方法(lombok)
@AllArgsConstructor//编译时生成全参构造方法(lombok)
@Entity
@Table(name="book")// 指定关联的数据库的表名
public class BookEntity {
@Id//声明主键ID
@GeneratedValue(strategy = GenerationType.IDENTITY)//自动生成id,使用jpa提供的save()方法必须先为类分配id,没有该注解会报错
private Long id;
private String name;
private String isbn;
private String author;
}
持久层,使用JPA进行数据持久化有两种实现方式
方式一:使用Spring Data JPA 提供的接口默认实现,
方式二:自定义符合Spring Data JPA规则的查询方法,由框架将其自动解析为SQL。
方式一:
Spring data JPA提供给用户使用的,主要有以下几个接口:
1.Repository:仅仅是一个标识,表明任何继承它的均为仓库接口类,方便Spring自动扫描识别
2.CrudRepository:继承Repository,实现了一组CRUD相关的方法
3.PagingAndSortingRepository:继承CrudRepository,实现了一组分页排序相关的方法
4.JpaRepository:继承PagingAndSortingRepository,实现一组JPA规范相关的方法
5.JpaSpecificationExecutor:比较特殊,不属于Repository体系,实现一组JPA Criteria查询相关的方法。
Crudrepository接口提供了一些简单的增删改查功能:
<S extends T> S save(S entity); // 保存并返回(修改后的)实体
<S extends T> Iterable<S> save(Iterable<S> entities); // 保存并返回(修改后的)实体集合
T findOne(ID id); // 根据ID获取实体
boolean exists(ID id); // 判断指定ID的实体是否存在
Iterable<T> findAll(); // 查询所有实体
Iterable<T> findAll(Iterable<ID> ids); // 根据ID集合查询实体
long count(); // 获取实体的数量
void delete(ID id); // 删除指定ID的实体
void delete(T entity); // 删除实体
void delete(Iterable<? extends T> entities); // 删除实体集合
void deleteAll(); // 删除所有实体
PagingAndSortingRepository继承于CrudRepository,除了具有CrudRepository接口的能力外,还新增了分页和排序的功能,接口定义如下:
Iterable<T> findAll(Sort sort); // 查询所有实体并排序
Page<T> findAll(Pageable pageable); // 分页查询实体
JpaRepository继承于PagingAndSortingRepository,所以它传递性地拥有了以上接口的所有方法,同时,它还继承了另外一个QueryByExampleExecutor接口,拥有了该接口匹配指定样例的能力,JpaRepository接口定义如下:
List<T> findAll(); // 查询所有实体
List<T> findAll(Sort sort); // 查询所有实体并排序
List<T> findAll(Iterable<ID> ids); // 根据ID集合查询实体
<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); // 根据ID查询实体
@Override
<S extends T> List<S> findAll(Example<S> example); // 查询与指定Example匹配的所有实体
@Override
<S extends T> List<S> findAll(Example<S> example, Sort sort);// 查询与指定Example匹配的所有实体并排序
QueryByExampleExecutor接口允许开发者根据给定的样例执行查询操作,接口定义如下:
<S extends T> S findOne(Example<S> example); // 查询与指定Example匹配的唯一实体
<S extends T> Iterable<S> findAll(Example<S> example); // 查询与指定Example匹配的所有实体
<S extends T> Iterable<S> findAll(Example<S> example, Sort sort); // 查询与指定Example匹配的所有实体并排序
<S extends T> Page<S> findAll(Example<S> example, Pageable pageable);// 分页查询与指定Example匹配的所有实体
<S extends T> long count(Example<S> example); // 查询与指定Example匹配的实体数量
<S extends T> boolean exists(Example<S> example); // 判断与指定Example匹配的实体是否存在
方式二,自定义查询方法
除了可以直接使用Spring Data JPA接口提供的基础功能外,Spring Data JPA还允许开发者自定义查询方法,对于符合以下命名规则的方法,Spring Data JPA能够根据其方法名为其自动生成SQL,除了使用示例中的 find 关键字,还支持的关键字有:query、get、read、count、delete等。
find+全局修饰+By+实体属性名+限定词+连接词+.(其它实体属性)+OrderBy+ 排序属性+排序方向
例如:
findDistinctByFirstNameIgnoreCaseAndLastNameOrderByAgeDesc(String firstName,String lastName);
其中:Distinct是全局修饰(非必须),FirstName和LastName是实体的属性名, And是连接词,IgnoreCase是限定词,Age是排序属性,Desc是排序方向,限定词 和连接词统称为“关键词”
常用词如下:
全局修饰:Distinct,Top,First
关键词:IsNull,IsNotNull,Like,NotLike,Containing,In,NotIn, IgnoreCase,
Between,Equals,LesThan,GreaterThan,After,Before
排序方向:Asc,Desc
连接词:And,Or
示例:
// 泛型参数1 : 实体类
// 泛型参数2 : 实体类中主键的类型
@Repository
public interface Demo extends JpaRepository<User,Integer>{
// =============标准命名方式===============
// 根据名字进行精准查询,Standard类中有name字段
User findByName(String name);
// 根据名字进行模糊查询
User findByNameLike(String name);
// 查询名字为空的数据
List<User> findByNameIsNull();
// 多条件查询
User findByNameAndPassword(String name,String password);
// ==============非标准命名方式=============
// 使用JPQL进行非标准命名查询
@Query("from User u where u.name like ?")
User findByNamexxxxxLikeJPQL(String name);
// 使用JPQL进行非标准多条件查询
// 默认情况下,问号的顺序和传入的参数顺序是一致的
// 可以在问号后面追加数字,改变和参数的匹配顺序
// 下面的示例中,传入的第一个参数匹配到第二个问号,传入的第二个参数匹配到第一个问号
@Query("from User u where u.name like ?2 and password = ?1")
User findByNameAndOperatorJPQL(String password,String name);
// 使用标准SQL进行非标准命名查询
@Query(value = "select * from user u where u.name like ?", nativeQuery = true)
User findByNamexxxxxLikeSQL(String name);
// 自定义增删改操作
@Transactional // 使用事务
@Modifying // 执行修改操作
@Query("delete from User u where u.name = ?")
void deleteByName(String name);
@Transactional
@Modifying // 执行修改操作
@Query("update User u set u.password = ?2 where u.name = ?1")
void updatePasswordByName(String name, String password);
}
分页查询
普通分页
首先分页查询的dao层需要集成两个接口:
@Repository
public interface BookRepository extends JpaRepository<BookEntity,Long>, JpaSpecificationExecutor<BookEntity> {
}
Service层:
@Autowired
BookRepository bookRepository; //dao层接口
@Override
public Page<BookEntity> findBookNoCriteria(Integer page,Integer size) {
Pageable pageable = new PageRequest(page, size, Sort.Direction.ASC, "id");
return bookRepository.findAll(pageable);
}
Sort.Direction.ASC,”id”是指根据id排序
Page<T>接口是jpa自带的,getContent()方法返回查询的数据集合,getNumber()返回当前页码(从0开始,不是从1开始),getSize()返回每页显示的数量。
多条件(含模糊查询)查询分页
Service层:
@Override
public Page<BookEntity> findBookCriteria(Integer page, Integer size, final BookQuery bookQuery) {
Pageable pageable = new PageRequest(page, size, Sort.Direction.ASC, "id");
//构造查询条件
Page<BookEntity> bookPage = bookRepository.findAll(new Specification<BookEntity>(){
@Override
public Predicate toPredicate(Root<BookEntity> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
/**
*创建一个查询的where语句
*@param root :根对象,可以简单的认为就是泛型对象
*@param criteriaBuilder:构建查询条件
*/
List<Predicate> list = new ArrayList<Predicate>();//存储条件的集合
if(null!=bookQuery.getName()&&!"".equals(bookQuery.getName())){
//构建模糊查询条件criteriaBuilder.like
list.add(criteriaBuilder.like(root.get("name").as(String.class),“%”+bookQuery.getName()+”%”));
}
if(null!=bookQuery.getIsbn()&&!"".equals(bookQuery.getIsbn())){
//构建精确查询条件criteriaBuilder.equal
list.add(criteriaBuilder.equal(root.get("isbn").as(String.class), bookQuery.getIsbn()));
}
if(null!=bookQuery.getAuthor()&&!"".equals(bookQuery.getAuthor())){
list.add(criteriaBuilder.equal(root.get("author").as(String.class),
bookQuery.getAuthor()));
}
//in条件
Set<Long> ids = new HashSet<>();
if (!CollectionUtils.isEmpty(ids)) {
In<Long> in = criteriaBuilder.in(root.get("id"));
ids.forEach((c) -> in.value(c));
list.add(in);
}
Predicate[] p = new Predicate[list.size()];
return criteriaBuilder.and(list.toArray(p));
}
},pageable);
return bookPage;
}
测试类:
@Test
public void testFindBookNoCriteria() throws Exception {
Page<BookEntity> page = bookQueryService.findBookNoCriteria(0, 5);
System.out.println(page.getContent());
}
@Test
public void testFindBookCriteria() throws Exception {
BookQuery bookQuery = new BookQuery("%1%",null,null);
Page<BookEntity> bookPage = bookQueryService.findBookCriteria(0, 5, bookQuery);
System.out.println(bookPage.getContent());
}