Springdata – jpa Springboot integration

“This is the fifth day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

About the author

  • The authors introduce

🍓 Blog home Page: author home page 🍓 Introduction: High-quality creator in the JAVA field 🥇, a junior student 🎓, participated in various provincial and national competitions during school, and won a series of honors 🍓, Ali Cloud expert blogger, 51CTO expert blogger, follow me: Pay attention to my learning materials, document download all have, regularly update the article every day, inspirational to do a JAVA senior program ape 👨💻


1, the introduction of

The purpose of the Spring Data project is to simplify Data access for building applications based on the Spring framework, including relational database libraries, non-relational databases, map-Reduce frameworks, cloud Data services, etc. Spring Data JPA is a sub-project under Spring Data that provides operational support for JPA.

spring-data

spring-data-jpa

Spring – data – jpa server Reference Documentation

JPA(Java Persistence API) is the official Java Persistence specification proposed by Sun. It provides Java developers with an object/association mapping tool to manage relational data in Java applications. It appears mainly to simplify the existing persistence development work and integrate ORM technology, ending the current situation of Hibernate, TopLink, JDO and other ORM frameworks operating separately. It is worth noting that JPA is developed on the basis of existing ORM frameworks such as Hibernate, TopLink and JDO. JPA was added to the specification in JAVA EE 5.

** Note :**JPA is a specification, not a product. Hibernate is an ORM framework that implements the JPA specification.

Spring Data JPA is a set of JPA application framework encapsulated by Spring based on ORM framework and JPA specification, which enables developers to access and operate Data with minimal code. It provides common functions such as add, delete, change, check, etc. Using Spring Data JPA can greatly improve development efficiency! Spring Data JPA frees us from the DAO layer, and basically all CRUDS can rely on it for implementation.

With springBoot’s automatic configuration, SpringData-JPA becomes much easier to use.

2, use,

To use Spring Data JPA in SpringBoot, you need to introduce its corresponding starter. Maven will automatically introduce JPA and its corresponding implementation. It uses Hibernate implementation by default. You can then use the Spring-Data-JPA framework, such as the SpringData-jPA project

The directory structure

Pom. XML file: Import springData-JPA with its corresponding starter


      
<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.6.2</version>
        <relativePath/> <! -- lookup parent from repository -->
    </parent>
    <groupId>com.sxau</groupId>
    <artifactId>demojpa</artifactId>
    <version>0.0.1 - the SNAPSHOT</version>
    <name>demojpa</name>
    <description>JPAdemo</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-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.12.4</version>
        </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>
Copy the code

Maven automatically introduces JPA and its corresponding implementation. If we look at the dependencies maven introduces, we can see that it uses Hibernate implementation by default.

Application. Properties file information:

spring.datasource.url=jdbc:mysql://localhost:3306/advertise? characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT&2B
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
Create tables automatically if necessary
spring.jpa.hibernate.ddl-auto=update
If necessary, the SQL statement executed can be displayed
Create tables automatically if necessary
#create allows you to drop tables and then create tables after each test start
SQL > alter table create table create table create table create table create table
spring.jpa.showsql=true
Format the SQ1 statement if necessary
spring.jpa.properties.hibernate.format_sql=true
Copy the code

Spring Data JPA configuration parameters in SpringBoot:

The website address

Some basic CURD methods are predefined in the Spring Data JPA in the form of a generic interface. The entity type of the operation and the corresponding primary key type need to be provided in the generic.

Note: we don’t need to implement these methods, they are already implemented in the Spring Data JPA framework, we just need to use them straight after dependency injection.

Next we see org. Springframework. Data. Jpa. Repository. JpaRepository

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.data.jpa.repository;

import java.util.List;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;

@NoRepositoryBean
// The generic T represents the type of the entity class to be operated on. The generic ID represents the type of the attributes corresponding to the primary key column in the database
public interface JpaRepository<T.ID> extends PagingAndSortingRepository<T.ID>, QueryByExampleExecutor<T> {
    List<T> findAll(a);

    List<T> findAll(Sort sort);

    List<T> findAllById(Iterable<ID> ids);

    <S extends T> List<S> saveAll(Iterable<S> entities);

    void flush(a);

    <S extends T> S saveAndFlush(S entity);

    <S extends T> List<S> saveAllAndFlush(Iterable<S> entities);

    / * *@deprecated* /
    @Deprecated
    default void deleteInBatch(Iterable<T> entities) {
        this.deleteAllInBatch(entities);
    }

    void deleteAllInBatch(Iterable<T> entities);

    void deleteAllByIdInBatch(Iterable<ID> ids);

    void deleteAllInBatch(a);

    / * *@deprecated* /
    @Deprecated
    T getOne(ID id);

    T getById(ID id);

    <S extends T> List<S> findAll(Example<S> example);

    <S extends T> List<S> findAll(Example<S> example, Sort sort);
}
Copy the code

JpaRepository inherits all the parent interfaces:

The Repository interface is a top-level interface that does not provide any abstract methods.

CrudRepository interface, which provides basic CURD methods.

PagingAndSortingRepository interface, provides support for paging and sorting method.

QueryByExampleExecutor interface, which provides a QBE query mode.

