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); }
3、修改原来的 UserController.java,代码略。