Spring Data Jpa: use and source code analysis of Spring Data Jpa

Posted by ted_chou12 on Tue, 03 Mar 2020 09:33:05 +0100

Spring Data Jpa (2): use and source code analysis of Spring Data Jpa

1, Overview of Spring Data JPA

1.1 Spring Data JPA

Spring Data JPA is a set of spring encapsulation based on ORM and JPA specification, which enables developers to access and operate the database with simple code. It provides common functions including addition, deletion, modification and query, and is easy to expand! Learning and using Spring Data JPA can greatly improve development efficiency!

Spring Data JPA frees us from the operation of DAO layer, basically all CRUD can be realized by relying on it. In the actual work project, it is recommended to use Spring Data JPA + ORM (such as hibernate) to complete the operation, which provides great convenience when switching different ORM frameworks, and also makes the operation of database layer simpler and easier to decouple.

SpringData Jpa greatly simplifies the database access layer code. How to simplify it? With spring datajpa, we only need to write the interface in the dao layer, which automatically has the methods of adding, deleting, modifying and querying, paging and so on.

1.2 relationship between Spring Data JPA, JPA and hibernate

JPA is a set of specifications, which is composed of interfaces and abstract classes. Hibernate is a mature ORM framework, and Hibernate implements the JPA specification, so we can also call hibernate an implementation of JPA. We use the API programming of JPA, which means we look at the problem from a higher perspective (interface oriented programming)

Spring Data JPA is a set of more advanced encapsulation of JPA operations provided by spring. It is a solution specially used for data persistence under the JPA specification. (with another layer of packaging)

2, Getting started

Spring Data JPA completes basic CRUD operations of customers

To use Spring Data JPA, you need to integrate spring and Spring Data JPA, and provide hibernate, the service provider of JPA, so you need to import spring related coordinates, hibernate coordinates, database driven coordinates, etc

  <properties>
        <spring.version>4.2.4.RELEASE</spring.version>
        <hibernate.version>5.0.7.Final</hibernate.version>
        <slf4j.version>1.6.6</slf4j.version>
        <log4j.version>1.2.12</log4j.version>
        <c3p0.version>0.9.1.2</c3p0.version>
        <mysql.version>5.1.6</mysql.version>
    </properties>

    <dependencies>
        <!-- junit unit testing -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.9</version>
            <scope>test</scope>
        </dependency>
        
        <!-- spring beg -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.8</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        
        <!-- spring end -->

        <!-- hibernate beg -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.2.1.Final</version>
        </dependency>
        <!-- hibernate end -->

        <!-- c3p0 beg -->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>${c3p0.version}</version>
        </dependency>
        <!-- c3p0 end -->

        <!-- log end -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <!-- log end -->

        
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>1.9.0.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>
        
        <!-- el beg Use spring data jpa Must introduce -->
        <dependency>  
            <groupId>javax.el</groupId>  
            <artifactId>javax.el-api</artifactId>  
            <version>2.2.4</version>  
        </dependency>  
          
        <dependency>  
            <groupId>org.glassfish.web</groupId>  
            <artifactId>javax.el</artifactId>  
            <version>2.2.4</version>  
        </dependency> 
        <!-- el end -->
    </dependencies>

2.1 integrating Spring Data JPA and Spring

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/data/jpa 
		http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
	
	<!-- 1.dataSource Configure database connection pool-->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver" />
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/jpa" />
		<property name="user" value="root" />
		<property name="password" value="111111" />
	</bean>
	
	<!-- 2.To configure entityManagerFactory -->
	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="packagesToScan" value="cn.itcast.entity" />
		<property name="persistenceProvider">
			<bean class="org.hibernate.jpa.HibernatePersistenceProvider" />
		</property>
		<!--JPA Vendor adapter for-->
		<property name="jpaVendorAdapter">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
				<property name="generateDdl" value="false" />
				<property name="database" value="MYSQL" />
				<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
				<property name="showSql" value="true" />
			</bean>
		</property>
		<property name="jpaDialect">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
		</property>
	</bean>
    
	
	<!-- 3.Transaction manager-->
	<!-- JPA Transaction manager  -->
	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>
	
	<!-- integration spring data jpa-->
	<jpa:repositories base-package="cn.itcast.dao"
		transaction-manager-ref="transactionManager"
		entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
		
	<!-- 4.txAdvice-->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="save*" propagation="REQUIRED"/>
			<tx:method name="insert*" propagation="REQUIRED"/>
			<tx:method name="update*" propagation="REQUIRED"/>
			<tx:method name="delete*" propagation="REQUIRED"/>
			<tx:method name="get*" read-only="true"/>
			<tx:method name="find*" read-only="true"/>
			<tx:method name="*" propagation="REQUIRED"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- 5.aop Declarative transaction-->
	<aop:config>
		<aop:pointcut id="pointcut" expression="execution(* cn.itcast.service.*.*(..))" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
	</aop:config>
	
	<context:component-scan base-package="cn.itcast"></context:component-scan>
		
	<!--Assemble other configuration files-->
	
