使用JPA指定索引(非唯一键)

113

如何使用JPA注释定义一个字段,例如email,并将其设置为索引。我们需要在email上创建一个非唯一键,因为每天有数百万次查询该字段,没有键会导致查询速度变慢。

@Entity
@Table(name="person", 
       uniqueConstraints=@UniqueConstraint(columnNames={"code", "uid"}))
public class Person {
    // Unique on code and uid
    public String code;
    public String uid;

    public String username;
    public String name;
    public String email;
}

我看到了一个特定于Hibernate的注释,但我想避免供应商特定的解决方案,因为我们仍在Hibernate和Datanucleus之间做决定。

更新:

从JPA 2.1开始,您可以这样做。请参阅:The annotation @Index is disallowed for this location


6
如果您能更新答案,让人们知道使用JPA 2.1确实有一种方法来做到这一点,那就太好了。 - borjab
2
为什么不采纳得分最高的答案? - naXa stands with Ukraine
11个回答

238

使用JPA 2.1版本,您应该能够完成它。

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.Table;

@Entity
@Table(name = "region",
       indexes = {@Index(name = "my_index_name",  columnList="iso_code", unique = true),
                  @Index(name = "my_index_name2", columnList="name",     unique = false)})
public class Region{

    @Column(name = "iso_code", nullable = false)
    private String isoCode;

    @Column(name = "name", nullable = false)
    private String name;

} 

更新:如果您需要创建一个包含两个或多个列的索引,可以使用逗号。例如:

@Entity
@Table(name    = "company__activity", 
       indexes = {@Index(name = "i_company_activity", columnList = "activity_id,company_id")})
public class CompanyActivity{

感谢Alvin的回答(+1票)。但是我还需要寻找一些东西,希望这个例子能让你的生活更轻松。 - borjab
2
正是我所需要的 - 一个简单干净的代码片段 - 不仅描述了整个事情。谢谢 +1。 - DominikAngerer
2
@borjab - 你能否给我一个提示,告诉我在@Table注释中使用@Index注释的“官方”示例或规范在哪里可以找到?我已经在JSR 338上搜索过了,但没有找到。你的帖子对我非常有用。 - JimHawkins
2
@Ulrich 不完全是官方文档,而是Oracle博客 https://blogs.oracle.com/arungupta/entry/jpa_2_1_schema_generation - borjab
@index 的好处是什么?它使用二分查找吗? - Ishan Garg
显示剩余2条评论

41

一个独特的手选的索引注释集合

= 规格 =

= ORM 框架 =

= Android 的 ORM =

= 其他(难以归类)=

  • Realm - 适用于iOS / Android的替代性数据库:使用注释io.realm.annotations.Index;
  • Empire-db - 基于JDBC的轻量级而功能强大的关系型数据库抽象层。它没有通过注释进行模式定义;
  • Kotlin NoSQL (GitHub) - 用于与NoSQL数据库工作的反应式和类型安全的DSL(概念验证):???
  • Slick - Scala的反应式函数关系映射。 它没有通过注释进行模式定义。

从中选择一个即可。


32
JPA 2.1(最终版)增加了对索引和外键的支持!请参阅此博客获取详细信息。JPA 2.1是Java EE 7的一部分,已经发布。
如果您想尝试最新版本,可以从maven仓库获取eclipselink的快照(groupId:org.eclipse.persistence,artifactId:eclipselink,version:2.5.0-SNAPSHOT)。如果只需要JPA注释(在任何提供程序支持2.1后都应该可以工作),请使用artifactID:javax.persistence,version:2.1.0-SNAPSHOT。
我正在使用它为一个项目服务,该项目直到发布后才完成,并且我没有注意到任何严重的问题(尽管我没有对其进行太复杂的操作)。
更新(2013年9月26日):现在,eclipselink的发行版和发布候选版本可在中央(主要)存储库中下载,因此您无需在Maven项目中添加eclipselink存储库。最新的发布版本是2.5.0,但也提供了2.5.1-RC3。我建议尽快切换到2.5.1,因为2.5.0版本存在问题(modelgen无法正常工作)。

10

在 JPA 2.1 中,您需要执行以下操作:

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

@Entity(name="TEST_PERSON")
@Table(
    name="TEST_PERSON", 
    indexes = {
       @Index(name = "PERSON_INDX_0", columnList = "age"),
       @Index(name = "PERSON_INDX_1", columnList = "fName"),
       @Index(name = "PERSON_INDX_1", columnList = "sName")  })
public class TestPerson {

