Spring Boot 2.x 之 Spring Data JPA, Hibernate 5


1. Spring Boot常用配置項

基於Spring Boot 2.0.6.RELEASE

1.1 配置屬性類

spring.jpa前綴的相關配置項定義在JpaProperties類中,

1.2 自動裝配類

涉及到的自動配置類包括:JpaBaseConfigurationHibernateJpaAutoConfiguration

1.3 常用配置項

# 是否開啟JPA Repositories,缺省: true
spring.data.jpa.repositories.enabled=true

# JPA數據庫類型,默認可以自動檢測,也能通過設置spring.jpa.database-platform達到同樣效果
spring.jpa.database=ORACLE

# 數據庫平台,常見的值如:
# org.hibernate.dialect.Oracle10gDialect
# org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect

# 是否使用JPA初始化數據庫,可以在啟動時生成DDL創建數據庫表,缺省為false
spring.jpa.generate-ddl = false

# 更細粒度的控制JPA初始化數據庫特性,用來設定啟動時DDL操作的類型,下文有詳細介紹
# 內嵌數據庫 hsqldb, h2, derby的缺省值為create-drop
# 非內嵌數據庫的缺省值為none
spring.jpa.hibernate.ddl-auto = update

# Hibernate操作時顯示真實的SQL, 缺省:false
spring.jpa.show-sql = true

# Hibernate 5 隱含命名策略類的全限定名
spring.jpa.hibernate.naming.implicit-strategy= 

# Hibernate 5 物理命名策略類的全限定名
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl 

# Use Hibernate's newer IdentifierGenerator for AUTO, TABLE and SEQUENCE.
spring.jpa.hibernate.use-new-id-generator-mappings= 

# 額外設置JPA配置屬性,通常配置項是特定實現支持的,如Hibernate常有下面的幾條配置項
spring.jpa.properties.* = 

# 將SQL中的標識符(表名,列名等)全部使用引號括起來
spring.jpa.properties.hibernate.globally_quoted_identifiers=true

# 日志記錄執行的SQL
spring.jpa.properties.hibernate.show_sql = true

# 是否將格式化SQL日志
spring.jpa.properties.hibernate.format_sql = true

# 是否注冊OpenEntityManagerInViewInterceptor. 綁定JPA EntityManager 到請求線程中. 默認為: true.
spring.jpa.open-in-view=true

# Hibernate 4 命名策略的全類名,Hibernate 5不再適用
spring.jpa.hibernate.naming-strategy=

1.4 配置項 spring.jpa.database

配置項spring.jpa.database的值通常可以自動檢測,可取值包括了:

public enum Database {
	DEFAULT,
	DB2,
	DERBY,
	H2,
	HSQL,
	INFORMIX,
	MYSQL,
	ORACLE,
	POSTGRESQL,
	SQL_SERVER,
	SYBASE
}

1.5 配置項spring.jpa.generate-ddl 和 spring.jpa.hibernate.ddl-auto

配置項spring.jpa.generate-ddl管理是否開啟自動初始化Schema特性,

配置項spring.jpa.hibernate.ddl-auto 進一步控制啟動時初始化Schema的特性,有以下可選值:

  1. create, 啟動時刪除上一次生成的表,並根據實體類生成表,表中的數據將被清空;
  2. create-drop,啟動時根據實體類生成表,sessionFactory關閉時刪除表;
  3. update,啟動時根據實體類生成表,當實體的屬性變動時,表結構也會更新,開發階段可以使用該屬性;
  4. validate,啟動時驗證實體類和數據表是否一致,在數據結構穩定時可以采取該選項;
  5. none,不采取任何操作;

1.6 配置項hibernate.globally_quoted_identifiers

配置項hibernate.globally_quoted_identifiers用來在SQL中加入引號,以解決某些場景中SQL標識符(表名,列名等)出現了數據庫關鍵字的情形。

Oracle中,該屬性給標識符加雙引號,注意Oracle雙引號中的內容區分大小寫;

