[java學習筆記]spring-data jpa 的簡單使用


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>
View Code

數據庫中的建表語句:

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;
View Code

以一個商品類別表演示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);
    }

}
View Code

對於查詢,單個的查詢有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;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM