1. Introduction to JPA
JPA (Java Persistence API) is a Java Persistence API. It is a Java persistence specification based on ORM proposed by Sun company.
The full name of ORM (Object Relational Mapping) is Object Relational Mapping. The mainstream Java ORM frameworks include Mybatis, Hibernate, etc.
2. Spring Data JPA
Spring Data JPA is a module of Spring Data framework, which can simplify the implementation of JPA. In addition, Spring Data JPA can also help us simplify the development of persistence layer. For simple queries, Spring Data JPA can resolve according to the method name and automatically generate query statements for query; For complex queries, Spring Data JPA also supports native SQL.
3. Spring Data JPA practice
Create a SpringBoot project and use JPA to implement simple CRUD.
3.1 introducing dependencies
POM files are as follows:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
3.2 configuring JPA and MySQL
application. The YML configuration file is as follows:
spring: datasource: url: jdbc:mysql://localhost:3306/jpa_test?serverTimezone=UTC username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update # When there is data in the table, it will not be cleared, only updated # Console display SQL show-sql: true
3.3 writing entity classes
This article takes the User class as an example:
package com.example.entity; import lombok.Data; import javax.persistence.*; /** * @Author join * @Date 2021/10/13 */ @Data @Entity @Table(name = "user") public class User extends BaseEntity{ @Id @Column(name = "id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(name = "name", columnDefinition = "varchar(20) not null") private String name; }
The User class contains id and name attributes, and the User class also inherits BaseEntity. When there are multiple entity classes in the project, we might as well encapsulate the common fields in the data table in BaseEntity, such as create_time,update_time, etc.
package com.example.entity; import lombok.Data; import javax.persistence.*; import java.util.Date; /** * @Author join * @Date 2021/10/13 */ @Data @MappedSuperclass public class BaseEntity { @Column(name = "create_time") @Temporal(TemporalType.TIMESTAMP) private Date createTime; @Column(name = "update_time") @Temporal(TemporalType.TIMESTAMP) private Date updateTime; @PrePersist public void prePersist() { Date now = new Date(); if (createTime == null) { createTime = now; } if (updateTime == null) { updateTime = now; } } @PreUpdate public void preUpdate() { updateTime = new Date(); } @PreRemove public void preRemove() { updateTime = new Date(); } }
Note explanation:
- @Data: lombok annotation, which can automatically generate methods such as get(), set(), toString();
- @Entity: JPA annotation, which declares that this class is an entity class and must be used with @ Id;
- @Table: JPA annotation, indicating that the entity class will be mapped to the user table of the database (name specifies the table name);
- @Id: JPA annotation, declaring the primary key;
- @GeneratedValue: JPA annotation, which indicates the generation strategy of the primary key, and IDENTITY indicates the use of self incrementing id;
- @Column: JPA annotation, which declares which field the attribute of the entity object is mapped to in the data table. Name specifies the field name and columnDefinition specifies the field definition.
- @MappedSuperclass: JPA annotation, which is applied to the parent class of the entity class. The class under the annotation will not be mapped to the data table, but its properties will be mapped to the data table of the child class.
- @PrePersist: the method modified by @ PrePersist is called before persisting the entity to the database;
- @PreUpdate: the method modified by @ PreUpdate is called when an attribute of the entity changes, such as updating the update of the entity_ time;
- @PreRemove: the method modified by @ PreRemove is called before the entity is deleted from the database.
3.4 definition of repository
Write UserRepository:
package com.example.repository; import com.example.entity.User; import lombok.NonNull; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; /** * @Author join * @Date 2021/10/13 */ public interface UserRepository extends JpaRepository<User, Integer> { @NonNull Optional<User> findByName(@NonNull String name); }
The rud repository() method can be automatically inherited from the rud repository() method, such as save repository(). In the above code, we also customized a method findByName(), and JpaRepository can automatically implement the corresponding logic according to the method name.
Be aware of the naming conventions of the JpaRepository.
3.5 define controller
Write UserController:
package com.example.controller; import com.example.entity.User; import com.example.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import java.util.Optional; /** * @Author join * @Date 2021/10/13 */ @Controller @RequestMapping("/user") public class UserController { @Autowired private UserRepository userRepository; @RequestMapping(path = "addUser", method = RequestMethod.POST) @ResponseBody public void addUser(@RequestBody User user) { userRepository.save(user); } @RequestMapping(path = "deleteUser", method = RequestMethod.POST) @ResponseBody public void deleteUser(@RequestBody User user) { userRepository.delete(user); } @RequestMapping(path = "/getById/{id}", method = RequestMethod.GET) @ResponseBody public User queryUserById(@PathVariable("id") int id) { return userRepository.findById(id).orElse(null); } @RequestMapping(path = "/getByName/{name}", method = RequestMethod.GET) @ResponseBody public User queryUserByName(@PathVariable("name") String name) { Optional<User> optional = userRepository.findByName(name); return optional.orElse(null); } }
In the above code, we have implemented four methods, which are used to create, delete and query users respectively.
3.6 testing
Start the project and use Navicat to view 'JPA'_ From the data table under 'test' library, we found that JPA automatically created the user table for us:
The table contains the id and name defined in the User class and the create defined in the BaseEntity class_ time,update_time and other fields.
Add user
Use Postman to send a post request and add user:
Query user
Query with id:
Query with name:
4. JPA analysis principle
JPA follows the principle of Convention over configuration and the naming rules defined by spring and JPQL. Spring provides a mechanism for query construction according to naming rules. This mechanism will filter out some keywords from the method name, such as find... By, read... By, query... By, count... By and get... By. The system will parse the method name into two sub statements according to the keyword. The first by is the keyword that distinguishes the two sub statements. The clause before by is a query clause, indicating the returned object. The rest is the conditional clause. If the method name is findBy..., then the object specified when repository is defined will be returned. The following table shows the usage instructions of some keywords:
Keyword | Sample | JPQL snippet |
---|---|---|
And | findByLastnameAndFirstname | ... where x.lastname = ?1 and |
Or | findByLastnameOrFirstname | ... where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals | 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 age) | ... 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) |
Welcome criticism and correction!!!