</beans>

2.2 use JPA annotation to configure mapping relationship

The Customer entity class object in our use case has been configured with mapping relationship

package cn.itcast.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * 
 *  * All annotations are provided by JPA specifications,
 *  * So when importing annotation packages, you must import the
 */
@Entity //Declaration entity class
@Table(name="cst_customer") //Establish the mapping relationship between entity class and table
@Data
public class Customer {
    
    @Id//Declare the current private property as the primary key
    @GeneratedValue(strategy=GenerationType.IDENTITY) //Configure primary key generation policy
    @Column(name="cust_id") //Specify the mapping relationship with cust? ID field in the table
    private Long custId;
    
    @Column(name="cust_name") //Specify the mapping relationship with cust'name field in the table
    private String custName;
    
    @Column(name="cust_source")//Specify the mapping relationship with cust source field in the table
    private String custSource;
    
    @Column(name="cust_industry")//Specify the mapping relationship with cust "industry field in the table
    private String custIndustry;
    
    @Column(name="cust_level")//Specify the mapping relationship with cust "level field in the table
    private String custLevel;
    
    @Column(name="cust_address")//Specify the mapping relationship with cust "address field in the table
    private String custAddress;
    
    @Column(name="cust_phone")//Specify the mapping relationship with cust? Phone field in the table
    private String custPhone;
    
  
}

2.3 use Spring Data JPA to complete requirements

2.3.1 write Dao layer interface conforming to Spring Data JPA specification

Spring Data JPA is a framework of data access layer (Dao layer) provided by spring. It can complete the definition of database addition, deletion, modification and query, paging query and other methods without implementation classes, greatly simplifying our development process.

In Spring Data JPA, we only need to follow the following points to define the Dao layer interface that meets the specification:

1. Create a Dao layer interface and implement JpaRepository and jpaspecification executor
 2. Provide corresponding generics
package cn.itcast.dao;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

import cn.itcast.entity.Customer;

/**
 * JpaRepository<Entity class type, primary key type >: used to complete basic CRUD operations
 * JpaSpecificationExecutor<Entity class type >: used for complex query (paging and other query operations)
 */
public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
}

In this way, we have defined a Dao layer interface that conforms to the Spring Data JPA specification

2.3.2 complete basic CRUD operation

After setting up the environment of Spring Data JPA and writing the Dao layer interface in line with the Spring Data JPA specification, you can use the defined Dao layer interface to perform basic CRUD operations for customers

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class CustomerDaoTest {

    @Autowired
    private CustomerDao customerDao;
    
    /**
     * Save customer: call save(obj) method
     */
    @Test
    public void testSave() {
        Customer c = new Customer();
        c.setCustName("xxx");
        customerDao.save(c);
    }
    
    /**
     * Modify customer: call save(obj) method
     * Explanation for the save method: if the method is executed because there is an id attribute in the object, the update operation will first query and update according to the id    
     * 
     * If the id attribute does not exist in the object in this method, it is a save operation
     *          
     */
    @Test
    public void testUpdate() {
        //Query the customer with id 1 according to id
        Customer customer = customerDao.findOne(1l);
        //Modify customer name
        customer.setCustName("Zhichuan podcast Shunyi Campus");
        //To update
        customerDao.save(customer);
    }
    
    /**
     * Delete according to id: call delete(id) method
     */
    @Test
    public void testDelete() {
        customerDao.delete(1l);
    }
    
    /**
     * Query according to id: call findOne(id) method
     */
    @Test
    public void testFindById() {
        Customer customer = customerDao.findOne(2l);
        System.out.println(customer);
    }
}

