zoukankan      html  css  js  c++  java
  • Spring Data JPA:关联关系(外键)

    JAVA 8

    Spring Boot 2.5.3

    MySQL 5.7.21

    ---

    目录

    0、概述

    1、一对一

    2、一对多(多对一)

    3、多对多

    参考文档

    0、概述

    关联关系,即外键.

    包括:一对一、一对多(多对一)、多对多。

    难点:级联更新、级联删除

    在MySQL中,仅InnoDB支持 外键。

    本文分别介绍各种关联关系的使用。

    外键:被引用的列,要么是 主键 ,要么 具有 唯一性约束(UNIQUE)

    MySQL的外键:

        创建语法:1)CREATE TABLE、2)ALTER TABLE

        查看:1)SHOW CREATE TABLE tbl;、2)SHOW INDEX FROM tbl;、3)DESC tbl;

        删除:1)ALTER TABLE tbl DROP FOREIGN KEY fk_id;

    使用 mysql> help create table; 可以查看创建外键的语法:

    CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
        (create_definition,...)
        [table_options]
        [partition_options]
    ...
    create_definition:
        col_name column_definition
        ...
      | [CONSTRAINT [symbol]] FOREIGN KEY
          [index_name] (index_col_name,...) reference_definition
    ...
    reference_definition:
        REFERENCES tbl_name (index_col_name,...)
          [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]
          [ON DELETE reference_option]
          [ON UPDATE reference_option]
    ...
    reference_option:
        RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT
    

    外键部分简版如下:

    [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name,...) 
    REFERENCES tbl_name (index_col_name,...)
          [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]
          [ON DELETE [RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT]]
          [ON UPDATE [RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT]]

    ON DELETE 级联删除配置,ON UPDATE 级联更新配置。相关参数说明:

    RESTRICT

    默认值

    拒绝主表删除或修改

    CASCADE 级联删除 或 级联更新
    SET NULL

    主表删除或更新时,使用NULL值替代从表中对应的记录

    注意,从表字段设置为 NOT NULL时无效。

    NO ACTION 同 默认值 RESTRICT
    SET DEFAULT

    设置默认值

    参考文档1 说 InnoDB不支持(待确定

    spring boot工程:jpa-mysql

    MySQL配置:

    # MySQL on Ubuntu
    spring.datasource.url=jdbc:mysql://mylinux:3306/jpa?serverTimezone=Asia/Shanghai
    spring.datasource.username=springuser
    spring.datasource.password=ThePassword
    #spring.datasource.driver-class-name =com.mysql.jdbc.Driver # This is deprecated
    spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver
    spring.jpa.hibernate.ddl-auto=update
    spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
    # 打开使用过程中执行的SQL语句
    spring.jpa.show-sql: true

    建立数据库(jpa)语句:

    CREATE DATABASE IF NOT EXISTS jpa DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;

    1、一对一

    三个实体类:OtoBasic、OtoExt、OtoName

    其中,OtoBasic是主表(被关联表),OtoExt、OtoName是从表;OtoExt和OtoBasic的id字段建立 一对一关联,OtoName和OtoBasic的name字段建立 一对一关联。

    建立关联使用的注解:

    @OneToOne、@JoinColumn、@ForeignKey

    @Entity
    @Data
    public class OtoBasic {
    
    	@Id
    	@GeneratedValue(strategy = GenerationType.IDENTITY)
    	private Long id;
    	
    	/**
    	 * 名称:唯一、非空
    	 */
    	@Column(columnDefinition = "VARCHAR(100) UNIQUE NOT NULL")
    	private String name;
    	
    	@OneToOne(mappedBy = "basic", cascade = {CascadeType.REMOVE})
    	private OtoExt ext;
    	
    	@OneToOne(mappedBy = "basic", cascade = {CascadeType.ALL})
    	private OtoName otoName;
    	
    }
    
    @Entity
    @Data
    public class OtoExt {
    
    	/**
    	 * 自增
    	 */
    	@Id
    	@GeneratedValue(strategy = GenerationType.IDENTITY)
    	private Long id;
    	
    	/**
    	 * 详情
    	 */
    	@Column(columnDefinition = "VARCHAR(100) NOT NULL")
    	private String detail;
    	
    	// 不能有!不需要!
    //	private Long basicId;
    
    	// 1、无法删除
    //	@OneToOne(cascade = {CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.REMOVE})
    	// 2、无法删除 发生异常
    //	@OneToOne(cascade = {CascadeType.REFRESH, CascadeType.REMOVE})
    	// 3、无法删除 发生异常
    //	@OneToOne(cascade = {CascadeType.REMOVE})
    	// 在主表使用 CascadeType.REMOVE 才有效:删除主表记录,级联删除从表,,而不是在从表设置
    	@OneToOne
    	@JoinColumn(name = "basic_id", referencedColumnName = "id", foreignKey = @ForeignKey(name="fk_basic_id", 
            value=ConstraintMode.CONSTRAINT))
    	private OtoBasic basic;
    	
    }
    
    @Entity
    @Data
    public class OtoName {
    
    	@Id
    	@GeneratedValue(strategy = GenerationType.IDENTITY)
    	private Long id;
    	
    	// 注意,需要手动设置外键,添加选项:ON DELETE CASCADE ON UPDATE CASCADE
    	// 才可以级联更新
    	@OneToOne
    	@JoinColumn(name = "basic_name", referencedColumnName = "name", 
    		foreignKey = @ForeignKey(name="fk_basic_name", value=ConstraintMode.CONSTRAINT))
    	private OtoBasic basic;
    	
    }

    上面的 关联关系 是双向的:主表用到了 @OneToOne 的 mappedBy 属性,其值为 从表的属性名(对象名)。

    除了上面这种 双向 使用,也可以和从表一样 使用 @JoinColumn 的name、referencedColumnName 实现,但取值和从表相反(未试验)。

    建立实体类的Repository:新建接口,继承JpaRepository接口即可。

    public interface OtoBasicDAO extends JpaRepository<OtoBasic, Long> {
    
    }
    
    public interface OtoExtDAO extends JpaRepository<OtoExt, Long> {
    
    }
    
    public interface OtoNameDAO extends JpaRepository<OtoName, Long> {
    
    }

    启动项目,即可在工程中建立三个实体类对应的表&外键:

    启动日志的SQL语句:建表、建约束等
    Hibernate: create table oto_basic (id bigint not null auto_increment, name VARCHAR(100) UNIQUE NOT NULL, primary key (id)) engine=InnoDB
    Hibernate: create table oto_ext (id bigint not null auto_increment, detail VARCHAR(100) NOT NULL, basic_id bigint, primary key (id)) engine=InnoDB
    Hibernate: create table oto_name (id bigint not null auto_increment, basic_name VARCHAR(100) UNIQUE NOT NULL, primary key (id)) engine=InnoDB
    Hibernate: alter table oto_basic drop index UK_qamvvb8udcjeoj4q2mv7r63tk
    Hibernate: alter table oto_basic add constraint UK_qamvvb8udcjeoj4q2mv7r63tk unique (name)
    Hibernate: alter table oto_ext add constraint fk_basic_id foreign key (basic_id) references oto_basic (id)
    Hibernate: alter table oto_name add constraint fk_basic_name foreign key (basic_name) references oto_basic (name)
    
    数据表结构:show create table XXX
    mysql> show create table oto_basic G
    *************************** 1. row ***************************
           Table: oto_basic
    Create Table: CREATE TABLE `oto_basic` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `name` varchar(100) NOT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `name` (`name`),
      UNIQUE KEY `UK_qamvvb8udcjeoj4q2mv7r63tk` (`name`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
    1 row in set (0.00 sec)
    
    mysql>
    mysql> show create table oto_ext G
    *************************** 1. row ***************************
           Table: oto_ext
    Create Table: CREATE TABLE `oto_ext` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `detail` varchar(100) NOT NULL,
      `basic_id` bigint(20) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `fk_basic_id` (`basic_id`),
      CONSTRAINT `fk_basic_id` FOREIGN KEY (`basic_id`) REFERENCES `oto_basic` (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
    1 row in set (0.00 sec)
    
    mysql>
    mysql> show create table oto_name G
    *************************** 1. row ***************************
           Table: oto_name
    Create Table: CREATE TABLE `oto_name` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `basic_name` varchar(100) NOT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `basic_name` (`basic_name`),
      CONSTRAINT `fk_basic_name` FOREIGN KEY (`basic_name`) REFERENCES `oto_basic` (`name`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
    1 row in set (0.00 sec)
    
    mysql>
    

    说明:来自博客园

    1、自定义外键名称

    外键体现在 从表 oto_ext、oto_name 中:fk_basic_id、fk_basic_name (名称 由 注解 @JoinColumn 的属性 @ForeignKey 确定,否则是一串无意义字符串,如oto_baisc的 UK_qamvvb8udcjeoj4q2mv7r63tk 键)。

    2、从表的新字段

    建立外键后,从表 多了响应的字段:oto_ext 中的 basic_id,oto_name 中的 basic_name,此时不能在 从表实体类中 添加对应的 属性。

    3、级联删除

    删除主表oto_baisc中的记录,此时,级联删除 从表oto_ext中的内容。

    需要使用 @OneToOne注解 的 cascade属性,其值为 CascadeType.ALL 或 包括 CascadeType.REMOVE 都可以。

    另外,还有一个 orphanRemoval 属性,默认为false——不级联删除,设置为true后,即可 级联删除。

    cascade 或 orphanRemoval 只要有一个 允许级联删除,即可执行 级联删除。来自博客园

    4、级联更新

    更新主表oto_baisc 的 name字段时,希望从表 oto_name 对应的 basic_name 也更新。

    但是,默认建立的外键 没有配置 ON UPDATE CASCADE,导致无法级联更新,也会阻止 主表更新。

    尝试多种 JPA方式 添加 ON UPDATE CASCADE 选项都失败了,最终只能手动操作

    先删除oto_name的外键 fk_basic_name,再新建一个 同名的外键。

    ALTER TABLE oto_name DROP FOREIGN KEY fk_basic_name;
    ALTER TABLE oto_name ADD CONSTRAINT fk_basic_name FOREIGN KEY (basic_name) REFERENCES oto_basic(name) 
    ON DELETE CASCADE ON UPDATE CASCADE;

     这样就可以级联删除了。

    一对一案例:

    1)一夫一妻

    2) 

    2、一对多(多对一)

    两个实体类:OtmBasic、OtmMany,其中,一个OtmBasic可以对应多个OtmMany。来自博客园

    建立关联使用的注解:

    @ManyToOne、@OneToMany、@JoinColumn、@ForeignKey、@OrderBy(结合@OneToMany使用)

    @Entity
    @Data
    public class OtmBasic {
    
    	/**
    	 * 自增
    	 */
    	@Id
    	@GeneratedValue(strategy = GenerationType.IDENTITY)
    	private Long id;
    	
    	@Column(columnDefinition = "VARCHAR(100) UNIQUE NOT NULL")
    	private String name;
    
    	@Column(insertable = false, columnDefinition = "DATETIME DEFAULT NOW()")
    	private Date createTime;
    	
    	// 1、不能删除
    //	@OneToMany(mappedBy = "basic", cascade = {})
    	// 2、可以删除
    	@OneToMany(mappedBy = "basic", cascade = {CascadeType.REMOVE})
    	// 按 创建时间倒序 排列
    	@OrderBy(value = "create_time DESC")
    	private List<OtmMany> manyList;
    	
    	// 构造函数
    	
    	public OtmBasic() {}
    	
    	public OtmBasic(String name) {
    		this.name = name;
    	}
    	
    }
    
    @Entity
    @Data
    public class OtmMany {
    
    	/**
    	 * 自增
    	 */
    	@Id
    	@GeneratedValue(strategy = GenerationType.IDENTITY)
    	private Long id;
    
    	/**
    	 * house:房子
    	 */
    	@Column(columnDefinition = "VARCHAR(100) UNIQUE NOT NULL")
    	private String house;
    
    	@Column(insertable = false, columnDefinition = "DATETIME DEFAULT NOW()")
    	private Date createTime;
    	
    	@ManyToOne
    	@JoinColumn(name="basic_id", foreignKey = @ForeignKey(name="fk_basic_id_otm"))
    	private OtmBasic basic;
    	
    }

    建立实体类的Repository:新建接口,继承JpaRepository接口即可。来自博客园

    public interface OtmBasicDAO extends JpaRepository<OtmBasic, Long> {
    
    }
    
    public interface OtmManyDAO extends JpaRepository<OtmMany, Long> {
    
    }

    启动项目,数据库中建立了实体类相关的表和外键:

    项目日志:
    Hibernate: create table otm_basic (id bigint not null auto_increment, create_time DATETIME DEFAULT NOW(), 
    	name VARCHAR(100) UNIQUE NOT NULL, primary key (id)) engine=InnoDB
    Hibernate: create table otm_many (id bigint not null auto_increment, create_time DATETIME DEFAULT NOW(), 
    	house VARCHAR(100) UNIQUE NOT NULL, basic_id bigint, primary key (id)) engine=InnoDB
    Hibernate: alter table otm_many add constraint fk_basic_id_otm foreign key (basic_id) references otm_basic (id)
    
    数据表:
    mysql> show create table otm_basic G
    *************************** 1. row ***************************
           Table: otm_basic
    Create Table: CREATE TABLE `otm_basic` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
      `name` varchar(100) NOT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `name` (`name`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
    1 row in set (0.00 sec)
    
    mysql> show create table otm_many G
    *************************** 1. row ***************************
           Table: otm_many
    Create Table: CREATE TABLE `otm_many` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
      `house` varchar(100) NOT NULL,
      `basic_id` bigint(20) DEFAULT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `house` (`house`),
      KEY `fk_basic_id_otm` (`basic_id`),
      CONSTRAINT `fk_basic_id_otm` FOREIGN KEY (`basic_id`) REFERENCES `otm_basic` (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
    1 row in set (0.00 sec)
    
    mysql>
    
    

    说明:

    1、外键名称冲突

    在前面试验 一对一关联 时,建立了外键 fk_basic_id。来自博客园

    在本试验中,最开始也想建立一个 同名的外键的,但是,发生了冲突:

    Hibernate: alter table otm_many add constraint fk_basic_id foreign key (basic_id) references otm_basic (id)
    2021-10-18 20:46:44.332  WARN 8768 --- [           main] o.h.t.s.i.ExceptionHandlerLoggedImpl     : 
    GenerationTarget encountered exception accepting command : Error executing DDL "alter table otm_many 
    add constraint fk_basic_id foreign key (basic_id) references otm_basic (id)" via JDBC Statement
    
    org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "alter table otm_many 
    add constraint fk_basic_id foreign key (basic_id) references otm_basic (id)" via JDBC Statement

    将其更改为 fk_basic_id_otm 后,冲突消失:

    // OtmMany.java
    @ManyToOne
    @JoinColumn(name="basic_id", foreignKey = @ForeignKey(name="fk_basic_id_otm"))
    private OtmBasic basic;

     2、级联删除

    使用 主表的@OneToMany 的 cascade属性,或 orphanRemoval属性,同 @OneToOne。

    3、数据排序

    一对多中,“一”的一方可以使用@OrderBy 对结果排序。来自博客园

    // OtmBasic.java
    @OneToMany(mappedBy = "basic", cascade = {CascadeType.REMOVE})
    @OrderBy(value = "create_time DESC")
    private List<OtmMany> manyList;

    一对多案例:来自博客园

    1)一个家庭 多套房产

    2) 

    3、多对多

    两个实体类:MtmBlog、MtmBlogTag,其中,博客 和 标签 是 多对多的关系。

    建立关联使用的注解:来自博客园

    @ManyToMany、@JoinTable、@JoinColumn、@ForeignKey、@OrderBy

    @Entity
    @Data
    public class MtmBlog {
    
    	@Id
    	@GeneratedValue(strategy = GenerationType.IDENTITY)
    	private Long id;
    	
    	@Column(columnDefinition = "VARCHAR(100) NOT NULL")
    	private String title;
    	
    	@Column(columnDefinition = "VARCHAR(1000) NOT NULL")
    	private String content;
    	
    	@ManyToMany(cascade = {CascadeType.ALL})
    	@JoinTable(
    			name = "mtm_blog_tag_rel",
    			joinColumns = @JoinColumn(name = "blog_id", referencedColumnName = "id",
    					foreignKey = @ForeignKey(name="fk_blog_tag_rel_blog")),
    			inverseJoinColumns = @JoinColumn(name = "tag_id", referencedColumnName = "id",
    					foreignKey = @ForeignKey(name="fk_blog_tag_rel_tag"))
    			)
    	// 排序
    	@OrderBy(value = "tag DESC")
    	private List<MtmBlogTag> tags;
    	
    }
    
    @Entity
    @Data
    public class MtmBlogTag {
    
    	@Id
    	@GeneratedValue(strategy = GenerationType.IDENTITY)
    	private Long id;
    	
        // 唯一性约束
    	@Column(columnDefinition = "VARCHAR(100) UNIQUE NOT NULL")
    	private String tag;
    
    	// 双向多对多
    	@ManyToMany(mappedBy = "tags")
    	// 排序
    	@OrderBy(value = "title DESC")
    	private List<MtmBlog> blogs;
    	
    }
    

    建立实体类的Repository:新建接口,继承JpaRepository接口即可。

    public interface MtmBlogDAO extends JpaRepository<MtmBlog, Long> {
    
    }
    
    public interface MtmBlogTagDAO extends JpaRepository<MtmBlogTag, Long> {
    
    	MtmBlogTag findByTag(String tag);
    	
    }

    启动项目,自动建立数据表:来自博客园

    2个实体类,但是建立了3个数据表,其中一个为 数据关联表,多对多时需要。

    # 项目日志
    Hibernate: create table mtm_blog_tag_rel (blog_id bigint not null, tag_id bigint not null) engine=InnoDB
    Hibernate: create table mtm_blog (id bigint not null auto_increment, content VARCHAR(1000) NOT NULL, 
    	title VARCHAR(100) NOT NULL, primary key (id)) engine=InnoDB
    Hibernate: create table mtm_blog_tag (id bigint not null auto_increment, tag VARCHAR(100) UNIQUE NOT NULL, 
    	primary key (id)) engine=InnoDB
    Hibernate: alter table mtm_blog_tag_rel add constraint fk_blog_tag_rel_tag foreign key (tag_id) references mtm_blog_tag (id)
    Hibernate: alter table mtm_blog_tag_rel add constraint fk_blog_tag_rel_blog foreign key (blog_id) references mtm_blog (id)
    
    数据表:3个
    mysql> show create table mtm_blog G
    *************************** 1. row ***************************
           Table: mtm_blog
    Create Table: CREATE TABLE `mtm_blog` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `content` varchar(1000) NOT NULL,
      `title` varchar(100) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
    1 row in set (0.00 sec)
    
    mysql> show create table mtm_blog_tag G
    *************************** 1. row ***************************
           Table: mtm_blog_tag
    Create Table: CREATE TABLE `mtm_blog_tag` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `tag` varchar(100) NOT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `tag` (`tag`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
    1 row in set (0.00 sec)
    
    mysql>
    mysql> show create table mtm_blog_tag_rel G
    *************************** 1. row ***************************
           Table: mtm_blog_tag_rel
    Create Table: CREATE TABLE `mtm_blog_tag_rel` (
      `blog_id` bigint(20) NOT NULL,
      `tag_id` bigint(20) NOT NULL,
      KEY `fk_blog_tag_rel_tag` (`tag_id`),
      KEY `fk_blog_tag_rel_blog` (`blog_id`),
      CONSTRAINT `fk_blog_tag_rel_blog` FOREIGN KEY (`blog_id`) REFERENCES `mtm_blog` (`id`),
      CONSTRAINT `fk_blog_tag_rel_tag` FOREIGN KEY (`tag_id`) REFERENCES `mtm_blog_tag` (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
    1 row in set (0.01 sec)
    
    mysql>
    

    说明:来自博客园

    1、关联表不能建立实体类

    两个实体类,生成了3个表:mtm_blog、mtm_blog_tag、mtm_blog_tag_rel,其中,mtm_blog_tag_rel 是 数据关联表。

    注,在 参考文档1 中,关联表 也需要建立实体类,但在试验中发现,工程里面不能有 关联表的实体类!否则,启动报错

    2021-10-20 14:26:12.889 ERROR 10496 --- [           main] j.LocalContainerEntityManagerFactoryBean : 
    Failed to initialize JPA EntityManagerFactory: No identifier specified for entity: org.lib.jpamysql.mtm.MtmBlogTagRel
    
    2021-10-20 14:26:12.889  WARN 10496 --- [           main] ConfigServletWebServerApplicationContext : 
    Exception encountered during context initialization - cancelling refresh attempt: 
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 
    'entityManagerFactory' defined in class path resource 
    [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; 
    nested exception is org.hibernate.AnnotationException: No identifier specified for entity: 
    org.lib.jpamysql.mtm.MtmBlogTagRel

    工程没有启动成功,一个数据表也没有建立成功。来自博客园

    2、数据排序

    使用@OrderBy,双方都可以使用。

    3、关联表默认名称

    @JoinTable 可以不使用 name属性,此时,建立的 关联表名称是 自动的。在本试验中,会自动建立 关联表 mtm_blog_tags:

    多对多案例:来自博客园

    1)博客、标签

    2)用户、角色

    3)老师、学生

    4)游客、度假胜地

    5)

    》》》全文完《《《来自博客园

    源码:jpa-mysql,其中oto、otm、mtm包下的代码。来自博客园

    外键 很好用,但是,使用不当会造成严重问题。

    在《阿里巴巴Java开发手册》(参考文档3 可以下载)中,不建议使用外键:

    参考文档

    1、书《Spring Data JPA 从入门到精通》 by 张振华

    2、书《MySQL数据库 原理、设计与应用》 by 黑马程序员

    3、阿里官方Java代码规范标准《阿里巴巴Java开发手册 终极版 v1.3.0》下载

    4、

  • 相关阅读:
    ThinkPHP 统计数据(数字字段)更新 setInc 与 setDec 方法
    JS正则表达式验证账号、手机号、电话和邮箱
    javascript document.referrer 用法
    location.hash详解
    window.location.hash属性介绍
    php伪静态
    文本框只允许输入数字.net/javascript
    ts文件编译后变量在vscode里报错
    win10虚拟桌面使用方法-提高工作效率
    vscode设置代码块
  • 原文地址:https://www.cnblogs.com/luo630/p/15428367.html
Copyright © 2011-2022 走看看