JpaRepository interface, which inherits the above interface, rewrites some methods, and defines some new methods.

When used, you just need to customize the Mapper layer interface and then inherit the JpaRepository interface and specify the type of the generics in the interface.

Mapper Layer interface UserMapper

package com.sxau.demojpa.mapper;

import com.sxau.demojpa.pojo.User;
import org.springframework.data.jpa.repository.JpaRepository;

/ * * *@ProjectName: demojpa
 * @Package: com.sxau.demojpa.dao
 * @ClassName: UserMapper
 * @Author: Shengrui Zhang *@Date: 2022/1/7 16:05
 * @Version: 1.0 * /
public interface UserMapper extends JpaRepository<User.Long> {}Copy the code

Entity class User

package com.sxau.demojpa.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.io.Serializable;

/ * * *@ProjectName: demojpa
 * @Package: com.sxau.demojpa.bean
 * @ClassName: User
 * @Author: Shengrui Zhang *@Date: 2022/1/7 16:06
 * @Version: 1.0 * /
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table
public class User implements Serializable {
// @ld indicates that the current attribute corresponds to the primary key column in t_user
// @generatedValue indicates that the column value in the t user table corresponding to this attribute is automatically increased
// @column (nullable-false,unique-true) indicates that the Column value in the t user table corresponding to the current attribute is non-null only - -
// @Enumerated(enumtype.string) indicates the STRING form of the enumeration type corresponding to the current attribute, instead of the original number
// Note that for oracle databases, you can use sequences to generate primary key column values, as explained in the annotations section of the code above.

    private static final Long serialVersionUID = 1L;
    @Id
    @GeneratedValue
    private Long id;
// Nullable is null and unique is unique
    @Column(nullable = false, unique = true)
    private String name;

    @Column(nullable = false)
    private Integer age;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private Gender gender;

	public User(String name, Integer age, Gender gender) {
        this.name = name;
        this.age = age;
        this.gender = gender; }}Copy the code

Let’s explain what the above notes mean, huh

@Entity Indicates that the current class is an entity class in JPA
@Table(name=”t_user”) T_user represents the table in the database corresponding to the current entity class
@Id Represents the primary key column in the T_USER table corresponding to the current attribute
@@GeneratedValue Indicates that the value of the column in the T_USER table is automatically increased
@@Column(nullable=false,unique=true) Indicates that the column value in the t_USER table corresponding to the current attribute is non-null unique
@Enumerated(EnumType.STRING) Represents the String representation of the enumeration type for the current property, rather than the raw number

The test class

package com.sxau.demojpa;

import com.sxau.demojpa.mapper.StudentMapper;
import com.sxau.demojpa.mapper.UserMapper;
import com.sxau.demojpa.pojo.Address;
import com.sxau.demojpa.pojo.Gender;
import com.sxau.demojpa.pojo.Student;
import com.sxau.demojpa.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
class DemojpaApplicationTests {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private StudentMapper studentMapper;
    @Test
    void contextLoads(a) {
        User user1 = new User("tom1".20,Gender.MALE);
        User user2 = new User("tom2".21,Gender.MALE);
        User user3 = new User("tom3".22,Gender.FEMALE);
        userMapper.save(user1);
        userMapper.save(user2);
        userMapper.save(user3);

        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
        List<User> list = userMapper.findAll();
        list.forEach(System.out::println);

        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
        User user = userMapper.findById(3L).orElse(null);
        System.out.println("Before Update:"+user);

        user.setName("zsr");
        userMapper.save(user);
        System.out.println("After update:"+user);

        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); userMapper.deleteAllInBatch(); }}Copy the code

We found that JPA automatically builds tables at runtime, mainly because we configured them in application.properties:

 spring.jpa.hibernate.ddl-auto=update
Copy the code

In the Mapper layer interface, only the JpaRepository interface is inherited and no other methods are defined, but the basic operations still use success calls.

3, the query

Spring Data JPA provides a powerful query function, we just need to determine the corresponding query method in the Mapper layer interface according to the rules, then Spring Data JPA will automatically generate the corresponding SQL statement according to the == method name == defined by us, for example:

package com.sxau.demojpa.mapper;

import com.sxau.demojpa.pojo.Gender;
import com.sxau.demojpa.pojo.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

/ * * *@ProjectName: demojpa
 * @Package: com.sxau.demojpa.dao
 * @ClassName: UserMapper
 * @Author: Shengrui Zhang *@Date: 2022/1/7 16:05
 * @Version: 1.0 * /
public interface UserMapper extends JpaRepository<User.Long> {
    User findByName(String name);

    List<User> findByAge(int age);

    List<User> findByNameOrAge(String name, int age);

    List<User> findByNameLike(String name);

    User findByNameIgnoreCase(String name);

    List<User> findByAgeOrderByNameDesc(int age);

    // The effect of first and top is the same, default to the first data
    User findFirstByOrderByAgeAsc(a);

    User findFirstByOrderByAgeDesc(a);

    User findTopByOrderByAgeAsc(a);

    User findTopByOrderByAgeDesc(a);

    // First and top can be followed by a number to indicate the first N rows of the queried data
    List<User> findFirst2ByGender(Gender gender);
}
Copy the code

