@Indices{
	@Index(members={"field1","field2"}, unique="true", name="index1"),
	@Index(members={"field1"}, name="index2"),
}

如果检索时没有索引或者说没有可用的索引,那么数据库会逐条遍历所有数据,以判断每条数据是否匹配。在数据量大的情况下,这种操作很耗时。

如果检索时有可用的索引,效率会有几何级的增长,因此需要仔细分析需求,以建立合适的索引。

ObjectDB为每个索引管理一个BTree,此BTree数据被数据库服务器维护在文件系统中。BTree的每个节点的键是索引的字段的所有去重后的值,又在每个节点上维护了一个字段值为此节点的键的所有实体引用的列表。

单字段索引

JPA 并没有对索引的声明给出一个标准的方法,但是JDO对此给出了规范。下面的例子就是用JDO的注解定义的索引:

import javax.jdo.annotations.Index;
import javax.jdo.annotations.Unique;

@Entity
public class EntityWithSimpleIndex {
    @Index String indexedField1;
    @Index(unique="true") int indexedField2; // unique
    @Index(name="i3") int indexedField3;
    @Unique Integer indexedField4; // unique
    @Unique(name="u2") Date indexedField5; // unique
}

@Unique 注解指明索引是唯一索引,即此字段的值不能重复,每条数据在此字段上的值,彼此都必须不相同。否则会抛出异常。

name可选项,指定索引名称,在应用程序中不会用到,但是日志和分析中很有用。

索引只能定义在可持久化字段上(不包括主键和version字段)

多字段复合索引

复合索引指在多个字段上建立一个联合索引,通过在Entity类上加 @Index(members={“field1”,“field2”},unique=true),或者@Unique(members={"",""})来实现

@Entity
@Indices({
    @Index(members={"lastName","firstName"})
    @Index(members={"firstName"}, unique="true")
}
public class EntityWithCompositeIndex {
    String firstName;
    String lastName;
}
多层级路径索引、多层级多字段索引
@Entity
@Index(members={"addresses.city"})
@Index(members={"addresses.city,addresses.street"})
public class Employee {
    List<Address> addresses;
     :
}
@Emdeddable
public class Address implements Serializable{
	String street;
	String city;
}

注意:可以建立索引的字段,必须是随Entity的实例一同存储的字段,而非单独存储。

注意:多层级多字段的索引,所有字段的路径都必须相同,下面的索引是无法创建的:

@Entity
@Index(members={"lastName", "address.city"}) // INVALID
public class Employee {
    String firstName;
    String lastName;
    Address address;
     :
}
索引在查询中的作用

索引对等值查询和范围查询的效率最明显,在以下的例子中,如果对字段x创建了索引,那么只需要迭代相关的 BTree的分支,而不是迭代所有数据,因此效率很高。

SELECT p FROM Point p WHERE p.x = 100
SELECT p FROM Point p WHERE p.x BETWEEN 50 AND 80
SELECT p FROM Point p WHERE p.x >= 50 AND p.x <= 80

在上面的例子中,如果没有对字段x单独创建索引,但是存在一个字段x和其它字段比如字段y的复合索引,那么上面的查询也能有效利用到这个复合索引。

多字段复合索引中,定义索引时的字段顺序很关键

在多字段复合索引中,如果定义索引时字段X为第一个字段,BTree就会按照字段X的去重值进行排序。

@Entity
@index(members={"x","y"})
public class MyEntity implements Serializable{
	int x;
	int y;
}

那么检索时如果是对字段x的 range查询,就能利用上这个索引,如下:

select e from MyEntity e where x between 20 and 50.

而如果查询仅仅是对字段y的range 查询,那么也会利用此索引,但是会对此索引的全部节点进行迭代(因为节点顺序是按照字段x进行排序的,而查询并没有对字段x做任何约束,因此无法将范围缩小到某些分支),然后再利用每个节点下的针对Y的BTree。虽然这种方式要迭代BTree的所有节点,但是利用到此索引可能仍然比不用索引而去对所有数据逐条迭代判断是否匹配效率更高。如下:

select e from MyEntity e where y between 20 and 50.
检索时的字段顺序也很关键

如果复合索引定义时的字段顺序是x,y,那么检索时查询语句的字段顺序最好也是x,y?? 。

在集合上建索引,对于join查询很有帮助
@index
List<String>  hobbys;
select u from User u  join u.hobbys h where  h="swming"

在字段hobbys上的索引将会维护所有User实例的每一个hobby值(将List中的每一个元素都作为一个键值)。
上面的索引也能用于以下检索,但是会对所有节点进行迭代,因为索引是对字符串值按照字典顺序进行排序的,而非按照字符串长度进行排序的,因此无法将范围缩小到某些分支。

select u from User u  join u.hobbys h where  length(h)>10
有排序要求的查询,也能用到索引

因为索引本身是维护某字段的值的排序,所以下面的例子中,如果有一个对x、y的复合索引,那么where 子句和 order by子句都能利用到。

select min(x) from User  where x<y  order by y

但是where子句是对x和y做比较,所以需要对BTree的所有节点进行迭代。
此外,如果索引定义时的第一个字段是y字段,将能最大限度的利用到索引,因为迭代所有节点直接按照索引维护的y的值的顺序即可。

min max 子句也能利用到索引

如果对字段x创建了索引,那么以下子句无需任何迭代,时间复杂度是O(1)。

select min(x),max(x)  from User ;