Spring Data JPA介紹與Spring的整合

一、JPA 、Hibernate、Spring Data JPA與Spring Data簡介

    Spring Data JPA官網介紹:https://spring.io/projects/spring-data-jpa​​​​​​​

1、什麼是JPA?

JPA(Java Persistence API)是Sun官方提出的Java持久化API規範。可以通過JDK 5.0註解或者XML描述【對象-關係表】之間的映射關係,並將實體對象持久化到數據庫中。主要是爲了簡化現有的持久化開發工作和整合ORM技術,使得應用程序以統一的方式訪問持久層。

2、什麼是Hibernate?

Hibernate 是一個開源的全自動的ORM框架,它對JDBC進行了非常輕量級的對象封裝,它將POJO與數據庫表建立映射關係,Hibernate 是一個實現了 ORM 思想的框架,封裝了 JDBC。 

3、什麼是Spring Data JPA?

Spring Data JPA是Spring提供的一套簡化JPA開發的框架,它是在JPA規範下提供了Repository層的實現,按照約定好的【方法命名規則】寫dao層接口,就可以在不寫接口實現的情況下,實現對數據庫的訪問和操作。同時提供了很多除了CRUD之外的功能,如分頁、排序、複雜查詢等等。

Spring Data JPA的功能實現默認使用的Hibernate,也可以使用其他持久層框架。

4、什麼是Spring Data?

Spring Data項目是爲了簡化構建基於Spring框架應用的數據訪問技術,是Spring官方提供的一套數據層的綜合解決方案。(或者可以封裝其他的持久層解決方案的一個解決方案)。它支持關係型數據庫、非關係型數據庫、Map-Reduce框架、雲數據服務等。 

Spring Data 包含很多個子模塊:

  • Commons - 提供共享的基礎框架,適合各個子項目使用,支持跨數據庫持久化
  • Hadoop - 基於 Spring 的 Hadoop 作業配置和一個 POJO 編程模型的 MapReduce 作業
  • Key-Value - 集成了 Redis 和 Riak ,提供多個常用場景下的簡單封裝
  • Document - 集成文檔數據庫:CouchDB 和 MongoDB 並提供基本的配置映射和資料庫支持
  • Graph - 集成 Neo4j 提供強大的基於 POJO 的編程模型
  • Graph Roo AddOn - Roo support for Neo4j
  • JDBC Extensions - 支持 Oracle RAD、高級隊列和高級數據類型
  • JPA - 簡化創建 JPA 數據訪問層和跨存儲的持久層功能
  • Mapping - 基於 Grails 的提供對象映射框架,支持不同的數據庫
  • Examples - 示例程序、文檔和圖數據庫
  • Guidance - 高級文檔

    

 Spring Data JPA是Spring Data的一個模塊。

5、它們之間的關係

JPA是一套ORM規範接口,接口是需要實現才能工作。而 Hibernate就是實現了JPA接口的ORM框架。

Spring Data JPA 可以理解爲 JPA 規範的再次封裝抽象,底層默認還是使用了 Hibernate 對JPA 技術實現。

Spring Data JPA是Spring Data的一個模塊。

按照時間線來說就明白了

  • 開發 Hibernate 的團隊開發了 Hibernate
  • 制訂 J2ee 規範的團隊邀請 Hibernate 的核心在 Hibernate 基礎上制訂了 JPA (Java Persistent API)標準。從功能上看,JPA 是 Hibernate 的子集。
  • Spring 的團隊使用 Spring 對 JPA 做了封裝,就是 Spring Data JPA 了。

總之,JPA 是一個 API 標準,除了 Hibernate 外,還有其它廠商的實現,例如 Eclipse 的 TopLink。Spring Data Jpa 是個對 JPA 的封裝,幫助程序員以 Spring 的方式來使用 JPA。

6、一些名詞含義

ORM(Object Relational Mapping):通過使用描述對象和數據庫之間映射的元數據,將程序中的對象自動持久化到關係數據庫中。本質就是將關係和對象通過映射文件進行聯繫。我們在程序中只需要操作對象即可操作對應的數據庫表的數據。

POJO(Plain Ordinary Java Object)簡單的Java對象,實際就是普通JavaBeans。

HQL(Hibernate Query Language)是面向對象的查詢。

JPQL(Java Persistence Query Language)查詢語言,通過面向對象而非面向數據庫的查詢語言查詢數據,避免程序的SQL語句緊密耦合。

JPA 規範提供了 JPQL ,Hibernate 提供了 HQL來查詢數據庫。JPQL的原型就是Hibernate 的HQL。

  

上面內容更多參考這兩篇文章,整的明明白白

Hibernate和Spring Data JPA有什麼區別?

JPA和SpringDataJPA簡介

二、Spring與Spring Data JPA的整合

