译者注
原文地址:https://www.baeldung.com/jpa-indexes
实例代码:https://github.com/eugenp/tutorials/tree/master/persistence-modules/java-jpa-3
1. 概述
本文我们将讨论使用JPA的@Index注解来定义索引。在实例中,我们首先讲解如何使用JPA和Hiernage来定义第一个索引。接下来讲解更多关于自定义注解的知识。
2. @Index 注解
数据库索引是一种数据结构,它以增加的写入和存储空间为代价,提高了对表进行数据检索操作的速度。通常来说,它是从某个单表中选择的数据库的副本。我们应该合理的为数据表创建索引来提高数据库的查询性能。
JPA允许我们在代码中使用@Index注解来定义数据表索引。该注解能够实现在创建(更新)数据表时自动为数据表添加索引。
接下来我们看一下如何使定义索引:
2.1. javax.persistence.Index
自JPA2.1版本,JPA增加了java.prsistence.Index来允许我们为数据表创建注解:
@Target({})
@Retention(RUNTIME)
public @interface Index {
String name() default "";
String columnList();
boolean unique() default false;
}
如上代码所示,@Index注解中仅有columnList属性是必填的。后面我们将具体使用该注解来具体的查看各个属性的用法。
2.2. JPA 和 Hibernate
我们常弄不清JPA与Hibernate的关系。我们知道JPA仅仅是一个规范,它除了做了一些规定提供了一些愿景(也可以说模板、蓝图)以外,其它什么也没做。而要其使愿景便成事实,则需要有人来做实际的工作将其实现。在Spring中使用了Hibernate来完成了这个愿景,你可以参考本文来进一步学习。当然了,除了 Hibernate以外还有其它的JPA实现,比如EclipseLink。所以JPA就好像面向对象思想中的接口,而Hibernate与EclipseLink则都是这个接口的具体实现。
值的注意的是由于JPA添加索引支持比较晚,而在这之前多个ORM框架均有自己支持索引的方法。这使得在JPA统一规范以前,出现了各个ORM各自为政的情况。比如Hibernate就有自己的org.hibernate.annotations.Index注解。所以我们在使用索引注解时,一定上要确认使用的是JPA下的注解 ---- java.prsistence.Index(而不是其他框架中的同名注解)。
简单的了解一些技术背景了,让我们正式开始。
3. 定义@Index
在定义索引前,我们需要了解一些如何定义JPA实体的相关知识。
我们实现一个Student学生实体如下:
/**
* 学生.
*
* @author panjie
*/
@Entity
@Table
public class Student implements Serializable {
@Id
@GeneratedValue
private Long id;
/**
* 姓名.
*/
private String name;
/**
* 学号.
*/
private String sno;
// setters getters
}
接下来,我们在@Table注解中加入相关的@Index注解。
@Table(indexes = @Index(columnList = "name"))
如上我们创建了第一个基于name的索引,此时数据表在初始化时,控制台的日志信息中将对应生成以下Sql代码:
// 中间的值是自动生成的,不会完全一致
[main] DEBUG org.hibernate.SQL -
create index IDX2gdkcjo83j0c2svhvceabnnoh on Student (name)
接下来我们索引的其他属性和功能。
3.1. 索引名称
不难看出,索引必须有一个名称。默认情况下我们没有指定名称,那么将会使用自动生成的值。
如果我们需要自定义一个名称,只需要修改name属性:
@Index(name = "test_index", columnList = "name")
如代码所示,我们创建了一个名为test_index
的索引。
在数据表初始化时,日志就会变成:
[main] DEBUG org.hibernate.SQL -
create index test_index on Student (name)
此外,我们可以通过在名称中指定模式(schema)的名称,以便在不同的模式(schema)中创建索引:
@Index(name = "schema2.test_index", columnList = "name")
3.2. 在多个列上定义一个@Index
columnList语法如下:
column ::= index_column [,index_column]*
index_column ::= column_name [ASC | DESC]
刚才我们提到,索引是对于一个列的索引,也必须包括列名。然而,我们也可以在单个索引中指定多个列。
方法就是用逗号来分隔名称:
@Index(name = "mulitIndex1", columnList = "name, phoneNumber")
@Index(name = "mulitIndex2", columnList = "phoneNumber, name")
生成的日志:
[main] DEBUG org.hibernate.SQL -
create index mulitIndex1 on Student (name, phoneNumber)
[main] DEBUG org.hibernate.SQL -
create index mulitIndex2 on Student (phoneNumber, name)
需要注意的是,如果我们定义多列索引,索引的前后顺序必须相同。如果顺序变化,即使使用的同一组列,也是两个不同的索引,如代码所示。
3.3. @Index 索引排序(Order)
我们还可以在列名column_name之后指定ASC(升序)或DESC(降序),来控制索引值的排列顺序:
@Index(name = "mulitSortIndex", columnList = "name, phoneNumber DESC")
日志:
[main] DEBUG org.hibernate.SQL -
create index mulitSortIndex on Student (name, phoneNumber desc)
3.4. 索引唯一性(Unique)
还有一个参数是unique属性,它用来定义索引是否唯一。“唯一”索引确保索引的字段不存储重复值(就像平时常见的ID或者学号等等)。
默认情况下它是false,如果想启用唯一性,可以这样声明:
@Index(name = "uniqueIndex", columnList = "name", unique = true)
日志:
[main] DEBUG org.hibernate.SQL -
alter table Student add constraint uniqueIndex unique (firstName)
当我们在某个列上使用唯一索引的时候,就相当于再这个列上添加了一个唯一性约束。它的作用和“在@Column注解上使用约束”是相似的。
但由于@Index可以声明多个列的唯一约束(相当于联合主键,单个列的数据允许重复,只有约束中的所有列的数据都重复,才不被允许),使用@Index要优于使用@Column:
@Index(name = "uniqueMulitIndex", columnList = "firstName, lastName", unique = true)
3.5. 在单个实体上定义多个@Index索引
到目前为止,我们已经学习了索引的各个参数的功能。
而事实上,我们可以在一个实体上定义多个索引,多个索引的定义方法是——在大括号中重复使用@Index注解:
@Entity
@Table(indexes = {
@Index(columnList = "firstName"),
@Index(name = "test_index", columnList = "name"),
@Index(name = "mulitIndex1", columnList = "name, phoneNumber"),
@Index(name = "mulitIndex2", columnList = "phoneNumber, name"),
@Index(name = "mulitSortIndex", columnList = "name, phoneNumber DESC"),
@Index(name = "uniqueIndex", columnList = "name", unique = true),
@Index(name = "uniqueMulitIndex", columnList = "name, phoneNumber", unique = true)
})
public class Student implements Serializable
此外,我们还可以为同一组列创建多个索引。
3.6. 主键
当我们谈论索引时,必须要考虑主键。因为被实体管理器EntityManager来管理的每个实体都必须指定一个主键。
一般来说,主键是一种特定类型的唯一索引,与其他索引不同的是,主键不需要用前面介绍的@Index来声明,因为@Id 注解自动实现了这个功能。
3.7. 对于非实体(Entity)对象使用@Index
我们需要知道的是,@Index注解不仅仅能用在表(@Table)上,也能用于某些其他的对象,例如@SecondaryTable(多表联合)、@CollectionTable(集合表)、@JoinTable(连接表)、@TableGenerator(表生成器),这些知识不再详细讲解,请查看javax.persistence JavaDoc。
4. 结论
本文中我们讨论了如果使用JPA中的@Index索引,包括更改名称、包含的列、顺序、以及唯一性。后面我们讨论了主键,以及如何在其他对象中声明索引。
简单总结一下:
- 索引名称name,不使用名称时会自动生成
- 多个列columnList,列的顺序必须一致
- 排序ASC或DESC
- 唯一性
unique = true
- 主键不需要使用@Index,因为@Id完成了这个功能
本文中的示例可在 GitHub 上找到。