springboot體系中一個持久層框架,只需要定義好實體類和接口,便可以調用相應的方法對數據庫進行基本的增刪查改的工作,比起mybatis,不需要寫配置文件,sql語句即可完成對數據庫的操作;
對於jpa的基本操作:
首先引入依賴,建立springboot工程:

<dependencies> <!--spring jpa依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!--mysql依賴--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--lombook--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--測試依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
數據庫中的建表語句:

CREATE TABLE `product_category` ( `category_id` int(11) NOT NULL AUTO_INCREMENT, `category_name` varchar(64) NOT NULL COMMENT '類目名字', `category_type` int(11) NOT NULL COMMENT '類目編號', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改時間', PRIMARY KEY (`category_id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;
以一個商品類別表演示jpa的操作;
建立對應的實體類:
import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; import org.hibernate.annotations.DynamicUpdate; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import java.util.Date; @Entity // @Table(name = "product_category") 駝峰模式自動映射,如果不是可以使用這個注解,javax.persistence包下的 @DynamicUpdate // 動態更新,對於由數據庫維護的例如更新時間的自動更新,如果屬性中有更新時間對應的字段,那么更新 // 時就需要手動更新而使數據庫的自動更新生效,增加上這個注解,可以使得該效果生效,亦即在有更新時間字段時也能自動更新該字段 @Data @ToString @NoArgsConstructor public class ProductCategory { // 主鍵 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增類型的主鍵 // @Column(name = "category_id") 列和屬性的映射,駝峰模式自動映射,如果不是需要額外設置 private Integer categoryId; // 類別名稱 private String categoryName; // 類別編號 private Integer categoryType; private Date updateTime; private Date createTime; }
需要注意的是:
類名和表明的映射關系,默認是駝峰模式,即對於數據庫表名:多個單詞之間使用'_'連接,對於類名而言:多個單詞直接並列寫,每個單詞首字母都大寫,這樣的命名是可以自動映射的,否則需要增加table注解來指定對應的表名,@DynamicUpdate動態更新注解,一般來說不需要,對於上述例子,更新時間這個屬性在數據庫中自動維護,如果在調用save函數時,updateTime這個屬性傳值了,那么在沒有添加@DynamicUpdate注解的情況下,最終更新在數據庫中的update_time這個列的值便是手動傳入的值,如果加上的話,結果便是數據庫自動維護當前時間的值;
java屬性和數據庫列名的映射:針對默認的駝峰模式的命名方法可以進行自動的映射,當然,也可以通過@Column注解來手動提供映射關系;
主鍵的注解@Id,聲明該屬性對應的字段為主鍵,可以添加注解@GeneratedValue,這個注解指定主鍵自動生成的策略,在這個模式下使用的是自增;
定義持久層接口:
import cn.ly.domain.ProductCategory; import org.springframework.data.jpa.repository.JpaRepository; public interface ProductCategoryRepository extends JpaRepository<ProductCategory,Integer> { }
關於這個接口的說明:需要繼承JpaRepository類,兩個泛型:第一個為實體類,第二個為主鍵類型;
將接口注入到spring容器中,就可以直接調用基本的增刪改查的方法了:

@SpringBootTest @RunWith(SpringRunner.class) public class ProductCategoryRepositoryTest { @Autowired ProductCategoryRepository repository; @Test public void testquery() { // 查找全部 List<ProductCategory> all = repository.findAll(); System.out.println(all); } @Test public void testqueryone() { // 查找單個 Optional<ProductCategory> optional = repository.findById(1); if (optional.isPresent()) { ProductCategory productCategory = optional.get(); System.out.println(productCategory); } } @Test public void add() { ProductCategory productCategory = new ProductCategory(); productCategory.setCategoryName("新增類目"); productCategory.setCategoryType(10); // 新增,主鍵有jpa自動生成,創建時間和更新時間由數據庫管理 // 返回的對象包含了自動生成的主鍵的信息; ProductCategory save = repository.save(productCategory); System.out.println(save); } @Test public void edit() { // 更新,和新增調用相同的函數,在添加了主鍵的情況下,會修改對應的值 // 修改主鍵為6的信息 // 先查詢 Optional<ProductCategory> optional = repository.findById(6); if (optional.isPresent()) { ProductCategory productCategory = optional.get(); // 修改 productCategory.setCategoryName("新增類目修改"); ProductCategory save = repository.save(productCategory); } } @Test public void delete() { // 刪除 repository.deleteById(6); } }
對於查詢,單個的查詢有findone和findById兩個方法,后者返回一個optional的對象,這個對象的存在主要是提醒程序員進行空的檢查,避免空指針異常;
對於修改:先根據id查詢的意義在於首先獲得一個可用的,包含了該條記錄的所有信息的對象,然后對希望修改的屬性進行單獨的設值,最后儲存進去;在屬性較多,只希望修改某些部分的屬性時,推薦采用這樣的流程修改數據,以免改了不希望修改的屬性;
對於刪除:這里調用的是根據id刪除的方法,還有一個delete方法,參數為一個希望刪除的對象;
對於分頁和排序:
對於分頁和排序,所要做是調用包含了參數Pagealbe的搜索方法:
@Test
public void queryPage() {
// 排序和分頁,需要傳遞Pageable和Sort
Pageable pageable = PageRequest.of(0, 10, Sort.by(Sort.Direction.DESC, "updateTime"));
Page<ProductCategory> all = repository.findAll(pageable);
// 總的數量
long totalElements = all.getTotalElements();
// 總的頁數
int totalPages = all.getTotalPages();
// 內容
List<ProductCategory> content = all.getContent();
System.out.println(all);
}
對於findAll的重載參數有Pageable和Sort兩種類型,前者可以進行分頁,后者則是排序,如果需要同時分頁和排序的話則要采用Pageable的重載類型
首先說分頁:pageable的構建函數中,第一個參數為頁碼,第二個參數為每頁的數據多少,這個頁碼是從0開始計數的;如果需要排序,則傳遞第三個參數Sort,這個對象的構建函數的參數:第一個指定了是順序還是倒敘,第二個參數希望按照的排序屬性,是個可變參數, 可以傳遞多個值;返回的page類型
如果只需要排序,不分頁的話,將sort對象作為參數直接傳遞到findAll方法內即可;
條件查詢:
可以采用這一組方法:
例子如下:
@Test public void queryExam() { // 確定條件 ProductCategory productCategory = new ProductCategory(); productCategory.setCategoryName("銷"); // 創建匹配的模式 ExampleMatcher matcher = ExampleMatcher.matching(); matcher = matcher.withMatcher("categoryName", ExampleMatcher.GenericPropertyMatchers.contains()); // 創建樣本 Example<ProductCategory> example = Example.of(productCategory, matcher); List<ProductCategory> all = repository.findAll(example); System.out.println(all); }
對於這段代碼的說明:首先創建一個實體類,實體類包含的信息即為要查詢的條件,然后要根據屬性設置匹配模式,這里設置的是categoryName只要包含條件中的子符串即可被查到,多個屬性配置的話可以重復調用withMatcher方法即可;
自定義方法
可以通過自定義一些符合命名規范的方法來進行特殊的查詢:
List<ProductCategory> findByUpdateTimeBefore(Date updateTime);
Page<ProductCategory> findByUpdateTimeBefore(Date updateTime, Pageable pageable);
方法定義在之前創建的ProductCategoryRepository接口中,命名為findByxxxxAndxxxx ,xxxx為屬性名,后面可以跟着before,in等字段做條件查詢,如果需要分頁,則返回為Page類型的值,同時參數額外傳遞Pageable; 這些方法可以直接使用,不需要自己寫實現;
還可以自己寫sql:
public interface ProductInfoRepository extends JpaRepository<ProductInfo,String> { List<ProductInfo> findByProductStatus(Integer status); @Modifying @Query("update ProductInfo p set p.productStock = p.productStock-:desStore where p.productId=:productId and p.productStock>=:desStore") int decreaseStore(@Param("productId") String productId, @Param("desStore") Integer desStore); @Modifying @Query("update ProductInfo p set p.productStock = p.productStock+:num where p.productId=:productId") int increaseStore(@Param("productId") String productId, @Param("num") Integer num); }
注意一點:這里的sql語句寫的都是java類的名字以及屬性名,參數使用“:參數名”的形式傳遞;
這樣的方法也是可以直接調用的;
總結:
jpa是非常方便的操作數據庫的框架,針對單表的開發比mybatis要方便;對於如何選擇的問題,個人看法:單表操作用jpa,多表復雜的查詢操作使用mybatis;