Many readers left comments hoping That Songo could talk about Spring Data Jpa! In fact, this topic has been introduced by Songko in the past, and also in my book, but we haven’t talked about it in the official account. Therefore, this article will discuss Spring Data and Jpa with you in detail.

The hero of the story

Jpa

1. What is JPA
  1. Java Persistence API: API for object Persistence
  2. The ORM specification of the Java EE 5.0 platform standard enables applications to access the persistence layer in a uniform manner

2. Relationship between JPA and Hibernate
  1. JPA is an abstraction of Hibernate (like JDBC and JDBC drivers);
  2. JPA is a specification: JPA is essentially an ORM specification, not an ORM framework. This is because JPA does not provide ORM implementation. It only makes some specifications and provides some programming APIS, but the specific implementation is provided by ORM vendors.
  3. Hibernate is implementation: In addition to being an ORM framework, Hibernate is also a JPA implementation
  4. Functionally, JPA is a subset of Hibernate’s capabilities
3. Suppliers of JPA

One of the goals of JPA is to develop an API that can be implemented by many vendors. Hibernate 3.2+, TopLink 10.1+, and OpenJPA all provide implementations of JPA. There are many JPA vendors.

  1. Hibernate

The originator of JPA is the author of Hibernate, which has been compatible with JPA since 3.2. OpenJPA is an open source project provided by the Apache organization. TopLink used to have a fee, but now it’s open source. 4. EclipseLink

4. Advantages of JPA
  1. Standardization: Providing the same API ensures that enterprise applications developed based on JPA can run under different JPA frameworks with minimal modifications.
  2. Ease of use and integration: One of the main goals of JPA is to provide a simpler programming model. Creating entities within the JPA framework is as simple as creating Java classes, with annotations using Javax.Persistence.Entity; JPA’s framework and interfaces are also very simple.
  3. Comparable to JDBC query capabilities: JPA’s query language is object-oriented, JPA defines a unique JPQL, and supports advanced query features such as batch updates and modifications, JOINS, GROUP BY, HAVING, and even sub-queries that are normally only available in SQL.
  4. Support for advanced object-oriented features: JPA supports advanced object-oriented features such as inheritance between classes, polymorphism, and complex relationships between classes to maximize the use of object-oriented models
5. Technologies included in JPA
  1. ORM Mapping metadata: JPA supports metadata in the form of XML and JDK 5.0 annotations. Metadata describes the mapping between objects and tables from which the framework persists entity objects to database tables.
  2. JPA’s API is used to manipulate entity objects, perform CRUD operations, and the framework does everything in the background, freeing developers from tedious JDBC and SQL code.
  3. Query Language (JPQL) : This is an important aspect of persistence operations, where data is queried in an object-oriented rather than database-oriented query language, avoiding tight coupling between programs and concrete SQL.

Spring Data

Spring Data is a subproject of Spring. Used to simplify database access and support NoSQL and relational data storage. Its main goal is to make database access easy and quick. Spring Data has the following characteristics:

  1. The SpringData project supports NoSQL storage: MongoDB (document database) Neo4j (graph database) Redis (key/value storage) Hbase (column family database)
  2. The relational data storage technology supported by the SpringData project: JDBC JPA
  3. Spring Data Jpa aims to reduce the amount of Data access layer (DAO) development. The only thing you need to do is declare the interface to the persistence layer, and let Spring Data JPA do the rest for you!
  4. How can a framework implement business logic on the developer’s behalf? For example, when you have a method declaration such as userdao.findUserById (), you should be able to tell that this is a query for a User object that meets the condition based on the given condition ID. What Spring Data JPA does is specify the name of the method and determine what logic the method needs to implement based on the name that conforms to the specification.

The story of the protagonist

The story of the Jpa

In order to get you all up to speed on these two things, I’m going to introduce you to pure Jpa usage, and then we’ll look at how to use Jpa in combination with Spring Data.

