在實(shí)際開發(fā)過程中,對數(shù)據(jù)庫的操作大多可以歸結(jié)為:“增刪改查”。就最為普遍的單表操作而言,除了表和字段不同外,語句幾乎都是類似的,開發(fā)人員需要寫大量類似而枯燥的語句來完成業(yè)務(wù)邏輯。
為了解決這些大量枯燥的數(shù)據(jù)操作語句,誕生了非常多的優(yōu)秀框架,比如:Hibernate。通過整合Hibernate,我們能夠以操作Java實(shí)體的方式來完成對數(shù)據(jù)的操作,通過框架的幫助,對Java實(shí)體的變更最終將自動地映射到數(shù)據(jù)庫表中。
在Hibernate的幫助下,Java實(shí)體映射到數(shù)據(jù)庫表數(shù)據(jù)完成之后,再進(jìn)一步解決抽象各個Java實(shí)體基本的“增刪改查”操作,我們通常會以泛型的方式封裝一個模板Dao來進(jìn)行抽象簡化,但是這樣依然不是很方便,我們需要針對每個實(shí)體編寫一個繼承自泛型模板Dao的接口,再編寫該接口的實(shí)現(xiàn)。雖然一些基礎(chǔ)的數(shù)據(jù)訪問已經(jīng)可以得到很好的復(fù)用,但是在代碼結(jié)構(gòu)上針對每個實(shí)體都會有一堆Dao的接口和實(shí)現(xiàn)。
由于模板Dao的實(shí)現(xiàn),使得這些具體實(shí)體的Dao層已經(jīng)變的非?!氨 保幸恍┚唧w實(shí)體的Dao實(shí)現(xiàn)可能完全就是對模板Dao的簡單代理,并且往往這樣的實(shí)現(xiàn)類可能會出現(xiàn)在很多實(shí)體上。Spring Data JPA的出現(xiàn)正可以讓這樣一個已經(jīng)很“薄”的數(shù)據(jù)訪問層變成只是一層接口的編寫方式。比如,下面的例子:
public interface UserRepository extends JpaRepository<User, Long> {
User findByName(String name);
@Query("from User u where u.name=:name")
User findUser(@Param("name") String name);
}
我們只需要通過編寫一個繼承自JpaRepository
的接口就能完成數(shù)據(jù)訪問,下面以一個具體實(shí)例來體驗(yàn)Spring Data JPA給我們帶來的強(qiáng)大功能。
使用步驟
由于Spring Data JPA依賴于Hibernate。如果您對Hibernate有一定了解,下面內(nèi)容可以毫不費(fèi)力的看懂并上手使用它。如果您還是Hibernate新手,您可以先按如下方式嘗試。
工程配置
在pom.xml
中添加相關(guān)依賴,加入以下內(nèi)容:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
在application.xml中配置:數(shù)據(jù)庫連接信息(如使用嵌入式數(shù)據(jù)庫則不需要)、自動創(chuàng)建表結(jié)構(gòu)的設(shè)置,例如使用mysql的情況如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
spring.jpa.properties.hibernate.hbm2ddl.auto
是hibernate的配置屬性,其主要作用是:自動創(chuàng)建、更新、驗(yàn)證數(shù)據(jù)庫表結(jié)構(gòu)。該參數(shù)的幾種配置如下:
create
:每次加載hibernate時(shí)都會刪除上一次的生成的表,然后根據(jù)你的model類再重新來生成新表,哪怕兩次沒有任何改變也要這樣執(zhí)行,這就是導(dǎo)致數(shù)據(jù)庫表數(shù)據(jù)丟失的一個重要原因。create-drop
:每次加載hibernate時(shí)根據(jù)model類生成表,但是sessionFactory一關(guān)閉,表就自動刪除。update
:最常用的屬性,第一次加載hibernate時(shí)根據(jù)model類會自動建立起表的結(jié)構(gòu)(前提是先建立好數(shù)據(jù)庫),以后加載hibernate時(shí)根據(jù)model類自動更新表結(jié)構(gòu),即使表結(jié)構(gòu)改變了但表中的行仍然存在不會刪除以前的行。要注意的是當(dāng)部署到服務(wù)器后,表結(jié)構(gòu)是不會被馬上建立起來的,是要等應(yīng)用第一次運(yùn)行起來后才會。validate
:每次加載hibernate時(shí),驗(yàn)證創(chuàng)建數(shù)據(jù)庫表結(jié)構(gòu),只會和數(shù)據(jù)庫中的表進(jìn)行比較,不會創(chuàng)建新表,但是會插入新值。
至此已經(jīng)完成基礎(chǔ)配置,如果您有在Spring下整合使用過它的話,相信你已經(jīng)感受到Spring Boot的便利之處:JPA的傳統(tǒng)配置在persistence.xml
文件中,但是這里我們不需要。
創(chuàng)建實(shí)體
創(chuàng)建一個User實(shí)體,包含id(主鍵)、name(姓名)、age(年齡)屬性,通過ORM框架其會被映射到數(shù)據(jù)庫表中,由于配置了hibernate.hbm2ddl.auto
,在應(yīng)用啟動的時(shí)候框架會自動去數(shù)據(jù)庫中創(chuàng)建對應(yīng)的表。
@Entity
@Data
@NoArgsConstructor
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
}
@Entity
注解標(biāo)識了User類是一個持久化的實(shí)體@Data
和@NoArgsConstructor
是Lombok中的注解。用來自動生成各參數(shù)的Set、Get函數(shù)以及不帶參數(shù)的構(gòu)造函數(shù)。@Id
和@GeneratedValue
用來標(biāo)識User對應(yīng)對應(yīng)數(shù)據(jù)庫表中的主鍵
注意:除了這些注解之外,還有很多用來精細(xì)化配置映射關(guān)系的注解,大家可以自行閱讀Hibernate的文檔來學(xué)習(xí)這些注解的詳細(xì)使用方法。
創(chuàng)建數(shù)據(jù)訪問接口
下面針對User實(shí)體創(chuàng)建對應(yīng)的Repository
接口實(shí)現(xiàn)對該實(shí)體的數(shù)據(jù)訪問,如下代碼:
public interface UserRepository extends JpaRepository<User, Long> {
User findByName(String name);
User findByNameAndAge(String name, Integer age);
@Query("from User u where u.name=:name")
User findUser(@Param("name") String name);
}
在Spring Data JPA中,只需要編寫類似上面這樣的接口就可實(shí)現(xiàn)數(shù)據(jù)訪問。不再像我們以往編寫了接口時(shí)候還需要自己編寫接口實(shí)現(xiàn)類,直接減少了我們的文件清單。
下面對上面的UserRepository
做一些解釋,該接口繼承自JpaRepository
,通過查看JpaRepository
接口的API文檔,可以看到該接口本身已經(jīng)實(shí)現(xiàn)了創(chuàng)建(save)、更新(save)、刪除(delete)、查詢(findAll、findOne)等基本操作的函數(shù),因此對于這些基礎(chǔ)操作的數(shù)據(jù)訪問就不需要開發(fā)者再自己定義。
在上例中,我們可以看到下面兩個函數(shù):
User findByName(String name)
User findByNameAndAge(String name, Integer age)
它們分別實(shí)現(xiàn)了按name查詢User實(shí)體和按name和age查詢User實(shí)體,可以看到我們這里沒有任何類SQL語句就完成了兩個條件查詢方法。這就是Spring-data-jpa的一大特性:通過解析方法名創(chuàng)建查詢。
除了通過解析方法名來創(chuàng)建查詢外,它也提供通過使用@Query 注解來創(chuàng)建查詢,您只需要編寫JPQL語句,并通過類似“:name”來映射@Param指定的參數(shù),就像例子中的第三個findUser函數(shù)一樣。
單元測試
在完成了上面的數(shù)據(jù)訪問接口之后,按照慣例就是編寫對應(yīng)的單元測試來驗(yàn)證編寫的內(nèi)容是否正確。這里就不多做介紹,主要通過數(shù)據(jù)操作和查詢來反復(fù)驗(yàn)證操作的正確性。
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {
@Autowired
private UserRepository userRepository;
@Test
public void test() throws Exception {
// 創(chuàng)建10條記錄
userRepository.save(new User("AAA", 10));
userRepository.save(new User("BBB", 20));
userRepository.save(new User("CCC", 30));
userRepository.save(new User("DDD", 40));
userRepository.save(new User("EEE", 50));
userRepository.save(new User("FFF", 60));
userRepository.save(new User("GGG", 70));
userRepository.save(new User("HHH", 80));
userRepository.save(new User("III", 90));
userRepository.save(new User("JJJ", 100));
// 測試findAll, 查詢所有記錄
Assert.assertEquals(10, userRepository.findAll().size());
// 測試findByName, 查詢姓名為FFF的User
Assert.assertEquals(60, userRepository.findByName("FFF").getAge().longValue());
// 測試findUser, 查詢姓名為FFF的User
Assert.assertEquals(60, userRepository.findUser("FFF").getAge().longValue());
// 測試findByNameAndAge, 查詢姓名為FFF并且年齡為60的User
Assert.assertEquals("FFF", userRepository.findByNameAndAge("FFF", 60).getName());
// 測試刪除姓名為AAA的User
userRepository.delete(userRepository.findByName("AAA"));
// 測試findAll, 查詢所有記錄, 驗(yàn)證上面的刪除是否成功
Assert.assertEquals(9, userRepository.findAll().size());
}
}
關(guān)于Spring Data
Spring Data JPA在Spring家族中實(shí)際上是一個二級項(xiàng)目,它隸屬于Spring Data這個頂級項(xiàng)目。讀者可以看一下關(guān)于這個項(xiàng)目的介紹,它除了涵蓋對關(guān)系型數(shù)據(jù)庫的抽象之外,其實(shí)還有很多對其他數(shù)據(jù)存儲中間件的實(shí)現(xiàn),比如我們常用的Redis、MongoDB、Elasticsearch等。
如果再找?guī)讉€項(xiàng)目看一下它們的簡單示例,你會發(fā)現(xiàn):不論你是要訪問什么數(shù)據(jù)存儲產(chǎn)品,它們的編碼方式幾乎都是一樣的!這就是Spring Data這個項(xiàng)目充滿魅力的地方!通過對數(shù)據(jù)訪問操作的抽象來屏蔽細(xì)節(jié),用不同子項(xiàng)目的方式去實(shí)現(xiàn)細(xì)節(jié)。讓開發(fā)者只需要學(xué)會使用Spring Data,就能方便快捷的學(xué)會對各種數(shù)據(jù)存儲的操作。所以,對于Spring Data,強(qiáng)烈推薦Java開發(fā)者們可以學(xué)、甚至讀一下源碼的重要框架。雖然,目前來說很多大型互聯(lián)網(wǎng)公司并不會選擇它(性能考量居多,能真正用好它的人不多)作為主要的開發(fā)框架,但是其背后的抽象思想是非常值得我們學(xué)習(xí)的。并且,在做一些非高并發(fā)項(xiàng)目的時(shí)候,這簡直就是一個快捷開發(fā)神器,它可以幫助我們少寫非常多的代碼!
注:本文轉(zhuǎn)載自“程序猿DD”,如有侵權(quán),請聯(lián)系刪除!