
前言:由于之前沒有接觸過Hibernate框架,但是最近看一些博客深深被它的“效率”所吸引,所以這就來跟大家一起就著一個(gè)簡(jiǎn)單的例子來嘗嘗Spring全家桶里自帶的JPA的鮮
Spring-DATA-JPA 簡(jiǎn)介
JPA(Java Persistence API)是Sun官方提出的Java持久化規(guī)范。它為Java開發(fā)人員提供了一種對(duì)象/關(guān)聯(lián)映射工具來管理Java應(yīng)用中的關(guān)系數(shù)據(jù)。他的出現(xiàn)主要是為了簡(jiǎn)化現(xiàn)有的持久化開發(fā)工作和整合ORM技術(shù),結(jié)束現(xiàn)在Hibernate,TopLink,JDO等ORM框架各自為營(yíng)的局面。值得注意的是,JPA是在充分吸收了現(xiàn)有Hibernate,TopLink,JDO等ORM框架的基礎(chǔ)上發(fā)展而來的,具有易于使用,伸縮性強(qiáng)等優(yōu)點(diǎn)。從目前的開發(fā)社區(qū)的反應(yīng)上看,JPA受到了極大的支持和贊揚(yáng),其中就包括了Spring與EJB3.0的開發(fā)團(tuán)隊(duì)。
注意:JPA是一套規(guī)范,不是一套產(chǎn)品,那么像Hibernate,TopLink,JDO他們是一套產(chǎn)品,如果說這些產(chǎn)品實(shí)現(xiàn)了這個(gè)JPA規(guī)范,那么我們就可以叫他們?yōu)镴PA的實(shí)現(xiàn)產(chǎn)品。
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 規(guī)范的基礎(chǔ)上封裝的一套JPA應(yīng)用框架,可使開發(fā)者用極簡(jiǎn)的代碼即可實(shí)現(xiàn)對(duì)數(shù)據(jù)的訪問和操作。它提供了包括增刪改查等在內(nèi)的常用功能,且易于擴(kuò)展!學(xué)習(xí)并使用 Spring Data JPA 可以極大提高開發(fā)效率!(spring data jpa讓我們解脫了DAO層的操作,基本上所有CRUD都可以依賴于它來實(shí)現(xiàn))
Hibernate 和 MyBatis 簡(jiǎn)單對(duì)比
由于JPA底層干活的仍然是Hibernate框架,而我們之前學(xué)習(xí)的只有MyBatis相關(guān)的東西,所以在嘗鮮之前還是有必要簡(jiǎn)單了解一下兩者的區(qū)別:
Hibernate的優(yōu)勢(shì):
- Hibernate的DAO層開發(fā)比MyBatis簡(jiǎn)單,Mybatis需要維護(hù)SQL和結(jié)果映射。
- Hibernate對(duì)對(duì)象的維護(hù)和緩存要比MyBatis好,對(duì)增刪改查的對(duì)象的維護(hù)要方便。
- Hibernate數(shù)據(jù)庫(kù)移植性很好,MyBatis的數(shù)據(jù)庫(kù)移植性不好,不同的數(shù)據(jù)庫(kù)需要寫不同SQL。
- Hibernate有更好的二級(jí)緩存機(jī)制,可以使用第三方緩存。MyBatis本身提供的緩存機(jī)制不佳。
MyBatis的優(yōu)勢(shì):
- MyBatis可以進(jìn)行更為細(xì)致的SQL優(yōu)化,可以減少查詢字段。
- MyBatis容易掌握,而Hibernate門檻較高。
簡(jiǎn)單總結(jié):
- MyBatis:小巧、方便、高效、簡(jiǎn)單、直接、半自動(dòng)化
- Hibernate:強(qiáng)大、方便、高效、復(fù)雜、間接、全自動(dòng)化
CRUD + 分頁(yè)后臺(tái)實(shí)例
下面我們來快速搭建一個(gè)使用Spring-DATA-JPA的CRUD+分頁(yè)后臺(tái)實(shí)例,并且我們會(huì)直接使用到RESTful API(不熟悉的同學(xué)戳這里)
第一步:新建SpringBoot項(xiàng)目
打開IDEA新建一個(gè)SpringBoot項(xiàng)目,不熟悉SpringBoot的同學(xué)請(qǐng)右轉(zhuǎn):【傳送門】,然后在pom.xml中添加以下依賴:
<!-- mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>
<!-- jpa-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
然后把a(bǔ)pplication.properties弄成這個(gè)樣子:
#數(shù)據(jù)庫(kù)
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/testdb?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=update
#顯示SQL語句
spring.jpa.show-sql=true
#不加下面這句則默認(rèn)創(chuàng)建MyISAM引擎的數(shù)據(jù)庫(kù)
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
#自己重寫的配置類,默認(rèn)使用utf8編碼
spring.jpa.properties.hibernate.dialect=com.wmyskxz.demo.config.MySQLConfig
spring.jpa.properties.hibernate.hbm2ddl.auto是hibernate的配置屬性,其主要作用是:自動(dòng)創(chuàng)建、更新、驗(yàn)證數(shù)據(jù)庫(kù)表結(jié)構(gòu)。該參數(shù)的幾種配置如下:
- create:每次加載hibernate時(shí)都會(huì)刪除上一次的生成的表,然后根據(jù)你的model類再重新來生成新表,哪怕兩次沒有任何改變也要這樣執(zhí)行,這就是導(dǎo)致數(shù)據(jù)庫(kù)表數(shù)據(jù)丟失的一個(gè)重要原因。
- create-drop:每次加載hibernate時(shí)根據(jù)model類生成表,但是sessionFactory一關(guān)閉,表就自動(dòng)刪除。
- update:最常用的屬性,第一次加載hibernate時(shí)根據(jù)model類會(huì)自動(dòng)建立起表的結(jié)構(gòu)(前提是先建立好數(shù)據(jù)庫(kù)),以后加載hibernate時(shí)根據(jù)model類自動(dòng)更新表結(jié)構(gòu),即使表結(jié)構(gòu)改變了但表中的行仍然存在不會(huì)刪除以前的行。要注意的是當(dāng)部署到服務(wù)器后,表結(jié)構(gòu)是不會(huì)被馬上建立起來的,是要等應(yīng)用第一次運(yùn)行起來后才會(huì)。
- validate:每次加載hibernate時(shí),驗(yàn)證創(chuàng)建數(shù)據(jù)庫(kù)表結(jié)構(gòu),只會(huì)和數(shù)據(jù)庫(kù)中的表進(jìn)行比較,不會(huì)創(chuàng)建新表,但是會(huì)插入新值。
然后新建一個(gè)【config】包,創(chuàng)建一個(gè)【MySQLConfig】類(上面的spring.jpa.properties.hibernate.dialect屬性就要配置這里的類全路徑):
package com.wmyskxz.demo.config;
import org.hibernate.dialect.MySQL5InnoDBDialect;
public class MySQLConfig extends MySQL5InnoDBDialect {
@Override
public String getTableTypeString() {
return "ENGINE=InnoDB DEFAULT CHARSET=utf8";
}
}
第二步:創(chuàng)建好需要的數(shù)據(jù)庫(kù)
打開SQL服務(wù),建表語句也很簡(jiǎn)單啦:
create database testdb;
第三步:創(chuàng)建實(shí)體類
實(shí)體類映射的實(shí)際上是數(shù)據(jù)庫(kù)表的結(jié)構(gòu),在適當(dāng)?shù)陌夸浵拢ɡ纭緀ntity】)下創(chuàng)建好實(shí)體類:
package com.wmyskxz.demo.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity // 表明這是個(gè)實(shí)體類
public class User {
@Id // 表明這個(gè)屬性是主鍵
@GeneratedValue // 自增長(zhǎng)
private long id;
@Column(nullable = false, unique = true) // 不允許為空,屬性唯一
private String username;
@Column(nullable = false) // 不允許為空
private String password;
// getter and setter
}
第四步:DAO層
新建一個(gè)【repository】包,然后新建一個(gè)【UserRepository】接口,并繼承JpaRepository類:
package com.wmyskxz.demo.repository;
import com.wmyskxz.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
}
繼承JpaRepository需要傳入兩個(gè)參數(shù),一個(gè)是實(shí)體類User一個(gè)是主鍵的類型Long,而凡是繼承了JpaRepository類的就會(huì)自動(dòng)實(shí)現(xiàn)很多內(nèi)置的方法,包括增刪改查,以及使用默認(rèn)支持的Pageable對(duì)象來進(jìn)行分頁(yè),默認(rèn)的方法大致如下:
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll();
List<T> findAll(Sort var1);
List<T> findAllById(Iterable<ID> var1);
<S extends T> List<S> saveAll(Iterable<S> var1);
void flush();
<S extends T> S saveAndFlush(S var1);
void deleteInBatch(Iterable<T> var1);
void deleteAllInBatch();
T getOne(ID var1);
<S extends T> List<S> findAll(Example<S> var1);
<S extends T> List<S> findAll(Example<S> var1, Sort var2);
}
第五步:Controller層
新建【controller】包,新建一個(gè)【UserController】類,編寫簡(jiǎn)單的增刪改查代碼:
package com.wmyskxz.demo.controoler;
import com.wmyskxz.demo.entity.User;
import com.wmyskxz.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
@RestController // 表明這是一個(gè)Controller并返回JSON格式
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping("/getOne")
public Optional<User> getOneUserById(@RequestParam long id) {
return userRepository.findById(id);
}
@GetMapping("/all")
public Iterable<User> getAllUsers(@RequestParam(value = "page", defaultValue = "0") int page,
@RequestParam(value = "size", defaultValue = "5") int size) {
page = page < 0 ? 0 : page;// 如果page為負(fù)數(shù)則修改為0,防止在首頁(yè)點(diǎn)擊上一頁(yè)發(fā)生錯(cuò)誤
Sort sort = new Sort(Sort.Direction.DESC, "id");// 按id倒敘排列
return userRepository.findAll(new PageRequest(page, size, sort));
}
@PostMapping("/add")
public String addUser(@RequestParam String username,
@RequestParam String password) {
User user = new User();
user.setUsername(username);
user.setPassword(password);
userRepository.save(user);// 注意這里是save
return "Saved";
}
@DeleteMapping("/delete")
public String deleteUserById(@RequestParam long id) {
userRepository.deleteById(id);
return "Deleted";
}
@PutMapping("/update")
public String updateUser(User user) {
// User user = new User();
// user.setId(id);
// user.setUsername(username);
// user.setPassword(password);
userRepository.save(user);
return "Updated";
}
}
上面就直接使用@Autowired自動(dòng)引入了繼承了JpaRepository的UserRepository接口,我們使用它默認(rèn)的方法已經(jīng)足夠完成我們的基礎(chǔ)功能了,值得一提的是我們的getAllUsers(...)方法,它往findAll()方法里傳入了一個(gè)Pageable對(duì)象,這是Spring Data庫(kù)中定義的一個(gè)接口,是所有分頁(yè)相關(guān)信息的一個(gè)抽象,通過該接口,我們可以得到和分頁(yè)相關(guān)的所有信息(例如pageNumber、pageSize等),這樣Jpa就能夠通過Pageable參數(shù)來得到一個(gè)帶分頁(yè)信息的Sql語句。
當(dāng)然上面我們是通過自己創(chuàng)建了一個(gè)Pageable對(duì)象,Spring也支持直接獲取Pageable對(duì)象,可以把上面的getAllUsers(...)方法改寫成下面這樣:
@GetMapping("/all")
public Iterable<User> getAllUsers(@PageableDefault(value = 5, sort = {"id"}, direction = Sort.Direction.DESC)
Pageable pageable) {
return userRepository.findAll(pageable);
}
默認(rèn)從第0頁(yè)開始,也可以自己傳入一個(gè)page參數(shù),跟上面的是一樣的。
第六步:運(yùn)行項(xiàng)目
上面我們就快速搭建起來了一個(gè)基于Spring Boot和JPA的REST風(fēng)格的后臺(tái)增刪改查實(shí)例,我們把項(xiàng)目跑起來,可以看到數(shù)據(jù)庫(kù)自動(dòng)創(chuàng)建了一些表:

JPA幫我們創(chuàng)建的user表的創(chuàng)建SQL如下:
CREATE TABLE `user` (
`id` bigint(20) NOT NULL,
`password` varchar(255) NOT NULL,
`username` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UK_sb8bbouer5wak8vyiiy4pf2bx` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
使用REST測(cè)試工具測(cè)試
完全符合我們的要求,然后我們使用一些REST的測(cè)試工具,來測(cè)試上面的功能是否都能正確運(yùn)行,比如我這里使用的【Restlet Client】,在Chrome商店就可以下載到。
/all地址測(cè)試:
首先先來測(cè)試一下http://localhost:8080/all地址,由于現(xiàn)在數(shù)據(jù)庫(kù)還是空的,所以可以看到返回如下:
{
"content": [
],
"pageable": {
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"offset": 0,
"pageNumber": 0,
"pageSize": 5,
"unpaged": false,
"paged": true
},
"totalElements": 0,
"last": true,
"totalPages": 0,
"number": 0,
"size": 5,
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"numberOfElements": 0,
"first": true,
"empty": true
}
添加用戶測(cè)試:
然后我們使用http://localhost:8080/add?username=wmyskxz&password=123地址,添加幾個(gè)類似的用戶信息:

可以看到返回正確的Saved信息:

/getOne地址測(cè)試:
我們就直接使用http://localhost:8080/getOne?id=1來獲取剛才添加的用戶,可以看到返回正確的數(shù)據(jù):
{
"id": 1,
"username": "wmyskxz",
"password": "123"
}
修改用戶測(cè)試:
然后我們使用http://localhost:8080/update?id=1&username=wmyskxz&password=123456來模擬進(jìn)行用戶密碼的修改:

可以看到正確的更新信息Updated,再次查詢用戶,也能看到正確的數(shù)據(jù):
{
"id": 1,
"username": "wmyskxz",
"password": "123456"
}
分頁(yè)測(cè)試:
我們使用添加功能為數(shù)據(jù)庫(kù)添加5條以上的數(shù)據(jù),然后進(jìn)行一次查詢/all,可以看到能夠按照id倒敘排列后返回5條數(shù)據(jù):

返回的JSON數(shù)據(jù)如下:
{
"content": [
{
"id": 10,
"username": "wmyskxz8",
"password": "123"
},
{
"id": 9,
"username": "wmyskxz7",
"password": "123"
},
{
"id": 8,
"username": "wmyskxz6",
"password": "123"
},
{
"id": 7,
"username": "wmyskxz5",
"password": "123"
},
{
"id": 6,
"username": "wmyskxz4",
"password": "123"
}
],
"pageable": {
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"offset": 0,
"pageNumber": 0,
"pageSize": 5,
"unpaged": false,
"paged": true
},
"totalElements": 9,
"last": false,
"totalPages": 2,
"number": 0,
"size": 5,
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"numberOfElements": 5,
"first": true,
"empty": false
}
刪除用戶測(cè)試:
使用地址http://localhost:8080/delete?id=1來刪除ID為1的用戶:

能正確看到Deleted信息,并查看數(shù)據(jù)能夠看到數(shù)據(jù)已經(jīng)被刪除了。
以上,我們就快速搭建好了一個(gè)CRUD+分頁(yè)的后臺(tái)實(shí)例,還用了比較流行的RESTful風(fēng)格,粗略的感受了一下JPA的方便,還是挺爽的..沒有復(fù)雜的Mapper文件,不用自動(dòng)生成實(shí)體,甚至不用管SQL,只需要專注在邏輯上就行了,其實(shí)簡(jiǎn)單使用的話以上的東西也能應(yīng)付一些常見的場(chǎng)景了,后期再深入了解了解吧!
參考資料:
springboot(五):spring data jpa的使用——純潔的微笑
springboot(十五):springboot+jpa+thymeleaf增刪改查示例——純潔的微笑
Spring Boot中使用Spring-data-jpa讓數(shù)據(jù)訪問更簡(jiǎn)單、更優(yōu)雅——程序猿DD
歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明出處!
簡(jiǎn)書ID:@我沒有三顆心臟
github:wmyskxz
歡迎關(guān)注公眾微信號(hào):wmyskxz
分享自己的學(xué)習(xí) & 學(xué)習(xí)資料 & 生活
想要交流的朋友也可以加qq群:3382693