The overall steps are as follows:

  1. Create a project using IntelliJ IDEA and select JavaEE Persistence as follows:

  1. After the creation, add the dependent JAR. Since Jpa is only a specification, we say that using Jpa must actually use one of the implementations of Jpa, so which one? Hibernate, of course, so the added JAR is actually from Hibernate, as follows:

  1. Adding an Entity Class

Next add the entity class to the project as follows:

@Entity(name = "t_book")
public class Book {
    private Long id;
    private String name;
    private String author;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getId(a) {
        return id;
    }
    // omit other getters/setters
}
Copy the code

First, the @Entity annotation indicates that this is an Entity class, so a table is automatically generated for this class when the project starts. The default table name is the class name, and the name attribute of the @Entity annotation indicates the custom generated table name. The @ID annotation indicates that the field is an Id, and the @GeneratedValue annotation indicates the self-growth policy for the primary key. For other attributes in the class, the default is to generate fields in the table based on the attribute name. The field name is the same as the attribute name, and if you want to customize the field, you can use the @column annotation. To configure the name of the field, length, whether it is empty, and so on.

  1. Create the persistence.xml file

The JPA specification requires persistence. XML to be placed in the meta-INF directory of the classpath with a fixed file name


      
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <persistence-unit name="NewPersistenceUnit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <class>org.sang.Book</class>
        <properties>
            <property name="hibernate.connection.url"
                      value="jdbc:mysql:///jpa01? useUnicode=true&amp;characterEncoding=UTF-8"/>
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.connection.username" value="root"/>
            <property name="hibernate.connection.password" value="123"/>
            <property name="hibernate.archive.autodetection" value="class"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
        </properties>
    </persistence-unit>
</persistence>
Copy the code

Note:

  • The name property of the persistence-unit is used to define the name of the persistence unit. Mandatory.
  • Transaction-type: specifies the transaction policy of the JPA. RESOURCE_LOCAL: default value. A database-level transaction can be executed only for one database. Distributed transactions are not supported. If you need to support distributed transactions, use JTA: transaction-type=”JTA”
  • The class node represents an explicit listing of entity classes
  • The configuration in Properties is divided into two parts: database connection information and Hibernate information
  1. Perform persistent operations

EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("NewPersistenceUnit");
EntityManager manager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = manager.getTransaction();
transaction.begin();
Book book = new Book();
book.setAuthor("Luo Guanzhong");
book.setName(Romance of The Three Kingdoms);
manager.persist(book);
transaction.commit();
manager.close();
entityManagerFactory.close();
Copy the code

Create an EntityManagerFactory from the configuration file, then create an EntityManager from the EntityManagerFactory instance, and then start the transaction. Call the Persist method in EntityManager to perform a persistent operation and commit the transaction. After these operations, there will be an additional T_book table in the database with one data in the table.