在實際的工程中,推薦採用 Spring Data JPA + ORM(如:Hibernate)進行開發,這樣在切換不同的ORM提供了方面,同時也使得Repository變得簡單。程序低耦合。

1、創建一個maven java項目,在 pom.xml 中導入包的依賴

     

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.jq.springdatajpademo</groupId>
    <artifactId>springdatajpademo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <!-- spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.7.RELEASE</version>
        </dependency>
        <!-- slf4j -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <!-- hibernate -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.2.17.Final</version>
        </dependency>
        <!-- hibernate-c3p0包含c3p0 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-c3p0</artifactId>
            <version>5.2.17.Final</version>
        </dependency>
        <!-- spring-data-jpa -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>2.3.1.RELEASE</version>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.10.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310 -->
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>2.10.3</version>
        </dependency>


        <!-- spring-test -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.7.RELEASE</version>
            <scope>test</scope>
        </dependency>

        <!-- junit  -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
            </plugin>
        </plugins>
    </build>

</project>

2、jdbc.properties :

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/springdatajpa_demo?useUnicode=true&characterEncoding=utf8&useSSL=true
jdbc.user=root
jdbc.password=123456

c3p0.initialPoolSize=5
c3p0.acquireIncrement=5
c3p0.maxPoolSize=20
c3p0.minPoolSize=5
c3p0.maxStatements=200
c3p0.maxStatementsPerConnection=5

hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
hibernate.show_sql=true
hibernate.format_sql=true
hibernate.hbm2ddl.auto=update

3、log4j.properties :

log4j.rootLogger = debug,stdout, D
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.Threshold = INFO
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p %m%n
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = ./log4j.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern=%d %p %m%n

4、spring.xml:

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.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/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

    <!-- 1. 導入源屬性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties" />
    <!-- c3p0 連接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 類似EL表達式取資源文件的值 -->
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="initialPoolSize" value="${c3p0.initialPoolSize}"></property>
        <property name="acquireIncrement" value="${c3p0.acquireIncrement}"></property>
        <property name="maxPoolSize" value="${c3p0.maxPoolSize}"></property>
        <property name="minPoolSize" value="${c3p0.minPoolSize}"></property>
        <property name="maxStatements" value="${c3p0.maxStatements}"></property>
        <property name="maxStatementsPerConnection"	value="${c3p0.maxStatementsPerConnection}"></property>
    </bean>

    <!-- 2. 整合jpa, 配置Hibernate的Sessionfactory實例 -->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <!-- 數據源 -->
        <property name="dataSource" ref="dataSource"></property>
        <!-- jpa的實現產品 -->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
        </property>
        <!-- 配置實體類所在的包 -->
        <property name="packagesToScan" value="cn.jq.springdatajpademo.model"></property>

        <!-- jpa的實現的基本屬性 -->
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
            </props>
        </property>
    </bean>

    <!-- 配置spring的jpa聲明事務管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"></property>
    </bean>

    <!-- 開啓事務註釋 @Transactional -->
    <!--<tx:annotation-driven transaction-manager="transactionManager" /> -->

    <!--  配置事務通知 -->
    <tx:advice id="tsAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="load*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="select*" read-only="true"/>
            <tx:method name="*" read-only="false"/>
        </tx:attributes>
    </tx:advice>
    <!-- 配置事務切入點 -->
    <aop:config>
        <aop:pointcut expression="execution(* cn.jq.springdatajpademo.service.*.*(..))" id="txPointcut"/>
        <aop:advisor advice-ref="tsAdvice" pointcut-ref="txPointcut"/>
    </aop:config>

    <!-- 配置spring data jpa : base-package會掃描對應的包,把裏面的接口的實現對象放到spring的ioc容器裏-->
    <jpa:repositories base-package="cn.jq.springdatajpademo.dao" entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>

</beans>

5、pojo層

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.format.annotation.DateTimeFormat;

import javax.persistence.*;
import java.time.LocalDateTime;

/**
 * 用戶的實體類
 *      配置映射關係
 *
 *   1.實體類和表的映射關係
 *      @Entity:聲明實體類
 *      @Table : 配置實體類和表的映射關係
 *          name : 配置數據庫表的名稱
 *   2.實體類中屬性和表中字段的映射關係
 *
 *
 */
@Entity
@Table(name = "t_user")
public class User {

