來說說JPA、Hibernate、Spring Data JPA之間的什么關系
Java 持久層框架訪問數據庫的方式大致分為兩種:一種以 SQL 核心,封裝一定程度的 JDBC 操作,比如: MyBatis。另一種是以 Java 實體類為核心,將實體類的和數據庫表之間建立映射關系,也就是我們說的ORM框架,如:Hibernate、Spring Data JPA。
JPA
Spring Data JPA是建立的JPA的基礎之上, 那到底什么是JPA呢?
我們都知道不同的數據庫廠商都有自己的實現類,后來統一規范也就有了數據庫驅動,Java在操作數據庫的時候,底層使用的其實是JDBC,而JDBC是一組操作不同數據庫的規范。我們的Java應用程序,只需要調用JDBC提供的API就可以訪問數據庫了,而JPA也是類似的道理。
JPA全稱為Java Persistence API(Java持久層API),它是Sun公司在JavaEE 5中提出的Java持久化規范。它為Java開發人員提供了一種對象/關聯映射工具,來管理Java應用中的關系數據,JPA吸取了目前Java持久化技術的優點,旨在規范、簡化Java對象的持久化工作。很多ORM框架都是實現了JPA的規范,如:Hibernate、EclipseLink。
需要注意的是JPA統一了Java應用程序訪問ORM框架的規范。
JPA為我們提供了以下規范:
-
ORM映射元數據:JPA支持XML和注解兩種元數據的形式,元數據描述對象和表之間的映射關系,框架據此將實體對象持久化到數據庫表中。
-
JPA 的API:用來操作實體對象,執行CRUD操作,框架在后台替我們完成所有的事情,開發人員不用再寫SQL了。
-
JPQL查詢語言:通過面向對象而非面向數據庫的查詢語言查詢數據,避免程序的SQL語句緊密耦合。
Hibernate

Hibernate是Java中的對象關系映射解決方案。對象關系映射或ORM框架是將應用程序數據模型對象映射到關系數據庫表的技術。Hibernate 不僅關注於從 Java 類到數據庫表的映射,也有 Java 數據類型到 SQL 數據類型的映射。
Hibernate 和 JPA是什么關系呢
上面我們介紹到JPA是Java EE 5規范中提出的Java持久化接口,而Hibernate是一個ORM框架
JPA和Hibernate的關系:
• JPA是一個規范,而不是框架
• Hibernate是JPA的一種實現,是一個框架
Spring Data是啥
Spring Data是Spring 社區的一個子項目,主要用於簡化數據(關系型&非關系型)訪問,其主要目標是使得數據庫的訪問變得方便快捷。
• 它提供很多模板操作
– Spring Data Elasticsearch
– Spring Data MongoDB
– Spring Data Redis
– Spring Data Solr
• 強大的 Repository 和定制的數據儲存對象的抽象映射
• 對數據訪問對象的支持
Spring Data JPA
Spring Data JPA是在實現了JPA規范的基礎上封裝的一套 JPA 應用框架,雖然ORM框架都實現了JPA規范,但是在不同的ORM框架之間切換仍然需要編寫不同的代碼,而使用Spring Data JPA能夠方便大家在不同的ORM框架之間進行切換而不需要更改代碼。Spring Data JPA旨在通過將統一ORM框架的訪問持久層的操作,來提高開發人的效率。

