來說說JPA、Hibernate、Spring Data JPA之間的什麼關係?
來說說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);