When we call the findByName method we defined in our interface in the test class, the corresponding SQL statement is automatically generated:

    @Test
    public void test_findByName(a) throws Exception {
        User user = userMapper.findByName("zsr");
        System.out.println(user);
    }
Copy the code

SQL output from the console is:

Hibernate: 
    select
        user0_.id as id1_2_,
        user0_.age as age2_2_,
        user0_.gender as gender3_2_,
        user0_.name as name4_2_ 
    from
        t_user user0_ 
    where
        user0_.name=?
Copy the code

When we call the findFirst2ByGender method we defined in our interface in the test class, the corresponding SQL statement is automatically generated:

@Test
public void test_findFirst2ByGender(a) throws Exception {
 List<User> list = userMapper.findFirst2ByGender(Gender.MALE);
 list.forEach(System.out::println);
}
Copy the code

SQL output from the console is:

Hibernate:
 select
 user0_.id as id1_1_,
 user0_.age as age2_1_,
 user0_.gender as gender3_1_,
 user0_.name as name4_1_
 from
 t_user user0_
 where
 user0_.gender=? limit ?
Copy the code

Can output the SQL statement, because in the application. The properties in the configuration of the corresponding attributes: spring. Jpa. Properties. Hibernate. Format_sql = true

There are a number of supported keywords in the method, as described below on the official website:

Keyword Indicates the Keyword supported by the method name

Sample Method name example

Snippets in JPQL SNIPpet JPA-generated SQL (query criteria section)

Supported keywords inside method names

Keyword
And
or
Is, Equals
Between
LessThan
LessThanEqual
GreaterThan !
GreaterThanEqual
After
Before
IsNull, Null
IsNotNull,NotNull
Like
NotLike
Startingwith
EndingWith
Containing
OrderBy
Not
In
NotIn
True
False
IgnoreCase
Once methods are defined in the interface according to the above rules, Spring Data JPA automatically generates the corresponding == conditional query == statement

4, sorting,

Spring Data JPA allows you to Sort the results of a query using Sort as an argument to the query method. The parent interface has defined methods whose arguments have Sort, such as List findAll(Sort Sort) in JpaRepository;

You can also add the Sort parameter to the parameters of the custom method in the interface:

package com.sxau.demojpa.mapper;

import com.sxau.demojpa.pojo.Gender;
import com.sxau.demojpa.pojo.User;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

/ * * *@ProjectName: demojpa
 * @Package: com.sxau.demojpa.dao
 * @ClassName: UserMapper
 * @Author: Shengrui Zhang *@Date: 2022/1/7 16:05
 * @Version: 1.0 * /
public interface UserMapper extends JpaRepository<User.Long> {

    List<User> findByGender(Gender gender, Sort sort);

    List<User> findFirst25ByGender(Gender gender, Sort sort);
}
Copy the code
@Test
public void test_sort1(a) throws Exception {
    List<User> list = userMapper.findAll(Sort.by(Sort.Direction.DESC, "age"));
    list.forEach(System.out::println);
}
Copy the code

The generated SQL statement is:

Hibernate: 
    select
        user0_.id as id1_2_,
        user0_.age as age2_2_,
        user0_.gender as gender3_2_,
        user0_.name as name4_2_ 
    from
        t_user user0_ 
    order by
        user0_.age desc
Copy the code

@Test
public void test_sort2(a) throws Exception {
    List<User> list = userMapper.findByGender(Gender.MALE,
                                              Sort.by(Sort.Direction.ASC, "age"));
    list.forEach(System.out::println);
}
Copy the code

The generated SQL statement is:

Hibernate: 
    select
        user0_.id as id1_2_,
        user0_.age as age2_2_,
        user0_.gender as gender3_2_,
        user0_.name as name4_2_ 
    from
        t_user user0_ 
    where
        user0_.gender=? 
    order by
        user0_.age asc

Copy the code

@Test
public void test_sort3(a) throws Exception {
    List<User> list = userMapper.findFirst25ByGender(Gender.MALE,
                                                     Sort.by(Sort.Direction.ASC, "age"));
    list.forEach(System.out::println);
}
Copy the code

The generated SQL statement is:

Hibernate: 
    select
        user0_.id as id1_2_,
        user0_.age as age2_2_,
        user0_.gender as gender3_2_,
        user0_.name as name4_2_ 
    from
        t_user user0_ 
    where
        user0_.gender=? 
    order by
        user0_.age asc limit ?
Copy the code

The by method in Sort is declared to accept more than one Sort argument,

 public static Sort by(Direction direction, String... properties)
Copy the code

5, paging

Spring Data JPA uses paging in a similar way to sort; you simply add a parameter of type Pageable to the parameter list of the query method.

Father in the interface contains Pageable has set the good method of parameters, such as the PagingAndSortingRepository Page < T > the.findall (Pageable Pageable);

You can also add Pageable parameters to the parameters of your custom method in the interface:

package com.sxau.demojpa.mapper;

import com.sxau.demojpa.pojo.Gender;
import com.sxau.demojpa.pojo.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

/ * * *@ProjectName: demojpa
 * @Package: com.sxau.demojpa.dao
 * @ClassName: UserMapper
 * @Author: Shengrui Zhang *@Date: 2022/1/7 16:05
 * @Version: 1.0 * /