    @Column(name = "age", nullable = false)
    private int age;

    @Column(name = "fName", nullable = false)
    private String firstName;

    @Column(name = "sName", nullable = false)
    private String secondName;

    @Id
    private long id;

    public TestPerson() {
    }
}

在上面的例子中,表TEST_PERSON将拥有3个索引:

  • 基于主键ID的唯一索引

  • AGE列的索引

  • 基于FNAME和SNAME两个列的复合索引

注意1:使用相同名称的两个@Index注释可以得到复合索引。

注意2:在columnList中指定列名而非字段名。


但复合索引的好处是什么?它在搜索时使用二分查找,或者强制执行唯一约束。 - Ishan Garg

5
我希望能够以标准化的方式指定数据库索引,但遗憾的是,这不是JPA规范的一部分(也许是因为JPA规范不需要DDL生成支持,这对于这样的功能是一种障碍)。
因此,您必须依靠特定于提供程序的扩展来实现。Hibernate、OpenJPA和EclipseLink显然都提供了这样的扩展。我无法确认DataNucleus是否提供索引定义,但既然索引定义是JDO的一部分,我猜它会提供。
我真的希望索引支持将在规范的下一个版本中得到标准化,因此与其他答案有些不同,我认为没有任何好的理由不在JPA中包含这样的东西(特别是因为数据库并不总是在您的控制之下),以获得最佳的DDL生成支持。
顺便说一句,我建议下载JPA 2.0规范。

4
据我所知,没有一种跨JPA提供程序的方法来指定索引。然而,您始终可以直接在数据库中手动创建它们,大多数数据库将在查询计划期间自动捕获它们。

2
哈哈,这是假设你有DBA的人来做DBA的工作(: - Jay
3
我觉得有一种方法可以使内容变得“独一无二”,但却没有实现一个索引的方法,感觉有些奇怪。 - Jay
7
@Jacob - 重要的是要在应用程序层面上知道某个字段是否唯一。而索引则是为了优化数据库访问而存在的。在Java层面上,我认为不需要知道某个列是否为索引。正如你所说,如果某个特定列会受益于索引,数据库管理员可以设置索引。 - Java Drinker
1
@Jacob,没有索引支持是因为那只是一种优化(通常是重要的优化,但仍然是一种优化)。另一方面,如果一个字段(或一组字段)是唯一的还是不唯一的取决于模型,并且会影响正确性。此外,不需要完整的200美元/小时的DBA,一些简单的索引创建语句通常就足够了。 - Tassos Bassoukos
8
JPA 2.1规定了javax.persistence.Index - Lukas Eder

2
EclipseLink提供了一个注释(例如@Index)来定义列上的索引。这里有一个示例说明它的用法。示例中包含了firstName和lastName字段的索引,可以单独或一起使用。
@Entity
@Index(name="EMP_NAME_INDEX", columnNames={"F_NAME","L_NAME"})  // Columns indexed together
public class Employee{
    @Id
    private long id;

    @Index                      // F_NAME column indexed
    @Column(name="F_NAME")
    private String firstName;

    @Index                      // L_NAME column indexed
    @Column(name="L_NAME")
    private String lastName;
    ...
}

1

OpenJPA允许您指定非标准注释来定义属性上的索引。

详细信息请参见此处


1

0

这个解决方案适用于EclipseLink 2.5,并且它是可行的(已测试):

@Table(indexes = {@Index(columnList="mycol1"), @Index(columnList="mycol2")})
@Entity
public class myclass implements Serializable{
      private String mycol1;
      private String mycol2;
}

这假定是升序排列。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接