初始化数据库

初始化数据库有两种:

  1. 项目中的业务表,会有一个po与之对应

  2. 初始化数据或者框架自带的一些表

业务表

业务表在框架中都有一个po类与之对应,底层用到了hibernate的orm全映射技术,会根据po自动生成表。

有一点需要注意,当po改变时,也就是表结构需要改变的时候,只会增加字段,不会修改字段,即不会自动修改字段的长度和类型。只有添加的时候会生效,减少或者改变的时候是不会生效的,这也正好符合线上的逻辑。

  • 如果产品已近上线,已近有真实的数据了,这个时候我们不会删除字段,也不可能修改字段的类型

  • 如果产品还没有上线,删除字段或者修改字段的类型,我们可以将数据库中的原表删除,启动项目的时候会根据最新的po映射一张新表出来

spring:
  jpa:
    show-sql: true
    generate-ddl: true
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    hibernate:
      ddl-auto: update
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
import java.io.Serializable;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.Table;

import com.univer.base.po.BaseEntity;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Generated;
/**
 * 
 * @author wumingsheng
 * @date 2019年2月25日
 */
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = "crawl", indexes = {@Index(name = "crawl_id_index", columnList = "crawl_id", unique = true)})
@Generated
public class Crawl extends BaseEntity implements Serializable {


    /**
     * 
     */
    private static final long serialVersionUID = 7474937903486382477L;


    @Id
    @Column(name = "id", unique = true, nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;


    /**任务名称<<冗余字段>>*/
    @Column(name = "task_name", nullable = false, columnDefinition = "varchar(32) comment '任务名称'")
    private String taskName;

    /**是否是定时任务还是一次性任务<<冗余字段>>*/
    @Column(name = "task_job_flag", columnDefinition = "tinyint(1) unsigned not null default 0 comment '是否只执行一次,0:不勾选,1:每隔'")
    private Boolean taskJobFlag;

     /** 数据类型,"video":视频,"image":图片,"txt":文本 <<冗余字段>>*/
    @Column(name = "data_type", nullable = false, columnDefinition = "varchar(16) default 'image' comment 'video:视频、image:图片、txt:文本'")
    private String dataType;

    /**
     * 任务id<br>
     * 冗余字段<br>
     * 外键、task主键
     */
    @Column(name = "task_id", columnDefinition = "bigint(24) not null comment '外键、task主键'")
    private Long taskId;

    /**
     * 网站id<br>
     * 冗余字段<br>
     * 外键、website主键
     */
    @Column(name = "website_id", columnDefinition = "bigint(24) not null default 0 comment '外键、website主键'")
    private Long websiteId;
    /**
     * redis-key<br>
     */
    @Column(name = "redis_key", columnDefinition = "varchar(64) not null default 'null' comment '开启爬虫任务时的redis-key'")
    private String redisKey;
    /**
     * 爬取方式,"all":非关键词爬取,"keyword":关键词爬取<br>
     * 冗余字段
     */
    @Column(name = "web_mode", columnDefinition = "varchar(16) not null default 'all' comment 'all:非关键字,keyword:关键字'")
    private String webMode;

    /**
     * 关键字<br>
     * 冗余字段
     */
    @Column(name = "key_word", columnDefinition = "varchar(256) default null comment '关键词,关键词爬取时有效,逗号分割'")
    private String keyWord;

    /**实时任务量、定时更新*/
    @Column(name = "task_nums", columnDefinition = "bigint(20) default null comment '实时任务数量、随任务状态定时更新'")
    private Long taskNums;

    /** 爬虫任务状态*/
    @Column(name = "crawl_status", columnDefinition = "int(1) not null default 1 comment '爬虫任务状态,枚举'")
    private Integer crawlStatus;

    /**crawlId*/
    @Column(name = "crawl_id", unique = true, columnDefinition = "varchar(32) not null comment '给py的uuid'")
    private String crawlId;


    /**任务优先级*/
    @Column(name = "task_priority", columnDefinition = "int(11) not null comment '优先级0,1,2,低中高'")
    private Integer taskPriority;


    /**source*/
    @Column(name = "source", columnDefinition = "varchar(16) not null default 'spider' comment 'spider'")
    private String source;

    /**parseFunction*/
    @Column(name = "parse_function", columnDefinition = "varchar(32) not null default 'parse_detail_page' comment 'parse_detail_page'")
    private String parseFunction;

    /**origin*/
    @Column(name = "origin", columnDefinition = "varchar(32) not null comment '网站名称'")
    private String origin;

    /**originId*/
    @Column(name = "origin_id", columnDefinition = "varchar(32) not null comment '解析id'")
    private String originId;

    /**parser*/
    @Column(name = "parser", columnDefinition = "varchar(128) not null comment '解析类'")
    private String parser;

    /**url*/
    @Column(name = "url", columnDefinition = "varchar(1024) not null comment '首页地址'")
    private String url;

    @Column(name = "create_time", columnDefinition = "timestamp not null default current_timestamp comment '创建时间'")
    private Date createTime;


}

初始化数据或者框架表

对于一些初始化数据,或者框架自带功能需要用到的一些表,我们可以使用 SpringBoot 提供的功能帮我们完成

由于项目会多次发布,或者多次重启,要做到初始化脚本的幂等性,也就是多次操作执行的相同的结果,结果不会累加,需要有几个细节注意:

  1. 对于schema脚本,可以使用CREATE TABLE IF NOT EXISTS QRTZ_JOB_DETAILSIF NOT EXISTS帮助我们避免多次执行ddl

  2. 对于初始化数据,我们可以使用主键的唯一性或者在ddl中定义唯一索引,来保证数据的唯一性,这样多次执行就会报错,我们在后续设置的continue-on-error: true可以保证项目正常启动同时数据不会被多次插入。似乎continue-on-error: true的设计就是为了帮助我们这样做的。实现幂等性的关键就是continue-on-error: true,其实我们在ddl中即使不定义IF NOT EXISTS,有continue-on-error: true给我们保证幂等性也是没有问题的。

  3. data: classpath:sql/data-${univer_company}.sql可以在配置文件中引用环境变量,保证不同的环境有不同的初始化数据

  4. 即使这样,也不可能做到一劳永逸,如果在新版本发布的时候,修改了初始化数据的一些字段数据,避免不了累计递增的添加更新脚本,具体情况具体对待

spring:
  datasource:
    url: 
    username: 
    password: 
    driverClassName: com.mysql.cj.jdbc.Driver
    platform: mysql
    continue-on-error: true
    schema: classpath:sql/schema.sql
    data: classpath:sql/data-${univer_company}.sql
    initialization-mode: always
    hikari:
      connection-timeout: 30000
      idle-timeout: 300000
      max-lifetime: 300000
      maximum-pool-size: 50
      minimum-idle: 10

执行顺序

按照一下顺序先后执行

  1. schema

  2. data

  3. hibernate-jpa-po

Last updated