About JPQL
  1. JPQL, short for Java Persistence Query Language. JPQL is an intermediate and objectified query language very similar to SQL, which is ultimately compiled into SQL queries for different underlying databases, thus masking differences between databases. JPQL statements can be SELECT statements, UPDATE statements, or DELETE statements, all of which are executed through the Query interface encapsulation.
  2. The Query interface encapsulates the relevant methods for executing database queries. The createQuery, Create NamedQuery and createNativeQuery methods of EntityManager can be used to obtain Query objects, and then the relevant methods of Query interface can be invoked to perform Query operations.
  3. The main methods of the Query interface are as follows:
  • int executeUpdate(); | used to perform the update or delete statement.
  • List getResultList(); | used to perform a select statement and return the result set entity list.
  • Object getSingleResult(); | to perform only returns a single result entities of a select statement.
  • Query setFirstResult(int startPosition); | is used to set from which entity records began returning to the query results.
  • Query setMaxResults(int maxResult); | for sets the maximum number of results returned entity. Used in conjunction with setFirstResult to implement paging queries.
  • Query setFlushMode(FlushModeType flushMode); | set Flush query object model. The parameter can take two enumerated values: flushmodetype. AUTO for automatically updating database records, and FlushMode type.mit for updating database records until the transaction is committed.
  • setHint(String hintName, Object value); | set specific vendor related to query object parameter or message. The parameter names and their values need to refer to the documentation of the specific JPA implementation library provider. If the second argument is invalid, an IllegalArgumentException is thrown.
  • setParameter(int position, Object value); | the parameter specified location for the query. Position specifies the parameter number, and value is the value assigned to the parameter.
  • setParameter(int position, Date d, TemporalType type); | for the query specified position parameters of the assigned Date value. Position specifies the parameter number, value is the value assigned to the parameter, temporalType takes the enumeration constant of temporalType, including DATE, TIME and TIMESTAMP, Use to temporarily convert Java date-type values to date-time types supported by the database (java.sql.Date, java.sql.Time, and java.sql.timestamp).
  • setParameter(int position, Calendar c, TemporalType type); | for the query specified position parameters of the fu Calenda r value. Position specifies the parameter number, value is the value assigned to the parameter, and temporalType is the same as temporalType.
  • setParameter(String name, Object value); | for the query parameter specified name.
  • setParameter(String name, Date d, TemporalType type); | for the query of the specified name gives parameters Date value, before usage.
  • setParameter(String name, Calendar c, TemporalType type); | value Calendar for the query specified name setting. Name is the parameter name. This method is called with an IllegalArgumentException if the argument location or argument name is incorrect, or if the assigned argument value type does not match.
JPQL, for example,

As in SQL, select statements in JPQL are used to execute queries. The syntax is as follows: select_clause form_clause [where_clause] [groupby_clause] [having_clause] [orderby_clause]

Among them:

  1. The FROM clause is a mandatory clause of the query statement.
  2. Select is used to specify the result entity or some attributes of the entity returned by the query.
  3. The FROM clause declares the query source entity class and specifies an identifier variable (equivalent to an alias for the SQL table).
  4. If you do not want to return duplicate entities, you can use the keyword distinct modifier. Select and FROM are JPQL keywords, which are all lowercase or uppercase. You are advised not to use case mixing.

In JPQL, a JPQL query statement for all entities is simple as follows: Select o from Order O or select O from Order as o where the keyword as can be omitted. Call EntityManager createQuery() to create a Query object, and then call getResultList() to get the Query result set as follows:

Query query = entityManager.createQuery( "select o from Order o"); 
List orders = query.getResultList();
Iterator iterator = orders.iterator();
while(iterator.hasNext() ) {
  / / handle the Order
}
Copy the code

Other methods are similar and will not be described here.

Spring Data story

In Spring Boot, Spring Data Jpa is an official package of too many things, resulting in many people do not know how to configure the bottom layer of Spring Data Jpa. This article and everyone to see how to configure Spring Data Jpa in the manual environment. The usage is the same as in Spring Boot.

Basic Environment construction

Start by creating a normal Maven project and adding the following dependencies:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>5.0.2. RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-oxm</artifactId>
        <version>5.0.2. RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.0.2. RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.0.2. RELEASE</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.27</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>5.0.2. RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>5.0.2. RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>5.2.12. The Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-jpamodelgen</artifactId>
        <version>5.2.12. The Final</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.0.29</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-jpa</artifactId>
        <version>1.11.3. RELEASE</version>
    </dependency>
</dependencies>
Copy the code

In addition to Jpa dependencies, Spring Data Jpa dependencies.

Next, create a User entity class in the same way that Jpa creates an entity class, which I won’t describe here.

Next create an applicationContext. XML file in the Resources directory and configure Spring and Jpa as follows:

<context:property-placeholder location="classpath:db.properties"/>
<context:component-scan base-package="org.sang"/>
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
    <property name="driverClassName" value="${db.driver}"/>
    <property name="url" value="${db.url}"/>
    <property name="username" value="${db.username}"/>
    <property name="password" value="${db.password}"/>
