Spring Data JPA 自動生成表結構

想在部署的時候隨應用的啓動而初始化數據腳本,這不就是Spring Data Jpa中的自動生成表結構,聽起來特別簡單,不就是配置Hibernateddl-auto嘛,有什麼好說的,是我的都知道。當初我也是這樣認爲,實際操做了一把,雖然表是建立成功了,可是字段註釋,字符集以及數據庫引擎都不對,沒想到在這些細節上翻車了。java

畢竟開翻的車還要本身扶起來,粗略寫點救援過程。mysql

注:本文中使用的Spring Data JPA版本爲2.1.4.RELEASEgit

MySQL爲例,其它數據庫可自行驗證:spring

import com.fasterxml.jackson.annotation.*;
import org.hibernate.annotations.*;
import org.springframework.data.annotation.*;
import javax.persistence.*;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.math.BigDecimal;

@Entity
@javax.persistence.Table(name = "basic_city")
@org.hibernate.annotations.Table(appliesTo = "basic_city", comment = "城市基本信息")
public class CityDO {
    
    @Id
    @GenericGenerator(name = "idGenerator", strategy = "uuid")
    @GeneratedValue(generator = "idGenerator")
    @Column(name = "CITY_ID", length = 32)
    private String cityId;
 
    @Column(name = "CITY_NAME_CN", columnDefinition = "VARCHAR(255) NOT NULL COMMENT '名稱(中文)'")
    private String cityNameCN;
    
    @Column(name = "CITY_NAME_EN", columnDefinition = "VARCHAR(255) NOT NULL COMMENT '名稱(英文)'")
    private String cityNameEN;

    @Column(name = "LONGITUDE", precision = 10, scale = 7)
    private BigDecimal longitude;

    @Column(name = "LATITUDE", precision = 10, scale = 7)
    private BigDecimal latitude;

    @Column(name = "ELEVATION", precision = 5)
    private Integer elevation;

    @Column(name = "CITY_DESCRIPTION", length = 500)
    private String cityDescription;
    
    // 構造方法及get/set方法省略
}

用到的註解簡要說明一下:sql

  1. @javax.persistence.Table 修改默認ORM規則,屬性name設置表名;
  2. @org.hibernate.annotations.Table 建表時的描述, 屬性comment修改表描述;
  3. @Id 主鍵
  4. @GenericGenerator 該註解爲Hibernate的註解,用來生成表的主鍵策略,屬性strategy的值在類DefaultIdentifierGeneratorFactory中定義:數據庫

    • uuid2: UUIDGenerator.class;
    • guid: GUIDGenerator.class;
    • uuid: UUIDHexGenerator.class;
    • uuid.hex: UUIDHexGenerator.class;
    • assigned: Assigned.class;
    • identity: IdentityGenerator.class;
    • select: SelectGenerator.class;
    • sequence: SequenceStyleGenerator.class;
    • seqhilo: SequenceHiLoGenerator.class;
    • increment: IncrementGenerator.class;
    • foreign: ForeignGenerator.class;
    • sequence-identity: SequenceIdentityGenerator.class;
    • enhanced-sequence: SequenceStyleGenerator.class;
    • enhanced-table: TableGenerator.class;
  5. @GeneratedValue 設置主鍵策略,這裏屬性generator指向@GenericGenerator策略的name,屬性strategy有四個枚舉值:segmentfault

    • GenerationType.TABLE 使用一個額外的表來存儲主鍵;
    • GenerationType.SEQUENCE 使用序列的方式存儲,且須要數據庫底層支持;
    • GenerationType.IDENTITY 由數據庫生成,通常爲主鍵自增等;
    • GenerationType.AUTO 表示由程序生成,不聲明則默認爲該屬性;
  6. @Column 修改默認的ORM規則,屬性有:app

    • name設置表中字段名稱,表字段和實體類屬性相同,則該屬性可不寫;
    • unique設置該字段在表中是否惟一,默認false
    • nullable是否可爲空,默認true
    • insertable表示insert操做時該字段是否響應寫入,默認爲true
    • updatable表示update操做時該字段是否響應修改,默認爲true
    • columnDefinition是自定義字段,能夠用這個屬性來設置字段的註釋;
    • table表示當映射多個表時,指定表的表中的字段,默認值爲主表的表名;
    • length是長度,僅對varchar類型的字段生效,默認長度爲255;
    • precision表示一共多少位;
    • scale表示小數部分佔precision總位數的多少位,例子中二者共同使用來確保經緯度的精度;

接下來須要設置數據引擎和字符集,網上的例子都大把的繼承MySQL5InnoDBDialect,可是這個類已通過期了,咱們這裏用MySQL5Dialectide

package com.jason.config;

import org.hibernate.dialect.MySQL5Dialect;
import org.springframework.stereotype.Component;

@Component
public class MySQL5TableType extends MySQL5Dialect {

    @Override
    public String getTableTypeString() {
        return "ENGINE=InnoDB DEFAULT CHARSET=utf8";
    }
}

而後在Spring Boot的配置文件中應用上面定義的MySQL5TableType,使用spring.jpa.properties.hibernate.dialect配置(注意書寫格式,這裏使用的是yml文件):單元測試

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/techno?useUnicode=true&characterEncoding=utf8
    username: root
    password: root
  jpa:
    hibernate:
      ddl-auto: update
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    properties:
      hibernate:
        dialect: com.jason.config.MySQL5TableType

