An overview of

In the previous article, I introduced several ways to access data, using Spring JDBC and using Mybatis framework, compared to the native two ways greatly simplified the data operation process, today I will introduce another way, using JPA to access the database.

What the JPA

Java Persistence API, full name for Java Persistence API, can describe the mapping between object-relational tables through annotations or XML, and persist entity objects to a database.

It provides us with:

1) ORM mapping metadata: JPA supports two forms of metadata, XML and annotations. Metadata describes the mapping relationship between objects and tables, and the framework persists entity objects to database tables.

Such as @entity, @table, @column, @TRANSIENT and other annotations.

2) JPA API: used to manipulate entity objects and perform CRUD operations. The framework does everything for us in the background, freeing developers from tedious JDBC and SQL code.

For example, entityManager.merge(T T);

3) JPQL query language: Query data through object-oriented rather than database-oriented query language, avoiding the tight coupling of SQL statements in the program.

From Student s where s.name =?

But:

JPA is just a specification, that is, it defines interfaces that need to be implemented to work. So you need some kind of implementation underneath, and Hibernate is the ORM framework that implements the JPA interface.

What is Spring Data JPA

Spirng Data Jpa is a set of framework provided by Spring to simplify Jpa development. By writing dao layer interface according to the agreed [method naming rule], access and operation to database can be realized without writing interface implementation. It also provides many functions in addition to CRUD, such as paging, sorting, complex queries, and so on.

Spring Data JPA can be understood as a re-encapsulation abstraction of THE JPA specification, with Hibernate’s JPA technology being used at the bottom.

The relationship among Spring Data JPA, Hibernate and JPA is as follows:

Simple introduction

Example code corresponding to the warehouse address: github.com/dragon8844/…

We use spring-boot-starter-data-jPA to automate the configuration of the Spring Data JPA, and use unit tests to demonstrate single-table CRUD operations. Without further ado, let’s start now

Introduction of depend on

Introduce dependencies into the pom.xml file:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency> <! -- In this example, we use MySQL -->
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <! Automatic configuration of Spring Data JPA
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <! Write unit tests later -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.jupiter</groupId>
                    <artifactId>junit-jupiter-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
Copy the code

Add the configuration

Create the application configuration file application.yml in the Resources directory and add the following configuration information:

spring:
  # datasource configures the datasource
  datasource:
    url: jdbc:mysql://localhost:3306/test? useSSL=false&useUnicode=true&characterEncoding=UTF-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
  # JPA configuration content, corresponding to the JpaProperties class
  jpa:
    show-sql: true Print SQL. The production environment is recommended to be shut down
    Hibernate configuration content, corresponding to the HibernateProperties class
    hibernate:
      ddl-auto: none
Copy the code
  • Datasource configures the datasource configuration item.

  • Jpa configuration items with Spring Data jpa configuration content, corresponding org. Springframework. Boot. Autoconfigure. The orm. Jpa. JpaProperties. Java classes.

  • Hibernate configuration items, configure hibernate configuration content, corresponding org. Springframework. Boot. Autoconfigure. The orm. Jpa. HibernateProperties. Java classes.

    • The DDL-auto configuration item sets the Hibernate DDL processing policy. The options are None, create, create-drop, update, and validate.

      Create: Each time Hibernate is loaded, the last generated table is deleted, and then the new table is generated again based on your Model class, even if there are no changes twice. This is an important cause of database table data loss.

      Create-drop: The table is generated from the Model class every time Hibernate is loaded, but the table is dropped automatically when sessionFactory is closed.

      Update: When Hibernate is loaded for the first time, the table structure will be automatically set up according to the model class (provided the database has been set up first). When Hibernate is loaded, the table structure will be automatically updated according to the Model class. Even if the table structure is changed, the rows in the table will remain. Note that when deployed to the server, the table structure is not established immediately, but only after the application is first run.

      Validate: Each time Hibernate is loaded, verify that the database table structure is created, and only the tables in the database are compared. No new tables are created, but new values are inserted.