</bean>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
    <property name="dataSource" ref="dataSource"/>
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
    </property>
    <property name="packagesToScan" value="org.sang.model"/>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.format_sql">true</prop>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL57Dialect</prop>
        </props>
    </property>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<! -- Configure jPA -->
<jpa:repositories base-package="org.sang.dao"
                  entity-manager-factory-ref="entityManagerFactory"/>

Copy the code

The entityManagerFactory, the Jpa transaction, and the dao location can be configured to create a Repository in the org.sang.dao package.

public interface UserDao extends Repository<User.Long> {
    User getUserById(Long id);
}
Copy the code

GetUserById means to query the User object by ID. As long as the name of our method conforms to a similar specification, there is no need to write SQL, which will be discussed later. Ok, next, create Service and Controller to call this method as follows:

@Service
@Transactional
public class UserService {
    @Resource
    UserDao userDao;

    public User getUserById(Long id) {
        returnuserDao.getUserById(id); }}public void test1(a) {
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService userService = ctx.getBean(UserService.class);
    User user = userService.getUserById(1L);
    System.out.println(user);
}
Copy the code

In this case, user 1 can be queried.

Repository

The Repository interface is implemented by the UserDao.

Let’s start with an inheritance diagram for Repository:

As you can see, there are quite a few implementation classes. So what exactly is Repository?

  1. The Repository interface is a core interface of Spring Data. It does not provide any methods, and developers need to declare the required methods in their own interfacespublic interface Repository<T, ID extends Serializable> { }
  2. If the interface we define inherits Repository, it will be recognized as a Repository Bean by the IOC container and incorporated into the IOC container so that methods that meet certain specifications can be defined in the interface.
  3. Spring Data allows us to define only interfaces, and there is no need to write implementation classes as long as we follow the Spring Data specification.
  4. An equivalent of inheriting Repository is to use the @RepositoryDefinition annotation on the persistence layer interface and specify the domainClass and idClass properties for it. Something like this:
@RepositoryDefinition(domainClass = User.class, idClass = Long.class)
public interface UserDao
{
    User findById(Long id);
    List<User> findAll(a);
}
Copy the code

The basic Repository provides the most basic data access functionality, and its subinterfaces extend that functionality. Common implementation classes are as follows:

  • CrudRepository: Inherits Repository and implements a set of CRUD-related methods
  • CrudRepository PagingAndSortingRepository: inheritance, implements a set of paging sorting method
  • PagingAndSortingRepository JpaRepository: inheritance, implement a group of the JPA specification related methods
  • The custom XxxxRepository inherits JpaRepository, so that the XxxxRepository interface has the capabilities of a common data access control layer.
  • JpaSpecificationExecutor: Implements a set of methods associated with JPA Criteria queries that are not part of the Repository architecture
Method definition specification
1. Query simple conditions
  • According to the standard of the Spring Data, query methods to find | read | get start
  • When a conditional query is involved, the attributes of a condition are joined by a conditional keyword. Note that the condition attributes are capitalized

For example: define an Entity class:

class User{private String firstName; 
   privateString lastName; }Copy the code

When using And conditional connection, the attribute name And number of the condition should correspond to the position And number of the parameter as follows:

findByLastNameAndFirstName(String lastName,String firstName);
Copy the code
  • Cascading query of attributes is supported. If the current class has attributes that match the criteria, they are used in preference to cascading attributes. If cascading properties are required, they are connected using _.

Query examples:

  1. Query by ID
User getUserById(Long id);
User getById(Long id);
Copy the code
  1. Query all persons younger than 90 years old
List<User> findByAgeLessThan(Long age);
Copy the code
  1. Query all persons named zhao
List<User> findByUsernameStartingWith(String u);
Copy the code
  1. Select * from ‘zhao’ where id > 50
List<User> findByUsernameStartingWithAndIdGreaterThan(String name, Long id);
Copy the code
  1. Query all people whose names contain the word “up”