public interface UserMapper extends JpaRepository<User.Long>{
 
 Page<User> findByGender(Gender gender,Pageable pageable);
 
 Page<User> findFirst25ByGender(Gender gender, Pageable pageable);
 
 List<User> findTop25ByGender(Gender gender, Pageable pageable);
 
}
Copy the code

The return type of the method can be Page or List, but the return value of the Page type can be used to retrieve more page-specific information.

@Test
public void test_pageable1(a) throws Exception {
    int page = 0;// Current page count -- the first page counts from 0
    int size = 2;// Specify how many elements per page
    Pageable pageable = PageRequest.of(page, size);
    Page<User> pageObj = userMapper.findAll(pageable);
    System.out.println("Total pages: \t\t" + pageObj.getTotalPages());
    System.out.println("Total data: \t\t" + pageObj.getTotalElements());
    System.out.println("Current page number: \t\t" + pageObj.getNumber());
    System.out.println("Number of items per page: \t\t" + pageObj.getSize());
    System.out.println("Actual number of items on current page: \t\t" + pageObj.getNumberOfElements());
    System.out.println("Does the current page have data: \t\t" + pageObj.hasContent());
    System.out.println("Current page contents: \t\t" + pageObj.getContent());
    System.out.println("The sorting of paging queries is: \t\t" + pageObj.getSort());
    System.out.println("Is the current page 1: \t\t" + pageObj.isFirst());
    System.out.println("Is this the last page: \t\t" + pageObj.isLast());
    System.out.println("Is there currently a previous page: \t\t" + pageObj.hasPrevious());
    System.out.println("Is there currently a next page: \t\t" + pageObj.hasNext());
    System.out.println("Return previous Pageable object \t\t"+pageObj.previousPageable());
    System.out.println("Return next Pageable object \t\t"+pageObj.nextPageable());
}
Copy the code

The generated SQL statement is:

Hibernate: 
    select
        user0_.id as id1_2_,
        user0_.age as age2_2_,
        user0_.gender as gender3_2_,
        user0_.name as name4_2_ 
    from
        t_user user0_ limit ?
Hibernate: 
    select
        count(user0_.id) as col_0_0_ 
    from
        t_user user0_
Copy the code

If the return value type is a List rather than a Page type, you cannot get the page-specific data information directly.

6, annotations,

Spring Data JPA allows you to use the @Query annotation on custom methods in the interface, where you can use JPQL or SQL to specify what SQL language the method needs to execute when called.

JPQL (JavaPersistence Query Language) is an object-oriented Query Language that can be translated into SQL for execution in ORM framework. In the Hibernate framework, such statements are called HQL (Hibernate Query Language).

JPQL is an object-oriented query language. In the query statement, it does not appear the name of the table or the name of the field, but the name of the class and the name of the attribute in the class. Because in ORM framework, the class and the table, the attribute and the field are all mapped, so JPQL can finally transform SQL statements according to the mapping relationship.

JPQL features:

  • Table names, column names, Java class names, property names, and case sensitive
  • The keywords in the statement have the same meaning as those in the SQL statement and are case insensitive
  • You cannot write select *, but the alias of the select class, or the name of the select attribute
@Query("select u from User u where u.name = ?1")
User findByUserName(String name);
Copy the code

In this statement? 1 is the first parameter,? 2 represents the second parameter

You can also use Named Parameters with the @param annotation:

@Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
User findByLastnameOrFirstname(@Param("lastname") String lastname, @Param("firstname") String firstname);
Copy the code

In addition to JPQL, the @query annotation supports writing native SQL statements:

@Query(value="select * from t_user where name = ? 1",nativeQuery=true)
User findByUserNameByNative(String name);
Copy the code

The generated SQL statement is:

Hibernate: 
    select
        * 
    from
        t_user 
    where
        name = ?
Copy the code

The result is:

The @Query annotation also supports update and delete statements, but requires a combination of the @Modifying annotation and the @Transactional annotation for transaction management

The @Transactional annotation is already learned and used in Spring’s transaction management.

@Transactional
@Modifying
@Query("update User u set u.name = ?1 where u.id = ?2")
int updateNameByUserId(String name, Long id);
@Transactional(timeout = 10)
@Modifying
@Query("delete from User u where u.id = ? 1)"
void deleteByUserId(Long id);
Copy the code

7, associated

In addition to a single table operation, Spring Data JPA can also support multiple table cascading operations. However, this requires an association between a table and a table. At the same time, you need to map the association to the configuration in the entity class (object-relationl Mapping).

1 to 1 relationship: Assuming that a student corresponds to an address and an address corresponds to a student, the relationship between the student and the address is 1 to 1.

Note that in a 1-to-1 relationship, the foreign key column of the table in the database can be on either side.

Entity class Address

package com.sxau.demojpa.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

/ * * *@ProjectName: demojpa
 * @Package: com.sxau.demojpa.bean
 * @ClassName: Address
 * @Author: Shengrui Zhang *@Date: 2022/1/8 11:06
 * @Version: 1.0 * /
@Entity
@Table(name = "t_address")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    private String city;
}
Copy the code

The entity class Student

package com.sxau.demojpa.pojo;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;

/ * * *@ProjectName: demojpa
 * @Package: com.sxau.demojpa.bean
 * @ClassName: Student
 * @Author: Shengrui Zhang *@Date: 2022/1/8 11:09
 * @Version: 1.0 * /
@Entity
@Table(name = "t_student")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;

    private String name;
/ * * *@OneToOneCascade =CascadeType.ALL indicates that the cascading type between student and Address is ALL * *@JoinColumnThis annotation specifically specifies the name of the foreign key column * in this case, there will be a foreign key column address_id in the student table in the future, and this foreign key column will default to the primary key column (ID) in the Address table */
    @OneToOne(cascade=CascadeType.ALL)
    @JoinColumn(name = "address_id")
    private Address address;
}
Copy the code

StudentMapper file

package com.sxau.demojpa.mapper;

import com.sxau.demojpa.pojo.Student;
import org.springframework.data.jpa.repository.JpaRepository;

/ * * *@ProjectName: demojpa
 * @Package: com.sxau.demojpa.dao
 * @ClassName: StudentMapper
 * @Author: Shengrui Zhang *@Date: 2022/1/8 and *@Version: 1.0 * /
public interface StudentMapper extends JpaRepository<Student.Long> {}Copy the code

The test class

@Autowired
private StudentDao studentDao;
@Test
public void test_one2one(a) throws Exception {}Copy the code

After running the test method, you can see the table building sentences and build tables between the mapping information we configured

Create tables automatically if necessary
# create allows you to drop tables and then create tables after each test start
SQL > alter table create table create table create table create table create table
spring.jpa.hibernate.ddl-auto=update
Copy the code

Table construction sentences output from the console:

Hibernate: 
    
    create table t_address (
       id bigint not null auto_increment,
        city varchar(255),
        primary key (id)
    ) engine=InnoDB
Hibernate: 
    
    create table t_student (
       id bigint not null auto_increment,
        name varchar(255),
        address_id bigint.primary key (id)
    ) engine=InnoDB
Hibernate: 
    
    alter table t_student 
       add constraint FK3n8ay7rgq1krcs8bv8nd3k6pq 
       foreign key (address_id) 
       references t_address (id)
Copy the code

On this basis, cascade operation tests can be carried out:

Cascade save

@Autowired
private StudentDao studentDao;
@Test
public void test_one2one(a) throws Exception {
    Student student = new Student();
        Address address = new Address();
        student.setName("zsr");
        address.setCity("Shanxi");
        // The student object and the address object establish a relationship in memory, which will be mapped to the relationship between the two tables in the database
        student.setAddress(address);
        studentMapper.save(student);
}
Copy the code

The generated SQL statement is:

Hibernate: 
    insert 
    into
        t_address
        (city) 
    values
        (?)
Hibernate: 
    insert 
    into
        t_student
        (address_id, name) 
    values
        (?, ?)
Copy the code

Cascade query

    @Test
    public void test_one2one_select(a) throws Exception {
        Student stu = studentMapper.findById(1L).orElse(null);
        System.out.println(stu);
        System.out.println(stu.getAddress());
    }
Copy the code

Generated SQL statement:

Hibernate: 
    select
        student0_.id as id1_1_0_,
        student0_.address_id as address_3_1_0_,
        student0_.name as name2_1_0_,
        address1_.id as id1_0_1_,
        address1_.city as city2_0_1_ 
    from
        t_student student0_ 
    left outer join
        t_address address1_ 
            on student0_.address_id=address1_.id 
    where
        student0_.id=?
Copy the code

Results: Not only the query id is 1 students, but also the student associated address is also cascaded query.

Cascading deletion:

    @Test
    public void test_one2one_delete(a) throws Exception {
        Student stu = studentMapper.findById(1L).orElse(null);
        studentMapper.delete(stu);
    }
Copy the code

Generated SQL statement:

Hibernate: 
    select
        student0_.id as id1_1_0_,
        student0_.address_id as address_3_1_0_,
        student0_.name as name2_1_0_,
        address1_.id as id1_0_1_,
        address1_.city as city2_0_1_ 
    from
        t_student student0_ 
    left outer join
        t_address address1_ 
            on student0_.address_id=address1_.id 
    where
        student0_.id=?
Hibernate: 
    select
        student0_.id as id1_1_0_,
        student0_.address_id as address_3_1_0_,
        student0_.name as name2_1_0_,
        address1_.id as id1_0_1_,
        address1_.city as city2_0_1_ 
    from
        t_student student0_ 
    left outer join
        t_address address1_ 
            on student0_.address_id=address1_.id 
    where
        student0_.id=?
Hibernate: 
    delete 
    from
        t_student 
    where
        id=?
Hibernate: 
    delete 
    from
        t_address 
    where
        id=?
Copy the code

During cascading deletion, you need to query data once to ensure that data exists and has a corresponding relationship.

1 versus N

To illustrate this vividly, suppose that a teacher has several cars, and one car belongs to only one teacher, then the relationship between the teacher and the car is 1 to N.

Note that in a 1-pair N relationship, the foreign key column in the database table must be set on the side of N, otherwise there will be data redundancy.

Entity class the Teacher

package com.sxau.demojpa.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