Write the code

  • Writing entity classes

    @Entity
    @Table(name = "user")
    @Data
    public class User {
    
        /** * primary key */
        @Id
        @generatedValue (strategy = generationtype. IDENTITY, // strategy set the use of database primary key increment strategy; generator = "JDBC")
        private Integer id;
    
        /** * User name */
        private String username;
    
        /** * Password */
        private String password;
    
        /** * create time */
        private Date createTime;
    
        /** * Whether to delete */
        private Integer deleted;
    }
    Copy the code
  • DDL statement for entity class:

    CREATE TABLE `user` (
      `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'primary key',
      `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'Username',
      `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'password',
      `create_time` datetime DEFAULT NULL COMMENT 'Creation time',
      `deleted` tinyint(1) DEFAULT NULL COMMENT 'Deleted or not 0- Not deleted; 1 - delete '.PRIMARY KEY (`id`),
      UNIQUE KEY `idx_username` (`username`)
    ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
    Copy the code
  • Write the Repository class, com. Dragon. Springdatajpa. Under the dao package User01Repository create interfaces

    public interface User01Repository extends JpaRepository<User.Integer> {}Copy the code

    Inheritance org. Springframework. Data. The repository. CrudRepository interface, the first generic setting corresponds to the entity is the User, the second generic setting corresponds to the primary key of type Integer.

    Since the CrudRepository interface is implemented, Spring Data JPA automatically generates the corresponding CRUD code.

Unit testing

By creating the User01RepositoryTest test class, let’s test each operation of the simple User01Repository. The code is as follows:

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class User01RepositoryTest {

    @Resource
    User01Repository user01Repository;

    @Test
    public void testInsert(a){
        User user = new User();
        user.setDeleted(0);
        user.setUsername("Zhang");
        user.setCreateTime(new Date());
        user.setPassword("123456");
        user01Repository.save(user);
    }

    @Test
    public void testSelectById(a){
        Optional<User> userOptional = user01Repository.findById(13);
        if(userOptional.isPresent()){
            User user = userOptional.get();
            log.info("user: {}", user.getId()); }}@Test
    public void testUpdateById(a){
        Optional<User> userOptional = user01Repository.findById(13);
        if(userOptional.isPresent()){
            User user = userOptional.get();
            user.setUsername("Bill");
            user = user01Repository.save(user);
            log.info("user: {}", user.getUsername()); }}@Test
    public void testDeleteBy(a){
        user01Repository.deleteById(13); }}Copy the code

paging

Spring Data provide org. Springframework. Data. The repository. PagingAndSortingRepository interface, inherit the CrudRepository interface, on the basis of the CRUD operations, Additional paging and sorting operations are provided. The code is as follows:

// PagingAndSortingRepository.java

public interface PagingAndSortingRepository<T.ID> extends CrudRepository<T.ID> {

	Iterable<T> findAll(Sort sort); // Sort operations

	Page<T> findAll(Pageable pageable); // Paging operations

}
Copy the code

Writing a Repository interface

In com. Dragon. Springdatajpa. The dao package path, create UserRepository02 interface. The code is as follows:

public interface User02Repository extends PagingAndSortingRepository<User.Integer> {}Copy the code

Implementation PagingAndSortingRepository interface, the first generic setting corresponds to the entity is the User, the second generic setting corresponds to the primary key of type Integer.

Unit testing

Creating the UserRepository02Test test class, let’s test each action of the simple UserRepository02. The code is as follows:

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class User02RepositoryTest {

    @Resource
    User02Repository user02Repository;

    @Test
    / / sorting
    public void testList(a){
        Sort sort = Sort.by("id").descending();
        Iterable<User> iterable = user02Repository.findAll(sort);
        iterable.forEach(System.out :: println);
    }

    @Test
    / / paging
    public void testPage(a){
        Sort sort = Sort.by("id").descending();
        PageRequest pageRequest = PageRequest.of(0.10,sort);
        Page<User> page = user02Repository.findAll(pageRequest);
        log.info("page: {}", page.getTotalElements());
        log.info("content:{}", page.getContent()); }}Copy the code

Query based on method name

In Spring Data, there is support for generating query (WHERE) conditions based on method names, further evolving to JPA WHERE method names start with findBy, existsBy, countBy, deleteBy, followed by specific conditions. The rules are detailed in the Spring Data JPA — Query Creation document. As follows:

The keyword Methods the sample JPQL snippet
And findByLastnameAndFirstname ... where x.lastname = ? 1 and x.firstname = ? 2
Or findByLastnameOrFirstname ... where x.lastname = ? 1 or x.firstname = ? 2
Is.Equals findByFirstname.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.Null findByAge(Is)Null ... 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 ages) ... 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)

Writing a Repository interface

public interface User03Repository extends PagingAndSortingRepository<User.Integer> {
/** * query * by username@paramusername
*@return* /
User findByUsername(String username);

/** * paging query user * after specified time@paramcreateTime
*@parampageable
*@return* /
Page<User> findByCreateTimeAfter(Date createTime, Pageable pageable);
}
Copy the code

If a Pageable operation is involved in a method, you need to use the Pageable parameter as the last parameter of the method.

Unit testing

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class User03RepositoryTest {

    @Resource
    User03Repository user03Repository;

    @Test
    public void testFindByUsername(a){
        User user = user03Repository.findByUsername("Zhang");
        log.info("user:{}", user.getUsername());
    }

    @Test
    public void findByCreateTimeAfter(a){
        Sort sort = Sort.by("id").descending();
        PageRequest pageRequest = PageRequest.of(0.10,sort);
        Page<User> page = user03Repository.findByCreateTimeAfter(new Date(), pageRequest);
        log.info("page:{}",page.getTotalElements()); }}Copy the code

Annotation-based query

Spring Data JPA provides a very powerful method name-based query mechanism that can be used for CRUD operations in most business scenarios, but there may be special cases where we still need to use a custom SQL operation database. We can use the method to add org. Springframework. Data. Jpa. Repository. @ Query annotation.

If it is update, or delete SQL operations, need on the way to add additional org. Springframework. Data. Jpa. Repository. @ Modifying annotation.

Writing a Repository interface

/** * query based on annotations **@author LiLong
 * @date2020/12/24 * /
public interface User04Repository extends PagingAndSortingRepository<User.Integer> {

    / * * *@QueryCustomizing an SQL operation and using placeholders for arguments (?) + the form of the parameter position *@param username
     * @return* /
    @Query("SELECT u FROM User u WHERE u.username = ? 1)"
    User findByUsername01(String username);

    /** * uses placeholder (:) + parameter name (required@ParamDeclaration). *@param username
     * @return* /
    @Query("SELECT u FROM User u WHERE u.username = :username")
    User findByUsername02(@Param("username") String username);

    /** * nativeQuery = true@QueryCustom is native SQL *@param username
     * @return* /
    @Query(value = "SELECT * FROM user u WHERE u.username = :username", nativeQuery = true)
    User findByUsername03(@Param("username") String username);

    /** * defines the update operation and needs to be added@ModifyingNote *@param id
     * @param username
     * @return* /
    @Query("UPDATE User u SET u.username = :username WHERE u.id = :id")
    @Modifying
    Integer updateUsernameById(Integer id, String username);
    
}
Copy the code

Unit testing

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class User04RepositoryTest {

    @Resource
    User04Repository user04Repository;

    @Test
    public void testFindByUsername01(a){
        User user = user04Repository.findByUsername01("Zhang");
        log.info("user:{}", user.getUsername());

    }

    @Test
    public void testFindByUsername02(a){
        User user = user04Repository.findByUsername02("Zhang");
        log.info("user:{}", user.getUsername());

    }

    @Test
    public void testFindByUsername03(a){
        User user = user04Repository.findByUsername03("Zhang");
        log.info("user:{}", user.getUsername());

    }

    @Test
    @Transactional
    public void testUpdateUsernameById(a){
        Integer count = user04Repository.updateUsernameById(14."Change");
        log.info("count:{}", count); }}Copy the code

summary

Spring Data JPA (Spring Data JPA) Spring Data JPA (Spring Data JPA) We use spring-boot-starter-data-jPA to complete the automatic assembly of Spring Data JPA, add jPA configuration to the configuration file, and then write entity class and Repository class. In entity class, we use JPA annotations. The Repository class inherits JpaRepository and implements CRUD operations on a single table. In actual development, there are more paging operations and queries based on method names. The query mechanism based on method names is really powerful, and most business scenarios can be handled. There are special usage scenarios that require native SQL, which can also be queried using annotation-based mechanisms.

One last word

If this article is helpful to you, or inspired, help pay attention to it, your support is the biggest motivation I insist on writing, thank you for your support.

In addition, pay attention to the public number: black lighthouse, focus on Java back-end technology sharing, covering Spring, Spring the Boot, SpringCloud, Docker, Kubernetes middleware technology, etc.