List<User> findByUsernameContaining(String name);
Copy the code
  1. Select * from all users whose surname is Zhao or whose age is greater than 90
List<User> findByUsernameStartingWithOrAgeGreaterThan(String name, Long age);
Copy the code
  1. Example Query all users whose role is 1
List<User> findByRole_Id(Long id);
Copy the code
2. Supported keywords

The following figure shows the supported query keywords:

3. Query method flow analysis

Why write the method name so JPA knows what you want to do? The framework parses findByUserDepUuid() by first excluding findBy and then parsing the remaining attributes, assuming the query entity is Doc:

  1. Determine whether userDepUuid (lowercase according to the POJO specification) is an attribute of the query entity, and if so, it indicates that the query is based on that attribute. If not, proceed to step 2;
  2. Truncate the string beginning with the first uppercase letter from right to left (Uuid in this case) and then check whether the remaining string is an attribute of the query entity, and if so, indicate that the query is based on that attribute; If not, repeat the second step and continue to intercept from right to left. Finally, assume that user is an attribute of the query entity.
  3. DepUuid specifies whether the type of user has DepUuid. If it does, the query is based on the value of doc.user. DepUuid. Otherwise, query data from right to left based on the value of doc.user.dep. uuid.
  4. There may be a special case where Doc contains a user attribute as well as a userDep attribute, where there is confusion. You can explicitly place “_” between attributes to express intent, such as “findByUser_DepUuid()” or “findByUserDep_uuid()”
  5. There are also some special parameters: for example, paging or sorting parameters:
Page<UserModel> findByName(String name, Pageable pageable);  
List<UserModel> findByName(String name, Sort sort);
Copy the code
@ Query annotation

The @query keyword can be used to customize the SQL Query, for example, to Query the User with the largest Id:

@Query("select u from t_user u where id=(select max(id) from t_user)")
User getMaxIdUser(a);
Copy the code

If the query has parameters, they can be passed in two different ways,

  1. Use the subscript index to pass the parameter, the index parameter is as follows, the index value starts from 1, query “? The number of X “must be the same as the number of parameters defined by the method, and the order must be the same:
@Query("select u from t_user u where id>? 1 and username like ? 2)"
List<User> selectUserByParam(Long id, String name);
Copy the code
  1. Named parameters (recommended) : This way you can define the parameter name and assign it to @param (” parameter name “) regardless of the order:
@Query("select u from t_user u where id>:id and username like :name")
List<User> selectUserByParam2(@Param("name") String name, @Param("id") Long id);
Copy the code

You can also use native SQL queries as follows:

@Query(value = "select * from t_user",nativeQuery = true)
List<User> selectAll(a);
Copy the code
@ Modifying annotations

When it comes to data modification operations, the @pipelineannotation, @query, and @pipelineannotation can be declared together to define personalized update operations. For example, they are most commonly used when updating certain fields, as shown in the following example:

@Modifying
@Query("update t_user set age=:age where id>:id")
int updateUserById(@Param("age") Long age, @Param("id") Long id);
Copy the code

Note:

  1. UPDATE and DELETE operations can be done using custom JPQL. Note: JPQL does not support the use of INSERTS
  2. The return value of the method should be int, indicating the number of rows affected by the update statement
  3. A transaction must be added at the place of the invocation, without which it cannot be executed
  4. By default, Spring Data has a transaction on each method, but it is a read-only transaction. They could not complete the modification operation

Speaking of which, let’s take a look at transactions in Spring Data:

  1. Spring Data provides a default transaction where all queries are declared read-only transactions.
  2. For custom methods that need to change the Transactional default provided by Spring Data, you can add the @Transactional annotation to the method.
  3. Multiple Repository operations should also be processed in the same transaction. According to the idea of hierarchical architecture, this part belongs to the business logic layer. Therefore, we need to implement multiple Repository calls in the Service layer and declare transactions on the corresponding methods.