Spring Data JPA和Hibernate的關系
Hibernate其實是JPA的一種實現,而Spring Data JPA是一個JPA數據訪問抽象。也就是說Spring Data JPA不是一個實現或JPA提供的程序,它只是一個抽象層,主要用於減少為各種持久層存儲實現數據訪問層所需的樣板代碼量。但是它還是需要JPA提供實現程序,其實Spring Data JPA底層就是使用的 Hibernate實現。
總結就是:
• Hibernate是JPA的一種實現,是一個框架
• Spring Data JPA是一種JPA的抽象層,底層依賴Hibernate
實踐
理論一定要與實踐相結合
1、首先,我們需要配置pom.xml
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
2、然后是application.properties
的配置
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/數據庫名字?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=true
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=create
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
這里重點簡單介紹下spring.jpa.properties.hibernate.hbm2ddl.auto
有幾種配置:
-
create
:表示每次加載Hibernate
時都會刪除上一次生成的表(包括數據),然后重新生成新表,即使兩次沒有任何修改也會這樣執行。適用於每次執行單測前清空數據庫的場景。 -
create-drop
:表示每次加載Hibernate
時都會生成表,但當SessionFactory
關閉時,所生成的表將自動刪除。 -
update
:最常用的屬性值,第一次加載Hibernate
時創建數據表(前提是需要先有數據庫),以后加載Hibernate
時不會刪除上一次生成的表,會根據實體更新,只新增字段,不會刪除字段(即使實體中已經刪除)。 -
validate
:每次加載Hibernate
時都會驗證數據表結構,只會和已經存在的數據表進行比較,根據model
修改表結構,但不會創建新表。 -
不配置此項,表示禁用自動建表功能
spring.jpa.show-sql=true
該配置當在執行數據庫操作的時候會在控制台打印 sql
語句,方便我們檢查排錯等。
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
這是數據庫的方言配置。
3、接下來我們建立用戶實體類
@Entity
public class User {
@Id
@GeneratedValue
private long id;
@Column(nullable = false, unique = true)
private String userName;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private int age;
}
這里的一些注解解釋如下:
-
@Entity 是一個類注解,用來注解該類是一個實體類用來進行和數據庫中的表建立關聯關系,首次啟動項目的時候,默認會在數據中生成一個同實體類相同名字的表(table),也可以通過注解中的
name
屬性來修改表(table)名稱, 如@Entity(name=“user”) , 這樣數據庫中表的名稱則是user
。該注解十分重要,如果沒有該注解首次啟動項目的時候你會發現數據庫沒有生成對應的表。 -
@Table 注解也是一個類注解,該注解可以用來修改表的名字,該注解完全可以忽略掉不用,@Entity 注解已具備該注解的功能。
-
@Id 類的屬性注解,該注解表明該屬性字段是一個主鍵,該屬性必須具備,不可缺少。
-
@GeneratedValue 該注解通常和 @Id 主鍵注解一起使用,用來定義主鍵的呈現形式,該注解通常有多種使用策略,先總結如下:
-
@GeneratedValue(strategy= GenerationType.IDENTITY) 該注解由數據庫自動生成,主鍵自增型,在 mysql 數據庫中使用最頻繁,oracle 不支持。
-
@GeneratedValue(strategy= GenerationType.AUTO) 主鍵由程序控制,默認的主鍵生成策略,
oracle
默認是序列化的方式,mysql
默認是主鍵自增的方式。 -
@GeneratedValue(strategy= GenerationType.SEQUENCE) 根據底層數據庫的序列來生成主鍵,條件是數據庫支持序列,
Oracle
支持,Mysql
不支持。 -
@GeneratedValue(strategy= GenerationType.TABLE) 使用一個特定的數據庫表格來保存主鍵,較少使用。
-
@Column 是一個類的屬性注解,該注解可以定義一個字段映射到數據庫屬性的具體特征,比如字段長度,映射到數據庫時屬性的具體名字等。
-
@Transient 是一個屬性注解,該注解標注的字段不會被映射到數據庫當中。
4、聲明 UserRepository
接口,繼承JpaRepository
,如下所示
public interface UserRepository extends JpaRepository<User, Long> {
}
這里的 JpaRepository
繼承了接口PagingAndSortingRepository
和QueryByExampleExecutor
。而PagingAndSortingRepository
又繼承CrudRepository
。
因此,JpaRepository
接口同時擁有了基本CRUD
功能以及分頁功能。因此,這里我們可以繼承JpaRepository
,從而獲得Spring
為我們預先定義的多種基本數據操作方法。
5、然后我們定義一個測試類, 這里我們演示下添加操作, @Transactional 表示開啟事務防止出現臟數據。
……
@Autowired
private UserRepository userRepository;
@Test
@Transactional
public void userAddTest() {
User user = new User();
user.setUserName("李嘉圖");
user.setAge(30);
user.setPassword("123456");
userRepository.save(user);
User item = userRepository.findByUserName("ljt");
log.info(JsonUtils.toJson(item));
}
6、接下來我們說下查詢,查詢可以分為基本查詢和自定義查詢,一種是 spring data
默認已經實現,只需要要繼承JpaRepository
,一種是根據查詢的方法來自動解析成 SQL
。
@Test
public void testQuery() throws Exception {
User user=new User();
userRepository.findAll();
userRepository.findOne(1l);
userRepository.save(user);
userRepository.delete(user);
userRepository.count();
userRepository.exists(1l);
……
}
7、自定義的簡單查詢就是根據方法名來自動生成SQL
,主要的語法是findXXBy,readAXXBy,queryXXBy,countXXBy, getXXBy
后面跟屬性名稱,舉幾個例子:
User findByUserName(String userName);
User findByUserNameOrEmail(String username, String email);
Long deleteById(Long id);
Long countByUserName(String userName);
List<User> findByEmailLike(String email);
User findByUserNameIgnoreCase(String userName);
List<User> findByUserNameOrderByEmailDesc(String email);
8、接下來,我們說下復雜的查詢,在實際的開發中我們需要用到分頁、刪選、連表等查詢的時候就需要特殊的方法或者自定義 SQL
。
以分頁查詢為例,分頁查詢在實際使用中非常普遍了,spring data jpa
已經幫我們實現了分頁的功能,在查詢的方法中,需要傳入參數Pageable
,當查詢中有多個參數的時候Pageabl
e建議做為最后一個參數傳入。Pageable
是 spring
封裝的分頁實現類,使用的時候需要傳入頁數、每頁條數和排序規則。
Page<User> findALL(Pageable pageable);
Page<User> findByUserName(String userName,Pageable pageable);
9、我們看下下面的測試用例
@Test
public void testPageQuery() throws Exception {
int page=1,size=5;
Sort sort = new Sort(Direction.DESC, "id");
Pageable pageable = new PageRequest(page, size, sort);
userRepository.findALL(pageable);
userRepository.findByUserName("testName", pageable);
}
Spring data大部分的SQL都可以根據方法名定義的方式來實現,但是由於某些原因我們想使用自定義的 SQL來查詢,spring data
也是完美支持的,如下所示:
@Modifying
@Query("update User u set u.userName = ?1 where c.id = ?2")
int modifyByIdAndUserId(String userName, Long id);
@Transactional
@Modifying
@Query("delete from User where id = ?1")
void deleteByUserId(Long id);
@Transactional(timeout = 10)
@Query("select u from User u where u.emailAddress = ?1")
User findByEmailAddress(String emailAddress);