MySQL中,該屬性給標識符加反引號(沒錯,就是鍵盤數字1左面的那個`),注意默認情況下,MySQL的表名區分大小寫,但列名不區分大小寫,在反引號中也是如此。

2. 開發常用注解——表/實體類使用

2.1 @Entity

@Entity用在實體類上,表示這是一個和數據庫表映射的實體類。

2.2 @Table

@Table用來聲明實體類對應的表信息。包括表名稱、索引等。

2.3 @SQLDelete,

@SQLDelete用來自定義刪除語句,

如果不想使用JPA Repository自帶的刪除語句,就可以使用該注解重寫,如常見的軟刪除:

@Entity
@Table(name = "App")
@SQLDelete(sql = "Update App set isDeleted = 1 where id = ?")

@Where

@Where用來指定默認查詢條件,可以用來配合軟刪除。

@Entity
@Table(name = "App")
@SQLDelete(sql = "Update App set isDeleted = 1 where id = ?")
@Where(clause = "isDeleted = 0")

2.4 @MappedSuperclass

@MappedSuperclass用來定義若干表中公共的字段,將它們封裝在一個基類中,基類上使用該注解,則其子實體類將自動獲得父類中的字段映射關系,同時不需要額外為父類建立真實的表,也就是真正的表只對應@MappedSuperclass的子類。

2.5 @Inheritance

@MappedSuperclass指定了實體類之間是可以存在層次關系的,通過@Inheritance可以進一步管理層次關系,其取值InheritanceType有以下枚舉變量:

public enum InheritanceType {

    /** A single table per class hierarchy. */
    SINGLE_TABLE,

    /** A table per concrete entity class. */
    TABLE_PER_CLASS,

    /**
     * A strategy in which fields that are specific to a
     * subclass are mapped to a separate table than the fields
     * that are common to the parent class, and a join is
     * performed to instantiate the subclass.
     */
    JOINED
}

例如

父類聲明如下,意味着每個子實體類對應了一張表

@MappedSuperclass
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class BaseEntity 

子實體類復用父類聲明的域/字段:

@Entity
@Table(name="App")
public class AppEntity entends BaseEntity

3. 開發常用注解——字段/域使用

3.1 @Column

@Column聲明一個表字段與域/方法的映射,其屬性可以進一步指定是否可為NULL,是否唯一等細節。

3.2 @Id

@Id標注一個屬性,表示該屬性映射為數據庫的主鍵。

單獨使用@Id來標識實體類的單個域映射為數據庫表的主鍵,對於聯合主鍵而言,就要配合@IdClass使用。

  1. 定義一個(外部)主鍵類,主鍵類中聲明了實體類(目標表)聯合主鍵中的所有主屬性,主鍵類需實現Serializable接口;
  2. 在真正的實體類上使用@IdClass,以上一步定義的主鍵類作為參數;
  3. 實體類的主屬性必須和主鍵類中的主屬性完全對應(個數,類型,名稱);
  4. 在實體類的所有主屬性上面添加@Id注解;

示例

外部主鍵類:

public class RoleUserId implements Serializable {
    private Long roleId;
    private Long userId;
}

實體類:

@Entity
@Table(name = "TB_ROLE_USER")
@IdClass(RoleUserId.class)
public class RoleUserDO  {
    @Id
    private Long roleId;

    @Id
    private Long userId;

    // ...
}

3.3 @GeneratedValue

@GeneratedValue設置字段的自動生成方式。

strategy屬性取值為枚舉GenerationType

    /**
     * Indicates that the persistence provider must assign
     * primary keys for the entity using an underlying
     * database table to ensure uniqueness.
     */
    TABLE,

    /**
     * Indicates that the persistence provider must assign
     * primary keys for the entity using a database sequence.
     */
    SEQUENCE,

    /**
     * Indicates that the persistence provider must assign
     * primary keys for the entity using a database identity column.
     */
    IDENTITY,

    /**
     * Indicates that the persistence provider should pick an
     * appropriate strategy for the particular database. The
     * <code>AUTO</code> generation strategy may expect a database
     * resource to exist, or it may attempt to create one. A vendor
     * may provide documentation on how to create such resources
     * in the event that it does not support schema generation
     * or cannot create the schema resource at runtime.
     */
    AUTO

使用自增的數據庫列生成主鍵是一種常見的主鍵生成方式,MySQL支持Identity列。

Oracle 12c以后支持Identity Column(身份列),可以使用GenerationType.IDENTITY作為生成策略,此時無需再使用@SequenceGenerator指定對應序列。此前的Oracle通常使用序列實現自增字段,需要將生成策略指定為GenerationType.SEQUENCE並配合@SequenceGenerator(下文)使用。

Oracle 12c新增Identity Column的使用請閱讀:https://www.cnblogs.com/zyon/p/11067115.html

使用身份列生成自增主鍵時,在主鍵屬性上使用@Id@GeneratedValue(strategy = GenerationType.IDENTITY)

@Entity
public class Author {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", updatable = false, nullable = false)
    private Long id;
    
    // …
}

@GeneratedValue#generator屬性指定主鍵生成器的名稱,需要與@SequenceGenerator@TableGenerator的名稱對應起來。

3.4 @SequenceGenerator

如果使用序列方式生成主鍵,@SequenceGenerator指定自動生成主鍵時對應的序列(Sequence)。

  • @SequenceGenerator#name指定序列生成器的名稱,@GeneratedValue#generator通過引用該值與具體的序列生成器關聯,進而關聯具體的序列。
  • @SequenceGenerator#sequenceName指定自動生成主鍵時的物理序列名稱;
  • @SequenceGenerator#initialValue屬性指定序列初值;
  • @SequenceGenerator#allocationSize指定自增步長;

實例:

  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
  @SequenceGenerator(name = "sequence", sequenceName = "ID_SEQ", allocationSize = 1)
  @Column(name = "Id")
  private long id;

3.5 JPA大對象支持

常用相關注解

  • @Lob
  • @Basic

3.5.1 在字符串型域上用@Lob

@Lob 
private String description; 
  • oracle映射字段的類型是CLOBjava.sql.Clob, Character[], char[]java.lang.String 將被持久化為 Clob 類型。
  • mysql中映射字段的類型是longtext

3.5.2 在二進制等類型域上用@Lob

  • oracle中java.sql.Blob, Byte[], byte[]Serializable實現類將被持久化為 Blob 類型。
  • mysql中對應longblob

@Lob持久化為Blob或者Clob類型,根據get方法的返回值不同,自動進行ClobBlob的轉換。

3.5.3 大對象字段的延遲加載

@Lob
@Basic(fetch=FetchType.LAZY)
private Byte[] file;

因為大對象數據一般占用的內存空間比較大,所以通常使用延遲加載的方式:@Basic注解設置加載方式為FetchType.LAZY

問題

延遲加載是延遲到什么時候?這個問題一直沒有找到確切的解答。

4. 常用Repository開發

4.1 Repository接口

public interface Repository<T, ID>

最基礎的Repository接口,不提供任何操作方法。

4.2 CrudRepository接口

public interface CrudRepository<T, ID> extends Repository<T, ID>

提供CRUD基本操作的Repository接口。

4.3 PagingAndSortingRepository 接口

public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID>

它繼承 CrudRepository 接口,在 CrudRepository 基礎上新增了兩個與分頁有關的方法。

也可以不將自定義的持久層接口直接繼承PagingAndSortingRepository,而是繼承 RepositoryCrudRepository 的基礎上,在自定義方法參數列表最后增加一個 PageableSort 類型的參數,用於指定分頁或排序信息,可以實現比直接繼承 PagingAndSortingRepository 更大的靈活性。

4.4 JpaRepository接口

public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T>

JpaRepository 繼承PagingAndSortingRepository,是針對 JPA 技術提供的接口,它在父接口的基礎上,提供了其他一些方法,比如flush()saveAndFlush()deleteInBatch() 等。

4.5 自定義JPA Repository方法

自定義JPA Repository方法,應參考以下規范:

注意兩點:

  1. 出現在方法名中用於組合查詢條件等的是實體類的域名稱,而不是數據庫的表字段;
  2. 這種方式用來組合一些固定模式的SQL,生成SQL的過程對程序員是透明的,程序員不用自己寫SQL。

4.6 @Query注解

org.springframework.data.jpa.repository.Query注解用來指定JPA Repository方法對應的查詢HSQL。

示例:

import org.springframework.data.repository.query.Param;

//...

@Query("select a from App a where a.name LIKE %:name%")
List<App> findByName(@Param("name") String name);

需要注意的是,@Query中的參數HQL默認不是真實的SQL語句,而是面向對象的。

通常我們可以使用@Table指定實體類對應的表名,如果實體類的類名和對應的表名不同,@Query中應該使用類名,同時參數HQL中使用的是實體類的域名稱不是實際的字段名。

如不這樣,例如在@Query的參數HQL中使用了實際的表名而非實體類名,則會報:QuerySyntaxException: XXX is not mapped.

如果希望使用原生SQL,將@Query#nativeQuery設置為true即可:

@Query(nativeQuery = true, value = "SELECT * FROM AUTH_USER WHERE name = :name1  OR name = :name2 ")
List<UserDO> findSQL(@Param("name1") String name1, @Param("name2") String name2);

4.7 @Modifying注解

org.springframework.data.jpa.repository.Modifying注解標識一個JPA Repository方法對應的SQL會修改數據庫。

示例:

@Modifying
@Query("update appnamespace set isDeleted=1, dataChangeLastModifiedBy = ?3 where appId = ?1 and name = ?2")
int delete(String appId, String namespaceName, String operator);

參考:

https://www.jianshu.com/p/c14640b63653


免責聲明!

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



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