Spring Data框架为数据访问提供了一个通用的模型,无论访问哪种数据库,都可以使用同样的方式,主要有以下几个功能:
(1)提供数据与对象映射的抽象层,同一个对象,可以被映射为不同数据库的数据;
(2)根据数据存储接口的方法名,自动实现数据查询;
(3)为各个领域模型提供最基本的实现,例如增删改查功能;
(4)可在原有逻辑的基础上实现自定义数据库操作逻辑。
JPA是Spring Data框架的其中一个模块,全称为Java Persistence API,是一个持久层规范,Hibernate框架是JPA实现之一。
本文内容:
(1)项目构建
(2)数据访问层与业务层
(3)自定义数据存储逻辑
(4)方法名查询
(5)使用@Query注解
开发环境:IntelliJ IDEA 2019.2.2
Spring Boot版本:2.1.8
一、项目构建
1、新建一个名称为demo的Spring Boot项目。
2、pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
3、application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC
username: root
password:
4、打开Navicat for MySQL,在测试数据库testdb中创建表user
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL,
`age` tinyint(4) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
5、实体类 User.java
package com.example.demo.entity;
import javax.persistence.*;
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer 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;
}
}
二、数据访问层与业务层
数据访问层继承JpaRepository后会自动实现很多内置的方法,拥有基本的数据库CRUD操作。
1、数据访问层 UserRepository.java
package com.example.demo.repository;
import com.example.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User,Integer>{
}
2、业务层 UserService.java
package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class UserService {
@Autowired
UserRepository userRepository;
public void save(User user) {
userRepository.save(user);
}
public Page<User> getUserPage(Pageable pageable) {
return userRepository.findAll(pageable);
}
public List<User> getUsers(){
List<User> users = userRepository.findAll();
return users;
}
public Optional<User> findById(Integer id) {
return userRepository.findById(id);
}
public void deleteById(Integer id) {
userRepository.deleteById(id);
}
}
3、控制器 UserController.java
package com.example.demo;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;
@RestController
public class UserController {
@Resource
UserService userService;
@RequestMapping("/save")
public String save(){
for(int i=1;i<=20;i++){
User user = new User();
user.setName("a" + i);
user.setAge(i);
userService.save(user);
}
return "添加成功";
}
@RequestMapping("/getUserPage")
public Page<User> getUserPage(Integer page, Integer size){
Sort sort = new Sort(Sort.Direction.ASC, "id");
Pageable pageable = PageRequest.of(page,size,sort);
Page<User> users = userService.getUserPage(pageable);
return users;
}
@RequestMapping("/getUsers")
public List<User> getUsers(){
List<User> users = userService.getUsers();
return users;
}
@RequestMapping("/findById")
public Optional<User> findById(Integer id){
Optional<User> user = userService.findById(id);
return user;
}
@RequestMapping("/deleteById")
public String deleteById(Integer id){
userService.deleteById(id);
return "删除成功";
}
}
三、自定义数据存储逻辑
继承JpaRepository可以完成很多工作,但有时需要实现自定义数据存储逻辑。
使用例子:
1、新建一个接口 UserRepositoryCustom.java
package com.example.demo.repository;
import com.example.demo.entity.User;
import java.util.List;
public interface UserRepositoryCustom {
List<User> myQuery();
}
2、新建接口 UserRepositoryCustom的实现类UserRepositoryCustomImpl.java
package com.example.demo.repository.impl;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepositoryCustom;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.util.List;
public class UserRepositoryImpl implements UserRepositoryCustom {
@PersistenceContext
private EntityManager em;
public List<User> myQuery(){
//说明:下面这个User不是数据库表名,而是实体类名,并且区分大小写
Query q = em.createQuery("from User");
return q.getResultList();
}
}
3、修改原来的 UserRepository.java,同时继承JpaRepository和UserRepositoryCustom
package com.example.demo.repository;
import com.example.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User,Integer>,UserRepositoryCustom {
}
4、修改原来的 UserService.java,增加方法
public List<User> myQuery(){
return userRepository.myQuery();
}
5、修改原来的 UserController.java,代码略。
四、方法名查询
JpaRepository支持接口规范方法名查询,即如果在接口中定义的查询方法符合它的命名规则,就可以不用写实现逻辑。
例如根据对象User的字段name进行查询,实现类似“from User where name=?”查询,直接在接口中写“List<User> name(String name);”,方法名也可写findByName,Spring Data JPA框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如find、findBy、read、readBy、get、getBy,然后对剩下部分进行解析。另外还可以根据特定关键字实现条件查询,如下表所示:
关键字 | 例子 | 对应的SQL |
IsNotNull | findByAgeNotNull | ... where x.age not null |
Like | findByNameLike | ... where x.name like ?1 |
NotLike | findByNameNotLike | ... where x.name not like ?1 |
StartingWith | findByNameStartingWith | ... where x.name like ?1(parameter bound with appended %) |
EndingWith | findByNameEndingWith | ... where x.name like ?1(parameter bound with prepended %) |
Containing | findByNameContaining | ... where x.name like ?1(parameter bound wrapped in %) |
OrderBy | findByAgeOrderByName | ... where x.age = ?1 order by x.name desc |
Not | findByNameNot | ... where x.name <> ?1 |
In | findByAgeIn | ... where x.age in ?1 |
NotIn | findByAgeNotIn | ... where x.age not in ?1 |
True | findByActiveTrue | ... where x.avtive = true |
Flase | findByActiveFalse | ... where x.active = false |
And | findByNameAndAge | ... where x.name = ?1 and x.age = ?2 |
Or | findByNameOrAge | ... where x.name = ?1 or x.age = ?2 |
Between | findBtAgeBetween | ... where x.age between ?1 and ?2 |
LessThan | findByAgeLessThan | ... where x.age < ?1 |
GreaterThan | findByAgeGreaterThan | ... where x.age > ?1 |
After/Before | ... | ... |
IsNull | findByAgeIsNull | ... where x.age is null |
使用例子:
1、修改原来的 UserRepository.java,增加方法
@RequestMapping("/id")
public List<User> id(Integer id){
List<User> users = userService.id(id);
return users;
}
@RequestMapping("/name")
public List<User> name(String name){
List<User> users = userService.name(name);
return users;
}
@RequestMapping("/age")
public List<User> age(Integer age){
List<User> users = userService.age(age);
return users;
}
@RequestMapping("/findByIdAndName")
public List<User> findByIdAndName(Integer id, String name){
List<User> users = userService.findByIdAndName(id, name);
return users;
}
@RequestMapping("/findByAgeBetween")
public List<User> findByAgeBetween(Integer startAge, Integer endAge){
List<User> users = userService.findByAgeBetween(startAge, endAge);
return users;
}
2、修改原来的 UserService.java,增加方法
public List<User> id(Integer id){
return userRepository.id(id);
}
public List<User> name(String name){
return userRepository.name(name);
}
public List<User> age(Integer age){
return userRepository.age(age);
}
public List<User> findByIdAndName(Integer id, String name){
return userRepository.findByIdAndName(id, name);
}
public List<User> findByAgeBetween(Integer startAge, Integer endAge){
return userRepository.findByAgeBetween(startAge, endAge);
}
3、修改原来的 UserController.java,代码略。
五、使用@Query注解
在方法中使用@Query注解,提供JPQL(Java Presistence Query Language)或SQL语句,同样可以实现查询功能。
使用例子:
1、修改原来的 UserRepository.java,增加方法
@Query("select u from User u where u.name = ?1")
List<User> findUserName(String name);
@Query(value = "select * from user u where u.name = ?1", nativeQuery = true)
List<User> findNativeByName(String name);
2、修改原来的 UserService.java,增加方法
public List<User> findUserName(String name){
return userRepository.findUserName(name);
}
public List<User> findNativeByName(String name){
return userRepository.findNativeByName(name);
}