SpringBoot中JPA的學習

SpringBoot中JPA的學習

 

準備環境和項目配置

  寫一下學習JPA的過程,主要是結合以前SpringBoot + Vue的項目和網上的博客學習一下。java

  首先,須要配置一下maven文件,有這麼兩個依賴:mysql

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>6.0.6</version>
</dependency>

  而後是application中的配置問題,JPA有這麼一些常見的參數:spring

  spring.jpa.show-sql 配置在日誌中打印出執行的 SQL 語句信息。sql

  spring.jpa.hibernate.ddl-auto配置了實體類維護數據庫表結構的具體行爲,update表示當實體類的屬性發生變化時,表結構跟着更新,也能夠取值create,create表示啓動的時候刪除上一次生成的表,並根據實體類從新生成表,這個時候以前表中的數據就會被清空;還能夠取值create-drop,這個表示啓動時根據實體類生成表,可是當sessionFactory關閉的時候表會被刪除;validate表示啓動時驗證明體類和數據表是否一致;none則什麼都不作。數據庫

  spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect 。在 SrpingBoot 2.0 版本中,Hibernate 建立數據表的時候,默認的數據庫存儲引擎選擇的是 MyISAM (以前好像是 InnoDB,這點比較詭異)。這個參數是在建表的時候,將默認的存儲引擎切換爲 InnoDB 用的。json

  spring.jackson.serialization.indent_output=true表示格式化輸出的json字符串,方便查看。segmentfault

 

定義數據實體類

  在這個項目裏,數據實體類的定義方式大同小異:後端

import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import javax.persistence.*; @Entity @Table(name = "book") @JsonIgnoreProperties({"handler","hibernateLazyInitializer"}) public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") int id; //把 category 對象的 id 屬性做爲 cid 進行了查詢
 @ManyToOne @JoinColumn(name="cid") private Category category; String cover; String title; String author; String date; String press; String abs; public Category getCategory() { return category; } public void setCategory(Category category) { this.category = category; } public String getDate() { return date; } public void setDate(String date) { this.date = date; } ... }

  下面分別寫一下這些註釋的做用以及和其餘部分關聯:session

  @Entity 是一個必選的註解,聲明這個類對應了一個數據庫表。app

  @Table(name = "book") 是一個可選的註解。聲明瞭數據庫實體對應的表信息。包括表名稱、索引信息等。這裏聲明這個實體類對應的表名是 book。若是沒有指定,則表名和實體的名稱保持一致。

  能夠看一下對應的在MySQL中的數據表:

  

  其中8個屬性分別表示序號、封面(存放圖牀的url或者本地的url地址)、標題、做者、出版日期、出版社、簡介、表示類別的外鍵。

  @JsonIgnoreProperties由於是作先後端分離,而先後端數據交互用的是 json 格式。那麼對象就會被轉換爲 json 數據。而本項目使用 jpa 來作實體類的持久化,jpa 默認會使用 hibernate,在 jpa 工做過程當中,就會創造代理類來繼承該類 ,並添加 handler 和 hibernateLazyInitializer 這兩個無須 json 化的屬性,因此這裏須要用 JsonIgnoreProperties 把這兩個屬性忽略掉。這裏我看好多博主都沒有寫,在後面會對這個註解多作一些測試相關的內容。

  @Id表示該字段是一個id  

  @GeneratedValue註解存在的意義主要就是爲一個實體生成一個惟一標識的主鍵、@GeneratedValue提供了主鍵的生成策略。@GeneratedValue註解有兩個屬性,分別是strategy和generator。generator屬性的值是一個字符串,默認爲"",其聲明瞭主鍵生成器的名稱。strategy屬性提供四種值:1.AUTO主鍵由程序控制, 是默認選項 ,不設置就是這個;2.IDENTITY 主鍵由數據庫生成, 採用數據庫自增加, Oracle不支持這種方;3.SEQUENCE 經過數據庫的序列產生主鍵, MYSQL不支持;4.Table提供特定的數據庫產生主鍵, 該方式更有利於數據庫的移植。須要注意的是不少博主將MySQL建表時設置自增屬性,這裏採用默認值表示自增。可是想運用到neo4j上可能要注意。

  @Column(length = 32) 用來聲明實體屬性的表字段的定義。默認的實體每一個屬性都對應了表的一個字段。字段的名稱默認和屬性名稱保持一致(並不必定相等)。字段的類型根據實體屬性類型自動推斷。這裏主要是聲明瞭字符字段的長度。若是不這麼聲明,則系統會採用 255 做爲該字段的長度。這裏的話我的感受原來博主的定義不夠嚴謹,應該是:

@Column(length = 20) String date;

  @JoinColumn 註解的做用:用來指定與所操做實體或實體集合相關聯的數據庫表中的列字段。因爲 @OneToOne(一對一)、@OneToMany(一對多)、@ManyToOne(多對一)、@ManyToMany(多對多) 等註解只能肯定實體之間幾對幾的關聯關係,它們並不能指定與實體相對應的數據庫表中的關聯字段,所以,須要與 @JoinColumn 註解來配合使用。咱們也能夠不寫@JoinColumn,Hibernate會自動生成一張中間表來進行綁定,一般並不推薦讓Hibernate自動去自動生成中間表,而是使用@JoinTable註解來指定中間表:  

  而後就是各個屬性的get和set方法,注意下屬性前面通常要加上private限制,可是這個博主沒有加,不太規範這裏。

 

實現持久層服務

public interface BookDAO extends JpaRepository<Book,Integer> { List<Book> findAllByCategory(Category category); /* 這個 findAllByTitleLikeOrAuthorLike,翻譯過來就是「根據標題或做者進行模糊查詢」, 參數是兩個 String,分別對應標題或做者。 記住這個寫法,我想固然的覺得是 findAllByTitleOrAuthorLike,只設置一個參數就行,結果瞎折騰了很久。 由於 DAO 裏是兩個參數,因此在 Service 裏把同一個參數寫了兩遍。 用戶在搜索時不管輸入的是做者仍是書名,都會對兩個字段進行匹配。 */ List<Book> findAllByTitleLikeOrAuthorLike(String keyword1, String keyword2); }

  聲明BookDAO接口,繼承JpaRepository,默認支持簡單的 CRUD 操做,很是方便。  

  注意這裏JpaRepository的第一個參數是剛剛定義的實體類,第二個參數是那個主鍵的類型,不少博客這裏是有問題的。  

  而後能夠放心的使用如下函數操做數據庫:

<S extends T> S save(S entity); <S extends T> Iterable<S> saveAll(Iterable<S> entities);( Optional<T> findById(ID id); boolean existsById(ID id); Iterable<T> findAll(); Iterable<T> findAllById(Iterable<ID> ids); long count(); void deleteById(ID id); void delete(T entity); void deleteAll(Iterable<? extends T> entities); void deleteAll();
  關於這裏JpaRepository的原理,能夠參考這篇博客的內容:  
  
  這裏寫一下自定義JPA對應的函數名:
 
Keyword Sample JPQL snippet
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is,Equals findByFirstnameIs,findByFirstnameEquals … where x.firstname = ?1
Between findByStartDateBetween … where x.startDate between ?1 and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age <= ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNull findByAgeIsNull … where x.age is null
IsNotNull,NotNull findByAge(Is)NotNull … where x.age not null
Like findByFirstnameLike … where x.firstname like ?1
NotLike findByFirstnameNotLike … where x.firstname not like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection<age> ages)</age> … where x.age in ?1
NotIn findByAgeNotIn(Collection<age> age)</age> … where x.age not in ?1
TRUE findByActiveTrue() … where x.active = true
FALSE findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)
相關文章
相關標籤/搜索