/ * * *@ProjectName: demojpa
 * @Package: com.sxau.demojpa.pojo
 * @ClassName: Teacher
 * @Author: Shengrui Zhang *@Date: 2022/1/14 13:09
 * @Version: 1.0 * /
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "t_teacher")
public class Teacher {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;

    private String name;

    private Double salary;

    / * * *@OneToManyMappedBy ="teacher" the foreign key column of a many-pair relationship between us * is maintained by the teacher attribute class mapping of the other person
    @OneToMany(mappedBy="teacher",cascade=CascadeType.ALL,fetch = FetchType.EAGER)
    private List<Car> cars = new ArrayList<>();

}

Copy the code

Entity class Car

package com.sxau.demojpa.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

/ * * *@ProjectName: demojpa
 * @Package: com.sxau.demojpa.pojo
 * @ClassName: Car
 * @Author: Shengrui Zhang *@Date: 2022/1/14 "*@Version: 1.0 * /
@Entity
@Table(name = "t_car")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {

    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;

    private String type;

    private Double price;

    / * * *@ManyToOneThe relationship between car and teacher is many-to-one *@JoinColumnIndicates that the current property is a foreign key column property and specifies the name of the foreign key column */
    @ManyToOne
    @JoinColumn(name="teacher_id")
    private Teacher teacher;
}

Copy the code

TeacherMapper.java

package com.sxau.demojpa.mapper;

import com.sxau.demojpa.pojo.Student;
import com.sxau.demojpa.pojo.Teacher;
import org.springframework.data.jpa.repository.JpaRepository;

/ * * *@ProjectName: demojpa
 * @Package: com.sxau.demojpa.mapper
 * @ClassName: TeacherMapper
 * @Author: Shengrui Zhang *@Date: 2022/1/14 "*@Version: 1.0 * /
public interface TeacherMapper extends JpaRepository<Teacher.Long> {}Copy the code

CarMapper.java

package com.sxau.demojpa.mapper;

import com.sxau.demojpa.pojo.Car;
import com.sxau.demojpa.pojo.Student;
import org.springframework.data.jpa.repository.JpaRepository;

/ * * *@ProjectName: demojpa
 * @Package: com.sxau.demojpa.mapper
 * @ClassName: CarMapper
 * @Author: Shengrui Zhang *@Date: 2022/1/14 *@Version: 1.0 * /
public interface CarMapper extends JpaRepository<Car.Long> {}Copy the code

The test class:

Cascade save

The construction sentence output from the console is:

Hibernate: 
    
    create table t_car (
       id bigint not null auto_increment,
        price double precision,
        type varchar(255),
        teacher_id bigint.primary key (id)
    ) engine=InnoDB
Hibernate: 
    
    create table t_teacher (
       id bigint not null auto_increment,
        name varchar(255),
        salary double precision.primary key (id)
    ) engine=InnoDB
Hibernate: 
    
    alter table t_car 
       add constraint FKsi8is87l0wb3a31t0hs25k2p3 
       foreign key (teacher_id) 
       references t_teacher (id)
Copy the code

The generated SQL statement is:

Hibernate: 
    insert 
    into
        t_teacher
        (name, salary) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        t_car
        (price, teacher_id, type) 
    values
        (?, ?, ?)
Hibernate: 
    insert 
    into
        t_car
        (price, teacher_id, type) 
    values
        (?, ?, ?)
Copy the code
Cascade query

Generated SQL statement:

Hibernate: 
    select
        teacher0_.id as id1_3_0_,
        teacher0_.name as name2_3_0_,
        teacher0_.salary as salary3_3_0_,
        cars1_.teacher_id as teacher_4_1_1_,
        cars1_.id as id1_1_1_,
        cars1_.id as id1_1_2_,
        cars1_.price as price2_1_2_,
        cars1_.teacher_id as teacher_4_1_2_,
        cars1_.type as type3_1_2_ 
    from
        t_teacher teacher0_ 
    left outer join
        t_car cars1_ 
            on teacher0_.id=cars1_.teacher_id 
    where
        teacher0_.id=?
Copy the code

After running the test example, the development exploded, ha ha ha, this is in my expectation, let’s be clear, because in the JPA automatic query process, involves the linked table will do the query operation repeatedly, at this time we need to declare a down to end the loop query. Causing our stack to overflow.

Change @data in our entity class Teacher and entity class Car

@Setter
@Getter
@EqualsAndHashCode// Automatically generate get, set, toString, equals methods
Copy the code

We can solve our development problem

@onetomany (mappedBy=”teacher”,cascade= cascadetype. ALL,fetch = fetchtype. EAGER) Indicates that the current association is real-time loading.

A. EAGER B. EAGER C. Desire; Eager, here means that when querying Teacher data, it will immediately issue SQL query to the corresponding Car

@onetomany (mappedBy=”teacher”,cascade= cascadetype. ALL,fetch = fetchtype. LAZY) Indicates that the current association is lazy loading.

LAZY c. Lazy and delayed, which means that when querying Teacher data, SQL will not be issued immediately to query the corresponding Car, but will not be issued until the private List cars property is really used to query data.

Note that when lazy loading is used, we get a proxy object by default, and only when this object is actually used will WE issue an SQL query for the data in the database. You can use the getClass method to see what the actual runtime class type of this proxy object is.

