Spring Boot 使用 JPA
1. 前言
使用 JDBC ,或者 JdbcTemplate 操作數據庫,需要編寫大量的 SQL 語句。SQL 語句基本都是些模板代碼,實際上是可以通過分析模板代碼的規則自動生成的。
JPA 就是簡化 Java 持久層數據操作的技術標準,是一種方案和規范。最開始是 Sun 公司提出的, Sun 公司就是開發出 Java 的公司,一度非常厲害,結果被 Oracle 收購了。Sun 公司雖然提出了 JPA 標準,但是并沒有具體實現。JPA 的實現里面比較出名的就是 Hibernate 了,所以本篇我們也是以 Hibernate 實現為基礎進行 Spring Boot + JPA 的實例講解。
本篇演示一個 Spring Boot 商品管理項目實例,其中數據持久層操作采用 JPA ,以體會 JPA 的優雅與高效。
2. JPA 基本原理
在開始實例之前,還是有必要聊聊 JPA 是如何實現的,便于大家理解。
首先是 ORM 映射,通過注解或 XML 描述對象和表直接的映射關系。例如 GoodsDo 商品類對應數據庫中的 goods 商品表,商品類里面的屬性和商品表里面的列一一對應,商品類的一個對象就對應商品表中的一行數據。
然后就是對數據庫進行 CRUD (增刪改查)操作了,由于已經配置了對象和表的映射關系,所以可以自動生成對應的 SQL 語句,然后執行語句即可。
3. 開發流程
光說不練那是假把式,我們來使用 Spring Boot + JPA 開發一個完整實例。
3.1 使用 Spring Initializr 創建項目
Spring Boot 版本選擇 2.2.5 ,Group 為 com.imooc
, Artifact 為 spring-boot-jpa
,生成項目后導入 Eclipse 開發環境。
3.2 引入項目依賴
我們引入 Web 項目依賴、熱部署依賴。由于本項目需要使用 JPA 訪問數據庫,所以引入 spring-boot-starter-jdbc
、 mysql-connector-java
和 spring-boot-starter-data-jpa
依賴。 pom.xml 文件中依賴項如下:
實例:
<!-- 熱部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- myql驅動 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
3.3 修改配置文件
在 application.properties
中添加以下配置:
實例:
# 配置數據庫驅動
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# 配置數據庫url
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/shop?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
# 配置數據庫用戶名
spring.datasource.username=root
# 配置數據庫密碼
spring.datasource.password=Easy@0122
# 啟動時更新表結構,保留數據
spring.jpa.hibernate.ddl-auto=update
此處需要注意的是 spring.jpa.hibernate.ddl-auto=update
。可以理解項目啟動時,根據實體類結構更新數據庫表結構,且保留數據庫中的數據。
3.4 開發商品類
開發商品類 GoodsDo ,并通過注解實現類結構與數據表結構的映射。
實例:
/**
* 商品類
*/
@Entity // 表示這是一個數據對象類
@Table(name = "goods") // 對應數據庫中的goods表
public class GoodsDo {
/**
* 商品id
*/
@Id // 該字段對應數據庫中的列為主鍵
@GeneratedValue(strategy = GenerationType.IDENTITY) // 主鍵自增長
@Column(name = "id") // 對應goods表中的id列
private Long id;
/**
* 商品名稱
*/
@Column(name = "name") // 對應goods表中的name列
private String name;
/**
* 商品價格
*/
@Column(name = "price") // 對應goods表中的price列
private String price;
/**
* 商品圖片
*/
@Column(name = "pic") // 對應goods表中的pic列
private String pic;
// 省略get set方法
}
3.5 開發數據操作接口
開發商品數據接口,代碼如下:
實例:
/**
* 商品數據操作接口
*/
@Repository
public interface IGoodsDao extends CrudRepository<GoodsDo, Long> {
}
解釋下,@Repository
將接口標注為數據訪問層組件,該接口通過繼承 CrudRepository
實現 CRUD 操作。泛型參數分別為實體類及主鍵的數據類型。
注意此時已經可以通過 IGoodsDao 對數據庫 goods 表進行增刪改查操作了。
3.6 開發服務層
開發 Goods Service ,注入 IGoodsDao 類型組件實現服務方法。
實例:
/**
* 商品服務類
*/
@Service
public class GoodsService {
@Autowired
private IGoodsDao goodsDao;
/**
* 新增商品
*/
public void add(GoodsDo goods) {
goodsDao.save(goods);
}
/**
* 刪除商品
*/
public void remove(Long id) {
goodsDao.deleteById(id);
}
/**
* 編輯商品信息
*/
public void edit(GoodsDo goods) {
goodsDao.save(goods);
}
/**
* 按id獲取商品信息
*/
public Optional<GoodsDo> getById(Long id) {
return goodsDao.findById(id);
}
/**
* 獲取商品信息列表
*/
public Iterable<GoodsDo> getList() {
return goodsDao.findAll();
}
}
此處需要解釋下 Optional
類,它是一個包裝類。它的內容是空或者包含的對象,所以可以避免空指針問題。此處稍作了解即可。
3.7 開發控制器
我們還是遵循 RESTful 風格,開發控制器類。
實例:
/**
* 商品控制器類
*/
@RestController
public class GoodsController {
@Autowired
private GoodsService goodsService;
/**
* 按id獲取商品信息
*/
@GetMapping("/goods/{id}")
public Optional<GoodsDo> getOne(@PathVariable("id") long id) {
return goodsService.getById(id);
}
/**
* 獲取商品列表
*/
@GetMapping("/goods")
public Iterable<GoodsDo> getList() {
return goodsService.getList();
}
/**
* 新增商品
*/
@PostMapping("/goods")
public void add(@RequestBody GoodsDo goods) {
goodsService.add(goods);
}
/**
* 編輯商品
*/
@PutMapping("/goods/{id}")
public void update(@PathVariable("id") long id, @RequestBody GoodsDo goods) {
// 修改指定id的博客信息
goods.setId(id);
goodsService.edit(goods);
}
/**
* 移除商品
*/
@DeleteMapping("/goods/{id}")
public void delete(@PathVariable("id") long id) {
goodsService.remove(id);
}
}
4. 測試
我們主要是測試 JPA 模塊正確可用,所以直接在測試類發起對 IGoodsDao 方法的測試即可。
4.1 新增測試
首先我們建立數據庫 shop ,數據庫中不必有表 goods ,如果有 goods 表的話可以將它刪除。因為我們設置了 spring.jpa.hibernate.ddl-auto=update
, JPA 會在項目啟動時自動建立表結構。
實例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class JpaAddTest {
@Autowired
private IGoodsDao goodsDao;
/**
* 新增測試
*/
@Test
public void testAdd() {
GoodsDo goods = new GoodsDo();
goods.setName("梨張");
goods.setPic("梨圖片");
goods.setPrice("2.0");
GoodsDo result = goodsDao.save(goods);
System.out.println("新增商品id:" + result.getId());
assertNotNull(result);
}
}
運行測試類,控制臺輸出新增商品id:1
,說明插入一條數據成功,且插入數據 id 為 1 。
同時查看數據庫,發現已經自動構建表結構:
4.2 修改測試
當調用 save 方法,如果給參數中 id 屬性賦值,則會進行數據更新操作。
實例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class JpaEditTest {
@Autowired
private IGoodsDao goodsDao;
/**
* 修改測試
*/
@Test
public void testEdit() {
GoodsDo goods = new GoodsDo();
goods.setId(1L);
goods.setName("梨張");
goods.setPic("梨圖片");
goods.setPrice("100.0");
GoodsDo result = goodsDao.save(goods);
assertNotNull(result);
}
}
此時查看數據庫中數據,發現金額已修改成功。
4.3 查詢測試
我們進行按 id 查詢、查詢所有操作,并打印查詢結果。
實例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class JpaQueryTest {
@Autowired
private IGoodsDao goodsDao;
/**
* 按id查詢
*/
@Test
public void testQueryById() {
Optional<GoodsDo> goodsOptional = goodsDao.findById(1L);
GoodsDo goods = goodsOptional.get();
System.out.println(goods.getId() + "-" + goods.getName() + "-" + goods.getPic() + "-" + goods.getPrice());
}
/**
* 查詢全部
*/
@Test
public void testQueryAll() {
Iterable<GoodsDo> goodsIt = goodsDao.findAll();
for (GoodsDo goods : goodsIt) {
System.out.println(goods.getId() + "-" + goods.getName() + "-" + goods.getPic() + "-" + goods.getPrice());
}
}
}
4.4 刪除測試
指定刪除 id 為 1 的商品。
實例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class JpaRemoveTest {
@Autowired
private IGoodsDao goodsDao;
/**
* 刪除測試
*/
@Test
public void testRemove() {
goodsDao.deleteById(1L);
}
}
運行后,數據庫中商品信息被刪除,大功告成!
5. 小結
使用 JPA 后,最大的好處就是不用寫 SQL 了,完全面向對象編程,簡潔又省心,何樂而不為。