jpa.hibernate.ddl-auto使用update,其它值及說明爲:

  • create: 啓動服務時都會從新建立表,且無論表存不存在;
  • create-drop: 啓動服務時都會從新建立表,且無論表存不存在,服務中止時刪除全部表,無論表中是否有數據;
  • update: 啓動服務時,自動更新表結構,但數據庫表中存在的舊字段不會刪除;
  • validate: 啓動服務時驗證表結構,若表結構存在差別則拋出異常;

至此,Sprign Data JPA生成表結構就完成了,當咱們創建數據庫後,啓動服務就能夠在MySQL中獲得表結構了,應用能夠經過ApplicaitonRunner或者CommandLineRunner接口一鍵部署,省去了初始化SQL等沒必要要的操做,這兩個接口的簡單使用能夠參考個人另外一篇文章

最後咱們再上一個例子,主要是寫入默認值等,部分說明就直接以註釋的形式寫到代碼裏,還有@ColumnDefault註解這裏就不作說明,你們能夠本身瞭解下:

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.hibernate.annotations.*;
import org.springframework.data.jpa.repository.*;

import javax.persistence.*;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Temporal;
import java.util.Date;

@ApiModel(value = "帳號基礎信息")
@Entity
@Table(name = "basic_account")
@org.hibernate.annotations.Table(appliesTo = "basic_account", comment = "帳號基礎信息表")
public class AccountDO {

    @ApiModelProperty(name = "accountId", value = "帳號Id", hidden = true)
    @Id
    @GenericGenerator(name = "idGenerator", strategy = "uuid")
    @GeneratedValue(generator = "idGenerator")
    @Column(name = "ACCOUNT_ID", length = 32)
    private String accountId;
    
    @ApiModelProperty(name = "username", value = "帳號", dataType = "String", required = true)
    @Column(length = 32, nullable = false)
    private String username;

    // 假設密碼加密後長度爲128
    @ApiModelProperty(name = "password", value = "密碼", dataType = "String", required = true)
    @Column(length = 128, nullable = false)
    private String password;

    // 新建的帳號未過時,默認值給1,這個值由數據庫生成,則設置insertable爲false
    @ApiModelProperty(name = "isAccountNonExpired", value = "帳號是否過時", hidden = true)
    @Column(name = "IS_ACCOUNT_EXPIRED", insertable = false, columnDefinition = "CHAR(1) NOT NULL DEFAULT '1' COMMENT '帳號 0:過時;1:未過時'")
    private Integer isAccountNonExpired;

    // 新建的帳號未鎖定,默認值給1,這個值由數據庫生成,則設置insertable爲false
    @Column(name = "IS_ACCOUNT_LOCKED", insertable = false, columnDefinition = "CHAR(1) NOT NULL DEFAULT '1' COMMENT '帳號 0:鎖定;1:未鎖定'")
    @ApiModelProperty(name = "isAccountNonLocked", value = "帳號是否鎖定", hidden = true)
    private Integer isAccountNonLocked;

    // 新建的帳號密碼未過時,默認值給1,這個值由數據庫生成,則設置insertable爲false
    @Column(name = "IS_CREDENTIALS_EXPIRED", insertable = false, columnDefinition = "CHAR(1) NOT NULL DEFAULT '1' COMMENT '密碼 0:已過時;1:未過時'")
    @ApiModelProperty(name = "isCredentialsNonExpired", value = "密碼是否過時", hidden = true)
    private Integer isCredentialsNonExpired;

    // 新建的帳號須要激活,默認值給0,這個值由數據庫生成,設置insertable爲false
    @ApiModelProperty(name = "isEnabled", value = "帳號是否可用", hidden = true)
    @Column(name = "IS_ENABLE", insertable = false, columnDefinition = "CHAR(1) NOT NULL DEFAULT '0' COMMENT '帳號 0:不可用;1:可用'")
    private Integer isEnabled;

    // 新建的帳號,默認值給1,這個值由數據庫生成,設置insertable爲false
    @ApiModelProperty(name = "isDelete", value = "帳號是否刪除", hidden = true)
    @Column(name = "IS_DELETE", insertable = false, columnDefinition = "CHAR(1) NOT NULL DEFAULT '1' COMMENT '帳號 0:已刪除;1:未刪除'")
    private Integer isDelete;

    // 新建帳號時間不能修改,設置updatable爲false,但此處不能設置insertable = false
    // @Temporal(TemporalType.TIMESTAMP) 因爲表字段類型爲TIMESTAMP,因此將Date轉換爲TIMESTAMP
    @ApiModelProperty(name = "createTimestamp", value = "建立時間")
    @Column(name = "CREATE_TIMESTAMP", nullable = false, updatable = false)
    @Temporal(TemporalType.TIMESTAMP)
    @CreationTimestamp
    private Date createTimestamp;
    
    public AccountDO(String username,String password) {
        this.username = username;
        this.password = password;
    }
    
    // 其餘構造方法及get/set方法省略
}

注:@ApiModel以及@ApiModelPropertyswagger註解。

咱們註冊帳號的單元測試就能夠直接寫成下面這樣,僅填寫帳號和密碼,其餘值則由數據庫生成:

@Test
    public void saveAccount() throws Exception {
        accountService.saveAccount(new AccountDO("123456", "654321"));
    }

原創不易,感謝支持。

相關文章
相關標籤/搜索