Modify fetch = fetchtype. LAZY, and then execute the corresponding test method.

In the entity class Teacher

@OneToMany(mappedBy="teacher",cascade=CascadeType.ALL,fetch =
FetchType.LAZY)
private List<Car> cars = new ArrayList<>();
Copy the code

The test class:

@Test
public void test_one2many_select(a) throws Exception{
    Teacher t = teacherMapper.findById(1L).orElse(null);
    System.out.println(t);
    System.out.println(t.getCars());
}
Copy the code

Don’t worry guys, it’s still not expected.

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.sxau.demojpa.pojo.Teacher.cars, could not initialize proxy - no Session
Copy the code

Could not initialize proxy-no Session

The reason: As lazy loading is set, the Car object associated with the query of Teacher is a proxy object, and when we use this proxy object (the 6th line of the code above, that is to say, the code does not report errors after the 6th line is commented out), we need to issue SQL statements to query the real data. The findById method is disabled by default when the current JPA operation (findById) is completed.

The Transactional annotation ona test method ensures that the test method is transactionally managed until the session runs out of findById.

Or the entity class has so many associations that it can be queried indefinitely. JsonIgnoreProperties = JsonIgnoreProperties = JsonIgnoreProperties = JsonIgnoreProperties = JsonIgnoreProperties = JsonIgnoreProperties

The generated SQL statement and execution result are:

Hibernate: 
    select
        teacher0_.id as id1_3_0_,
        teacher0_.name as name2_3_0_,
        teacher0_.salary as salary3_3_0_ 
    from
        t_teacher teacher0_ 
    where
        teacher0_.id=?
com.sxau.demojpa.pojo.Teacher@12d8157e
Hibernate: 
    select
        cars0_.teacher_id as teacher_4_1_0_,
        cars0_.id as id1_1_0_,
        cars0_.id as id1_1_1_,
        cars0_.price as price2_1_1_,
        cars0_.teacher_id as teacher_4_1_1_,
        cars0_.type as type3_1_1_ 
    from
        t_car cars0_ 
    where
        cars0_.teacher_id=?
[com.sxau.demojpa.pojo.Car@e5ca7496, com.sxau.demojpa.pojo.Car@3fb8aaaa]
Copy the code

From the result, it can be seen that at the beginning, only a SQL statement was sent to query the Teacher object, and then another SQL statement was sent to query the Car when Car was really used later. Finally, two Car objects associated with Teacher are also queried.

Cascade deletion
@Test
public void test_one2many_delete(a) throws Exception{
    Teacher t = teacherMapper.findById(1L).orElse(null);
    teacherMapper.delete(t);
}
Copy the code

Generated SQL statement:

Hibernate: 
    select
        teacher0_.id as id1_3_0_,
        teacher0_.name as name2_3_0_,
        teacher0_.salary as salary3_3_0_ 
    from
        t_teacher teacher0_ 
    where
        teacher0_.id=?
Hibernate: 
    select
        teacher0_.id as id1_3_0_,
        teacher0_.name as name2_3_0_,
        teacher0_.salary as salary3_3_0_ 
    from
        t_teacher teacher0_ 
    where
        teacher0_.id=?
Hibernate: 
    select
        cars0_.teacher_id as teacher_4_1_0_,
        cars0_.id as id1_1_0_,
        cars0_.id as id1_1_1_,
        cars0_.price as price2_1_1_,
        cars0_.teacher_id as teacher_4_1_1_,
        cars0_.type as type3_1_1_ 
    from
        t_car cars0_ 
    where
        cars0_.teacher_id=?
Hibernate: 
    delete 
    from
        t_car 
    where
        id=?
Hibernate: 
    delete 
    from
        t_car 
    where
        id=?
Hibernate: 
    delete 
    from
        t_teacher 
    where
        id=?
Copy the code

It can be seen from the result that when the teacher’s data is deleted, the car data associated with the teacher is also deleted in cascade.

N on N

Similarly, if a player can play more than one game, and a game can have more than one player, the relationship between the player and the game is N to N.

Note that in a 1-on-N relationship, there needs to be a third bridge table in the database to associate the other two tables.

Entity class Player

package com.sxau.demojpa.pojo;

import lombok.*;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

/ * * *@ProjectName: demojpa
 * @Package: com.sxau.demojpa.pojo
 * @ClassName: Player
 * @Author: Shengrui Zhang *@Date: 2022/1/14 * to him@Version: 1.0 * /
@Entity
@Table(name = "t_player")
@Setter
@Getter
@EqualsAndHashCode// Automatically generate get, set, toString, equals methods
@AllArgsConstructor
@NoArgsConstructor
public class Player {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    /** * Many-to-many, generally do not set any cascade operation. (Save, update, delete) * Cascade query default behavior, do not set will also have. * *@ManyToManyRepresents a many-to-many relationship between the current class (Player) and the other party (Game) *@JoinTableName ="player_game" indicates that the name of the bridge table is player_game * joinColumns=@JoinColumnInverseJoinColumns = (name = "player_id") * indicates that player_id is a foreign key class and references the primary key of Play * inverseJoinColumns=@JoinColumn(name = "game_id") * Inverse is the word for each other here. * indicates that game_id in the bridge table is a foreign key column and refers to the primary key */ of the other party (Game)
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable( name = "player_game", joinColumns = @JoinColumn(name = "player_id"), inverseJoinColumns = @JoinColumn(name = "game_id") )
    private List<Game> gameList = new ArrayList<>();
}
Copy the code