3, Source code analysis of Spring Data JPA

3.1 common interface analysis of Spring Data JPA

In the custom CustomerDao, many of these methods can be used without providing any methods, so how do these methods come from?

The answer is simple. For our custom Dao interface, we can use all the methods of the two interfaces because we inherit the JpaRepository and jpasecification executor. But these methods are just declarations, and there is no specific way to implement them. So how is it implemented in Spring Data JPA?

3.2 implementation process of Spring Data JPA

By analyzing the original Spring Data JPA to analyze the execution process of the program, we take findOne method as an example.

  1. Implementation process of agent subclass

    When the breakpoint is executed on the method, we can find that the injected customerDao object is essentially a proxy object generated by JdkDynamicAopProxy.
  2. Analysis of method call in proxy object

    When the program is executed, the dynamic proxy object is generated for the customerDao object through the invoke method of JdkDynamicAopProxy. If you want to perform findOne query method, the API of JPA specification will finally appear to complete the operation, where are these underlying codes? The answer is very simple. It is hidden in the dynamic proxy object generated by JdkDynamicAopProxy, which is simplejparpository.

Through the source code analysis of simplejpaepository, the findOne method is located. In this method, the return result of em.find() is returned. We find that EM is the EntityManager object, which is the native implementation of JPA.

Conclusion: Spring Data JPA only further encapsulates the standard JPA operations, simplifying the development of Dao layer code

3.3 complete call process analysis of Spring Data JPA

4, Query method of Spring Data JPA

4.1 query using the method defined by the interface in Spring Data JPA

After inheriting the JpaRepository and JpaRepository interfaces, we can use the methods defined in the interfaces to query

  1. List of methods inherited from JpaRepository
  2. List of methods (complex queries) that inherit the jpaspecification executor

4.2 Query by @ Query annotation JPQL

Using the Query method provided by Spring Data JPA can solve most of the application scenarios, but for some businesses, we need to flexibly construct the Query conditions. At this time, we can use @ Query annotation and JPQL statement to complete the Query.

Just annotate @ Query on the method and provide a JPQL Query statement.

public interface CustomerDao extends JpaRepository<Customer, Long>,JpaSpecificationExecutor<Customer> {    
    //@Query queries using jpql.
    @Query(value="from Customer")
    public List<Customer> findAllCustomer();
    
    //@Query queries using jpql. ? 1 represents a placeholder for the parameter, where 1 corresponds to the parameter index in the method
    @Query(value="from Customer where custName = ?1")
    public Customer findCustomer(String custName);
}

In addition, you can also use @ Query to perform an update operation. To do this, we need to use @ Query and @ Modifying to mark the operation as Modifying Query, so that the framework will eventually generate an update operation instead of a Query

@Query(value="update Customer set custName = ?1 where custId = ?2")
@Modifying//It shows that the method is modified
public void updateCustomer(String custName,Long custId);

4.3 query with SQL statement

@Query also supports query of sql statements, as follows:

/**
 * nativeQuery : Query using local sql
 */
@Query(value="select * from cst_customer",nativeQuery=true)
public void findSql();

4.4 method naming rule query

As the name implies, method naming rule query is to create a query based on the name of the method. You can complete the query by defining the name of the method according to the method naming rules provided by Spring Data JPA.

The query method starts with findBy. When it comes to condition query, the attributes of the condition are connected with the condition keyword. Note that the initial of the condition attribute should be capitalized. When the framework resolves method names, it will first truncate the redundant prefixes of method names, and then parse the rest.

//Method name method query (query customer by customer name)
public Customer findByCustName(String custName);

Specific keywords, usage and production into SQL are shown in the following table

Published 61 original articles, won praise 18, visited 6150
Private letter follow

Topics: Spring Hibernate MySQL Database