SpringBoot整合JPA
使用數據庫是開發基本應用的基礎,借助于開發框架,我們已經不用編寫原始的訪問數據庫的代碼,也不用調用JDBC(Java Data Base Connectivity)或者連接池等諸如此類的被稱作底層的代碼,我們將從更高的層次上訪問數據庫,這在Springboot中更是如此,本章我們將詳細介紹在Springboot中使用 Spring Data JPA 來實現對數據庫的操作。
JPA & Spring Data JPA
JPA是Java Persistence API的簡稱,中文名Java持久層API,是Sun官方提出的Java持久化規范.
其設計目標主要是為了簡化現有的持久化開發工作和整合ORM技術。
JPA使用XML文件或注解(JDK 5.0或更高版本)來描述對象-關聯表的映射關系,能夠將運行期的實體對象持久化到數據庫,它為Java開發人員提供了一種ORM工具來管理Java應用中的關系數據。
簡單地說,JPA就是為POJO(Plain Ordinary Java Object)提供持久化的標準規范,即將Java的普通對象通過對象關系映射(Object-Relational Mapping,ORM)持久化到數據庫中。
由于JPA是在充分吸收了現有Hibernate,TopLink,JDO等ORM框架的基礎上發展而來的,因而具有易于使用、伸縮性強等優點。
Spring Data JPA 是 Spring 基于 Spring Data 框架、在JPA 規范的基礎上開發的一個框架,使用 Spring Data JPA 可以極大地簡化JPA 的寫法,可以在幾乎不用寫實現的情況下實現對數據庫的訪問和操作,除了CRUD外,還包括分頁和排序等一些常用的功能。
Spring Data JPA 還提供了對分頁查詢、自定義SQL、查詢指定N條記錄、聯表查詢等功能的支持
JPA不是一種新的ORM框架,它的出現只是用于規范現有的ORM技術,它不能取代現有的Hibernate、TopLink等框架。相反,在采用JPA開發時,我們將仍將使用到這些ORM框架,只是此時開發出來的應用不再依賴于某個持久化提供商。應用可以在不修改代碼的情況下在任何JPA環境下運行,真正做到低耦合,可擴展的程序設計。
Hibernate & JPA
1、JPA
全稱Java Persistence API,通過JDK 5.0注解或XML描述對象-關系表的映射關系,并將運行期的實體對象持久化到數據庫中。
JPA的出現有兩個原因:
其一,簡化現有Java EE和Java SE應用的對象持久化的開發工作;
其二,Sun希望整合對ORM技術,實現持久化領域的統一。
JPA提供的技術:
1)ORM映射元數據:JPA支持XML和JDK 5.0注解兩種元數據的形式,元數據描述對象和表之間的映射關系,框架據此將實體對象持久化到數據庫表中;
2)JPA 的API:用來操作實體對象,執行CRUD操作,框架在后臺替我們完成所有的事情,開發者從繁瑣的JDBC和SQL代碼中解脫出來。
3)查詢語言:通過面向對象而非面向數據庫的查詢語言查詢數據,避免程序的SQL語句緊密耦合。
2、JPA & Hibernate 關系
JPA是規范,Hibernate是框架,JPA是持久化規范,而Hibernate實現了JPA。
Hibernate VS Mybatis
Mybatis:小巧、方便、高效、簡單、直接、半自動
Hibernate:強大、方便、高效、復雜、繞彎子、全自動
一、導入依賴
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
< dependencies > < dependency > < groupId >com.alibaba</ groupId > < artifactId >fastjson</ artifactId > < version >1.2.76</ version > </ dependency > < dependency > < groupId >org.projectlombok</ groupId > < artifactId >lombok</ artifactId > </ dependency > < dependency > < groupId >com.alibaba</ groupId > < artifactId >druid</ artifactId > < version >1.1.21</ version > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-data-jpa</ artifactId > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-web</ artifactId > </ dependency > < dependency > < groupId >mysql</ groupId > < artifactId >mysql-connector-java</ artifactId > < scope >runtime</ scope > </ dependency > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-test</ artifactId > < scope >test</ scope > </ dependency > </ dependencies > |
二、簡單的CRUD
1、配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
spring: datasource: username: root password: root url: jdbc:mysql://localhost:3306/shy driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource jpa: hibernate: #定義數據庫表的生成策略 create 創建一個表 update 更新或者創建數據表 ddl-auto: update #控制臺顯示sql語句 show-sql: true server: port: 80 |
其中,spring.jpa.hibernate.ddl-auto 參數用來配置是否開啟自動更新數據庫表結構,可取create、create-drop、update、validate、none五個值。
- create 每次加載hibernate時,先刪除已存在的數據庫表結構再重新生成;
- create-drop 每次加載hibernate時,先刪除已存在的數據庫表結構再重新生成,并且當 sessionFactory關閉時自動刪除生成的數據庫表結構;
- update 只在第一次加載hibernate時自動生成數據庫表結構,以后再次加載hibernate時根據model類自動更新表結構;
- validate 每次加載hibernate時,驗證數據庫表結構,只會和數據庫中的表進行比較,不會創建新表,但是會插入新值。
- none 關閉自動更新
2、實體類
Shop:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package com.shy.entity; import lombok.Data; import javax.persistence.*; @Data //使用jpa注解 配置映射關系 @Entity //告訴jpa這是一個實體類 和數據表映射的類 public class Shop{ @Id //表明這是以一個主鍵 @GeneratedValue (strategy = GenerationType.IDENTITY) //自增 private Integer shopId; @Column (length = 20 ) private String shopName; private double price; private Integer shopClassId; private Integer num; } |
ShopClass,商品類別
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package com.shy.entity; import lombok.Data; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Data @Entity public class ShopClass { @Id @GeneratedValue (strategy = GenerationType.IDENTITY) private Integer shopClassId; private String shopClassName; } |
3、Dao層
編寫dao繼承JpaRepository類,泛型傳入 要操作的實體類,和主鍵類型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package com.example.dao; import com.shy.entity.Shop; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; /* 參數一 T :當前需要映射的實體 * 參數二 ID :當前映射的實體中的OID(映射對象標識符,數據庫主鍵)的類型*/ @Repository public interface ShopRepository extends JpaRepository<Shop,Integer> { /* * 我們在這里直接繼承 JpaRepository * 這里面已經有很多現成的方法了 * 這也是JPA的一大優點 * 我們可以直接使用這些方法,包括其父類的好多方法。 * */ } |
JpaRepository:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package org.springframework.data.jpa.repository; import java.util.List; import javax.persistence.EntityManager; import org.springframework.data.domain.Example; import org.springframework.data.domain.Sort; import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.QueryByExampleExecutor; @NoRepositoryBean public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> { @Override List<T> findAll(); // 查詢所有實體 @Override List<T> findAll(Sort sort); // 查詢所有實體并排序 @Override List<T> findAllById(Iterable<ID> ids); // 根據ID集合查詢實體 @Override <S extends T> List<S> saveAll(Iterable<S> entities); // 保存并返回(修改后的)實體集合 void flush(); // 提交事務 <S extends T> S saveAndFlush(S entity); // 保存實體并立即提交事務 <S extends T> List<S> saveAllAndFlush(Iterable<S> entities); @Deprecated default void deleteInBatch(Iterable<T> entities){deleteAllInBatch(entities);} void deleteAllInBatch(Iterable<T> entities); // 批量刪除實體集合 void deleteAllByIdInBatch(Iterable<ID> ids); void deleteAllInBatch(); // 批量刪除所有實體 @Deprecated T getOne(ID id); // 根據ID查詢實體 T getById(ID id); @Override <S extends T> List<S> findAll(Example<S> example); // 查詢與指定Example匹配的所有實體 @Override <S extends T> List<S> findAll(Example<S> example, Sort sort); // 查詢與指定Example匹配的所有實體并排序 } |
4、service層
service:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package com.shy.service; import com.shy.entity.Shop; import com.shy.vo.ShopAndShopClassVo; import java.util.List; public interface ShopService { //查詢所有商品 List<Shop> findAll(); //增加商品 Shop addShop(Shop shop); //通過商品id修改商品名 Shop updateShop(); //通過商品id刪除商品 void delShop(Integer id); } |
Impl:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
package com.shy.service; import com.shy.dao.ShopRepository; import com.shy.entity.Shop; import com.shy.vo.ShopAndShopClassVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import java.util.List; @Service public class ShopServiceImpl implements ShopService{ @Autowired private ShopRepository shopRepository; @Override public List<Shop> findAll() { return shopRepository.findAll(); } @Override public Shop addShop(Shop shop) { shop.setPrice( 333 ); shop.setShopClassId( 3 ); shop.setNum( 30 ); shop.setShopName( "耳機" ); return shopRepository.save(shop); } @Override public Shop updateShop() { Shop shop = new Shop(); shop.setShopId( 11 ); shop.setShopName( "平板" ); shop.setShopClassId( 3 ); shop.setNum( 40 ); return shopRepository.save(shop); } @Override public void delShop(Integer id) { shopRepository.deleteById(id); System.out.println( "刪除成功" ); } } |
5、controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package com.shy.controller; import com.alibaba.fastjson.JSON; import com.shy.entity.Shop; import com.shy.service.ShopService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController public class ShopController { @Autowired private ShopService shopService; @GetMapping ( "/list" ) public String findAll(){ return JSON.toJSONString(shopService.findAll()); } @GetMapping ( "/save" ) public String save(Shop shop){ return JSON.toJSONString(shopService.addShop(shop)); } @GetMapping ( "/saveAndFlush" ) public String saveAndFlush(){ return JSON.toJSONString(shopService.updateShop()); } @GetMapping ( "/delShop/{id}" ) public void delShop( @PathVariable Integer id){ shopService.delShop(id); } } |
全程用postman測試
三、自定義SQL
在ShopRepository中添加
1
2
3
4
5
6
7
8
9
|
//使用原生sql需要加上,nativeQuery = true //?1代表第一個參數 @Query (value= "select * from shop where shop.price = ?1" ,nativeQuery = true ) Shop findByPrice(Double price); //修改商品,@Modifying+@Query執行更新操作,serviceImpl不要忘記加上,要使用hql的話,需要把entity別名刪掉 @Transactional //設計修改表的操作需要開啟事務支持 @Modifying //這個注解只支持返回值為int/Integer @Query ( "update Shop s set s.shopName = ?1 where s.shopId = ?2" ) int updateshop2(String name,Integer shopId); |
service:
1
2
3
4
|
//通過價格查詢商品 Shop findByPrice(Double price); //修改商品名原生sql方法 int updateshop2(String name,Integer shopId); |
Impl:
1
2
3
4
5
6
7
8
|
@Override public Shop findByPrice(Double price) { return shopRepository.findByPrice(price); } @Override public int updateshop2(String name, Integer shopId) { return shopRepository.updateshop2(name, shopId); } |
controller:
1
2
3
4
5
6
7
8
9
|
@GetMapping ( "/listPrice/{price}" ) public String findByPrice( @PathVariable Double price){ return JSON.toJSONString(shopService.findByPrice(price)); } @GetMapping ( "/saveAndFlush2/{id}/{name}" ) public String saveAndFlush2( @PathVariable (value = "id" ) Integer shopId, @PathVariable String name){ return shopService.updateshop2(name, shopId)> 0 ? "修改成功" : "修改失敗" ; } |
四、分頁查詢
Pageable 是 Spring 封裝的分頁實現類,使用的時候需要傳入頁數、每頁條數和排序規則。
Spring Data JPA 已經幫我們內置了分頁功能,在查詢的方法中,需要傳入參數 Pageable,當查詢中有多個參數的時候 Pageable 建議作為最后一個參數傳入,
Pageable使用的時候需要傳入頁數、每頁條數和排序規則,排序規則可省略
service:
1
2
3
4
5
6
7
8
|
/** * 分頁查詢 * @param pageNo 第幾頁 * @param pageSize 每頁有多少條數據 * @param pageable Spring 封裝的分頁實現類 * @return 數據 */ Page<Shop> pageShop(Integer pageNo,Integer pageSize,Pageable pageable); |
Impl:
1
2
3
4
5
6
7
|
@Override public Page<Shop> pageShop(Integer pageNo, Integer pageSize, Pageable pageable) { //注意排序這找的是實體類中的字段,不是數據庫里的 //分頁頁碼從0開始 pageable = PageRequest.of(pageNo,pageSize, Sort.Direction.DESC, "shopId" ); return shopRepository.findAll(pageable); } |
controller:
1
2
3
4
5
|
@GetMapping ( "/pageShop/{pageNo}/{pageSize}" ) public String pageShop( @PathVariable Integer pageNo, @PathVariable Integer pageSize,Pageable pageable){ return JSON.toJSONString(shopService.pageShop(pageNo, pageSize, pageable)); } |
五、連表查詢
VO(value object)值對象
- 通常用于業務層之間的數據傳遞,僅僅包含數據而已
- View object:視圖對象
接受頁面傳遞來的對象,封裝對象
將業務處理完成的對象,封裝成頁面要用的數據
創建ShopAndShopClassVo用來接收連表查詢后的結果
entity,這里的有參構造必須加,要往里賦值
1
2
3
4
5
6
7
8
9
10
11
12
|
package com.shy.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class ShopAndShopClassVo { private String shopName; private double price; private String shopClassName; } |
ShopRepository:
1
2
3
4
|
//連表查詢 @Query ( "select new com.shy.vo.ShopAndShopClassVo(s.shopName,s.price,sc.shopClassName) from Shop s,ShopClass sc " + "where s.shopClassId=sc.shopClassId and sc.shopClassName=?1" ) List<ShopAndShopClassVo> findShopInfo(String shopClassName); |
JPQL進行查詢,它的特征就是與原生SQL語句類似,完全面向對象,通過類名和屬性訪問,而不是表名和表屬性
service:
1
2
|
//連表查詢 List<ShopAndShopClassVo> findShopInfo(String shopClassName); |
Impl:
1
2
3
4
|
@Override public List<ShopAndShopClassVo> findShopInfo(String shopClassName) { return shopRepository.findShopInfo(shopClassName); } |
controller:
1
2
3
4
|
@GetMapping ( "/andFind/{name}" ) public String findShopInfo( @PathVariable ( "name" ) String shopClassName){ return JSON.toJSONString(shopService.findShopInfo(shopClassName)); } |
六、分組查詢
接收數據的另一種寫法,創建一個結果集的接口來接收連表查詢后的結果
定義一個結果集的接口類,接口類的內容來自于商品表和商品類別表。
entity:
1
2
3
4
5
|
package com.shy.entity; public interface GroupShop { String getNum(); String getShopClassName(); } |
在運行中 Spring 會給接口(GroupShop)自動生產一個代理類來接收返回的結果,代碼中使用 getXX 的形式來獲取。
ShopRepository:
1
2
3
|
//分組查詢 @Query ( "select count(s.shopName) as 商品數量,sc.shopClassName as 類別名稱 from Shop s,ShopClass sc where s.shopClassId=sc.shopClassId group by s.shopClassId" ) List<GroupShop> groupShop(); |
service:
1
2
|
//分組查詢 List<GroupShop> groupShop(); |
Impl:
1
2
3
4
|
@Override public List<GroupShop> groupShop() { return shopRepository.groupShop(); } |
controller:
1
2
3
4
|
@GetMapping ( "/groupShop" ) public String groupShop(){ return JSON.toJSONString(shopService.groupShop()); } |
七、與mybatis對比
- jpa是對象與對象之間的映射,而mybatis是對象和結果集的映射
- jpa移植性比較好,不用關心用什么數據庫,因為mybatis自由寫sql語句,所以當項目移植的時候還需要改sql。
- 修改字段時JPA更簡單,mybatis需要修改一堆的xml,mapper等文件很麻煩。
- mybatis自定義sql,比較靈活,也可以寫復雜的sql,JPA只適合簡單的單表sql
總結
mybatis和JPA各有優勢,如果sql簡單,則jpa使用效率更高,如果sql較復雜,需要自定義,則使用mybatis更加順手。
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關注服務器之家的更多內容!
原文鏈接:https://blog.csdn.net/CSDN_SHY/article/details/117923206