    /**
     * @Id:聲明主鍵的配置
     * @GeneratedValue:配置主鍵的生成策略
     *      strategy
     *          GenerationType.IDENTITY :自增,mysql
     *                 * 底層數據庫必須支持自動增長(底層數據庫支持的自動增長方式,對id自增)
     *          GenerationType.SEQUENCE : 序列,oracle
     *                 * 底層數據庫必須支持序列
     *          GenerationType.TABLE : jpa提供的一種機制,通過一張數據庫表的形式幫助我們完成主鍵自增
     *          GenerationType.AUTO : 由程序自動的幫助我們選擇主鍵生成策略
     * @Column:配置屬性和字段的映射關係
     *      name:數據庫表中字段的名稱
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;

    private String password;

    private Integer age;


    @JsonFormat(shape = JsonFormat.Shape.STRING, timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @Column(name = "create_time")
    private LocalDateTime createtime; /** 創建時間*/

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public LocalDateTime getCreatetime() {
        return createtime;
    }

    public void setCreatetime(LocalDateTime createtime) {
        this.createtime = createtime;
    }

    // 空構造
    public User() {
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                ", createtime=" + createtime +
                '}';
    }
}

6、dao層

import cn.jq.springdatajpademo.model.User;
import org.springframework.data.repository.Repository;

/**
 * 符合SpringDataJpa的dao層接口規範
 *      Repository<操作的實體類類型,實體類中主鍵屬性的類型>
 *          * Repository標記接口,並沒有提供任何操作方法
 */
public interface UserDao extends Repository<User, Long> {

    // 定義一個方法(方法名有一定的規則), 不需要做任何的sql實現
    User getById(Long id);
}

7、測試類

import cn.jq.springdatajpademo.dao.UserDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @Description:
 * @Auther: leijq
 * @Date: 2020-07-06 22:54
 * @Version: V1.0
 */
@RunWith(SpringJUnit4ClassRunner.class) //聲明spring提供的單元測試環境
@ContextConfiguration(locations = "classpath:spring.xml")//指定spring容器的配置信息
public class UserDaoTest {

    @Autowired
    private UserDao userDao;

    @Test
    public void testGetById() {
        // 先註釋,運行UserDaoTest類,生成表,手動在表中添加一個記錄,然後再測試testGetById
//        User user = userDao.getById(1L);
//        System.out.println(user);
    }
}

 

      

    

 

三、測試類的實現分析

1、dao層接口繼承的Repository接口

查看 Repository接口的源碼:該接口中沒有定義任何方法,即稱之爲標記接口

這個標記接口的作用:是把繼承它的子接口,比如:UserDao標記爲 Repository Bean,Spring就會爲這個接口創建代理實現類,並且放入到IOC反轉容器裏,就像以前自己寫的 UserDao的實現類 UserDaoImpl一樣。

2、extends Repository<User, Integer>繼承的寫法還可以使用一個 @RepositoryDefinition註解來替代

import org.springframework.data.repository.RepositoryDefinition;


@RepositoryDefinition(domainClass = User.class, idClass = Long.class)
public interface UserDao {

    // 定義一個方法(方法名有一定的規則), 不需要做任何的sql實現
    User getById(Long id);
}

3、在dao層的接口中定義方法

遵守一些規定:

1)查詢的方法:find或get或read開頭

2)查詢條件:用關鍵字鏈接,涉及屬性首寫字母大寫

3)支持級聯查詢,關聯對象和它的屬性之間用_連接

Keyword Sample JPQL snippet

And

findByLastnameAndFirstname

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

IsEquals

findByFirstname,findByFirstnameIs,findByFirstnameEquals

… where x.firstname = ?1

Between

findByStartDateBetween

… where x.startDate between ?1 and ?2

LessThan

findByAgeLessThan

… where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

… where x.age <= ?1

GreaterThan

findByAgeGreaterThan

… where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

… where x.age >= ?1

After

findByStartDateAfter

… where x.startDate > ?1

Before

findByStartDateBefore

… where x.startDate < ?1

IsNullNull

findByAge(Is)Null

… where x.age is null

IsNotNullNotNull

findByAge(Is)NotNull

… where x.age not null

Like

findByFirstnameLike

… where x.firstname like ?1

NotLike

findByFirstnameNotLike

… where x.firstname not like ?1

StartingWith

findByFirstnameStartingWith

… where x.firstname like ?1 (parameter bound with appended %)

EndingWith

findByFirstnameEndingWith

… where x.firstname like ?1 (parameter bound with prepended %)

Containing

findByFirstnameContaining

… where x.firstname like ?1 (parameter bound wrapped in %)

OrderBy

findByAgeOrderByLastnameDesc

… where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

… where x.lastname <> ?1

In

findByAgeIn(Collection<Age> ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection<Age> ages)

… where x.age not in ?1

True

findByActiveTrue()

… where x.active = true

False

findByActiveFalse()

… where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

… where UPPER(x.firstame) = UPPER(?1)

4、Repository接口的子接口和實現類(重點內容)

    

 

到此,Spring Data JPA與Spring的簡單使用搞定,對於Repository接口的子接口和實現類是後面掌握的重點

 

—— Stay Hungry. Stay Foolish. 求知若飢,虛心若愚。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章