Entity class Game

package com.sxau.demojpa.pojo;

import lombok.*;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

/ * * *@ProjectName: demojpa
 * @Package: com.sxau.demojpa.pojo
 * @ClassName: Game
 * @Author: Shengrui Zhang *@Date: 2022/1/14 all *@Version: 1.0 * /
@Entity
@Table(name = "t_game")
@Setter
@Getter
@EqualsAndHashCode// Automatically generate get, set, toString, equals methods
@AllArgsConstructor
@NoArgsConstructor
public class Game {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    /** * mappedBy property, which must be present in bidirectional association * 

* mappedBy indicates that the foreign key configuration/maintenance is handled by the other party. *

* mappedBy="gameList" Means that the relationship between Game and Player is maintained * by a gameList property. * is actually configured/maintained by annotations on a property (gameList) in the other (Player)

@ManyToMany(mappedBy = "gameList") private List<Player> playerList = new ArrayList<>(); } Copy the code

PlayerMapper.java

package com.sxau.demojpa.mapper;

import com.sxau.demojpa.pojo.Player;
import org.springframework.data.jpa.repository.JpaRepository;

/ * * *@ProjectName: demojpa
 * @Package: com.sxau.demojpa.mapper
 * @ClassName: PlayMapper
 * @Author: Shengrui Zhang *@Date: 2022/1/14 all *@Version: 1.0 * /
public interface PlayerMapper extends JpaRepository<Player.Long> {}Copy the code

GameMapper.java

package com.sxau.demojpa.mapper;

import com.sxau.demojpa.pojo.Game;
import org.springframework.data.jpa.repository.JpaRepository;

/ * * *@ProjectName: demojpa
 * @Package: com.sxau.demojpa.mapper
 * @ClassName: GameMapper
 * @Author: Shengrui Zhang *@Date: 2022/1/14 in *@Version: 1.0 * /
public interface GameMapper extends JpaRepository<Game.Long> {}Copy the code

Entity class:

Note that cascading save/delete is not currently configured

	@Autowired
    private PlayerMapper playerMapper;

    @Autowired
    private GameMapper gameMapper;

@Test
    public void test_many2many_insert(a) {
        Player p1 = new Player();
        p1.setName("tom1");
        Player p2 = new Player();
        p2.setName("tom2");
        Game g1 = new Game();
        g1.setName("Basketball Madness");
        Game g2 = new Game();
        g2.setName(Madden football);
        Game g3 = new Game();
        g3.setName("Volleyball Madness");
        List<Game> gameList1 = new ArrayList<>();
        gameList1.add(g1);
        gameList1.add(g2);
        gameList1.add(g3);
        List<Game> gameList2 = new ArrayList<>();
        gameList2.add(g1);
        gameList2.add(g3);
        // Establish an association between objects
        p1.setGameList(gameList1);
        p2.setGameList(gameList2);
        // Remember to save the game object's data first
        // Then save the data of the player object, since it is the game that the player is associated with
        gameMapper.save(g1);
        gameMapper.save(g2);
        gameMapper.save(g3);
        playerMapper.save(p1);
        playerMapper.save(p2);
    }
Copy the code

In this case, be careful to save the game first, and then the Player, because this is the game that the player is associated with

The construction sentence output from the console is:

Hibernate: 
    
    create table player_game (
       player_id bigint not null,
        game_id bigint not null
    ) engine=InnoDB
Hibernate: 
    
    create table t_game (
       id bigint not null auto_increment,
        name varchar(255),
        primary key (id)
    ) engine=InnoDB
Hibernate: 
    
    create table t_player (
       id bigint not null auto_increment,
        name varchar(255),
        primary key (id)
    ) engine=InnoDB
Hibernate: 
    
    alter table player_game 
       add constraint FKijjwgqv8y2mdiwxhk2i56e4jy 
       foreign key (game_id) 
       references t_game (id)
Hibernate: 
    
    alter table player_game 
       add constraint FKpc16kqwtroqa8jcwu91f5204p 
       foreign key (player_id) 
       references t_player (id)
Copy the code

The generated SQL statement is:

Hibernate: 
    insert 
    into
        t_game
        (name) 
    values
        (?)
Hibernate: 
    insert 
    into
        t_game
        (name) 
    values
        (?)
Hibernate: 
    insert 
    into
        t_game
        (name) 
    values
        (?)
Hibernate: 
    insert 
    into
        t_player
        (name) 
    values
        (?)
Hibernate: 
    insert 
    into
        player_game
        (player_id, game_id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        player_game
        (player_id, game_id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        player_game
        (player_id, game_id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        t_player
        (name) 
    values
        (?)
Hibernate: 
    insert 
    into
        player_game
        (player_id, game_id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        player_game
        (player_id, game_id) 
    values
        (?, ?)
Copy the code

You can observe the meaning of these SQL statements by executing properties based on how the code is written.

The final execution result in the database table: