Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specifying an Index (Non-Unique Key) Using JPA

How do you define a field, eg email as having an index using JPA annotations. We need a non-unique key on email because there are literally millions of queries on this field per day, and its a bit slow without the key.

@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;
}

I have seen a hibernate specific annotation but I am trying to avoid vendor specific solutions as we are still deciding between hibernate and datanucleus.

UPDATE:

As of JPA 2.1, you can do this. See: The annotation @Index is disallowed for this location

like image 459
Jay Avatar asked Aug 04 '10 11:08

Jay


People also ask

How does JPA indicate foreign key?

Implementing With a Foreign Key in JPA. Note that we place the @OneToOne annotation on the related entity field, Address. Also, we need to place the @JoinColumn annotation to configure the name of the column in the users table that maps to the primary key in the address table.

Is @column mandatory in JPA?

@Column. Let's start with the @Column annotation. It is an optional annotation that enables you to customize the mapping between the entity attribute and the database column.

What is @ID annotation in JPA?

In JPA the object id is defined through the @Id annotation and should correspond to the primary key of the object's table. An object id can either be a natural id or a generated id. A natural id is one that occurs in the object and has some meaning in the application.

Does JPA require primary key?

Every JPA entity must have a primary key. You can specify a primary key as a single primitive, or JDK object type entity field (see "Configuring a JPA Entity Simple Primary Key Field").


9 Answers

With JPA 2.1 you should be able to do it.

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;

} 

Update: If you ever need to create and index with two or more columns you may use commas. For example:

@Entity
@Table(name    = "company__activity", 
       indexes = {@Index(name = "i_company_activity", columnList = "activity_id,company_id")})
public class CompanyActivity{
like image 179
borjab Avatar answered Oct 11 '22 09:10

borjab


A unique hand-picked collection of Index annotations

= Specifications =

  • JPA 2.1+: javax.persistence.Index (or see JSR-000338 PDF, p. 452, item 11.1.23)
    The JPA @Index annotation can only be used as part of another annotation like @Table, @SecondaryTable, etc.:

    @Table(indexes = { @Index(...) })
    
  • JDO 2.1+: javax.jdo.annotations.Index

= ORM Frameworks =

  • ♥ Hibernate ORM: org.hibernate.annotations.Index;
  • OpenJPA: org.apache.openjpa.persistence.jdbc.Index and org.apache.openjpa.persistence.jdbc.ElementIndex (see Reference Guide);
  • EclipseLink: org.eclipse.persistence.annotations.Index;
  • DataNucleus: org.datanucleus.api.jpa.annotations.Index;
  • Carbonado (GitHub): com.amazon.carbonado.Index;
  • EBean: com.avaje.ebean.annotation.Index or io.ebean.annotation.Index ?
  • Ujorm: Annotation org.ujorm.orm.annot.Column, index and uniqueIndex properties;
  • requery (GitHub. Java, Kotlin, Android): Annotation io.requery.Index;
  • Exposed (Kotlin SQL Library): org.jetbrains.exposed.sql.Index, org.jetbrains.exposed.sql.Table#index(). Example:

    object Persons : IdTable() {
        val code = varchar("code", 50).index()
    }
    

= ORM for Android =

  • ♥ ActiveAndroid: Annotation com.activeandroid.annotation.Column has index, indexGroups, unique, and uniqueGroups properties;
    UPDATE [2018]: ActiveAndroid was a nice ORM 4 years ago, but unfortunately, the author of the library stopped maintaining it, so someone forked, fixed bugs, and rebranded it as ReActiveAndroid - use this if you're starting a new project or refer to Migration Guide if you want to replace ActiveAndroid in a legacy project.
  • ReActiveAndroid: Annotation com.reactiveandroid.annotation.Column has index, indexGroups, unique, and uniqueGroups properties;
  • ORMLite: Annotation com.j256.ormlite.field.DatabaseField has an index property;
  • greenDAO: org.greenrobot.greendao.annotation.Index;
  • ORMAN (GitHub): org.orman.mapper.annotation.Index;
  • ★ DBFlow (GitHub): com.raizlabs.android.dbflow.sql.index.Index (example of usage);
  • other (lots of ORM libraries at the Android Arsenal).

= Other (difficult to categorize) =

  • Realm - Alternative DB for iOS / Android: Annotation io.realm.annotations.Index;
  • Empire-db - a lightweight yet powerful relational DB abstraction layer based on JDBC. It has no schema definition through annotations;
  • Kotlin NoSQL (GitHub) - a reactive and type-safe DSL for working with NoSQL databases (PoC): ???
  • Slick - Reactive Functional Relational Mapping for Scala. It has no schema definition through annotations.

Just go for one of them.

like image 40
naXa Avatar answered Oct 11 '22 11:10

naXa


JPA 2.1 (finally) adds support for indexes and foreign keys! See this blog for details. JPA 2.1 is a part of Java EE 7, which is out .

If you like living on the edge, you can get the latest snapshot for eclipselink from their maven repository (groupId:org.eclipse.persistence, artifactId:eclipselink, version:2.5.0-SNAPSHOT). For just the JPA annotations (which should work with any provider once they support 2.1) use artifactID:javax.persistence, version:2.1.0-SNAPSHOT.

I'm using it for a project which won't be finished until after its release, and I haven't noticed any horrible problems (although I'm not doing anything too complex with it).

UPDATE (26 Sep 2013): Nowadays release and release candidate versions of eclipselink are available in the central (main) repository, so you no longer have to add the eclipselink repository in Maven projects. The latest release version is 2.5.0 but 2.5.1-RC3 is also present. I'd switch over to 2.5.1 ASAP because of issues with the 2.5.0 release (the modelgen stuff doesn't work).

like image 31
Alvin Thompson Avatar answered Oct 11 '22 10:10

Alvin Thompson


In JPA 2.1 you need to do the following

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() {
    }
}

In the above example the table TEST_PERSON will have 3 indexes:

  • unique index on the primary key ID

  • index on AGE

  • compound index on FNAME, SNAME

Note 1: You get the compound index by having two @Index annotations with the same name

Note 2: You specify the column name in the columnList not the fieldName

like image 45
James Clayton Avatar answered Oct 11 '22 10:10

James Clayton


I'd really like to be able to specify database indexes in a standardized way but, sadly, this is not part of the JPA specification (maybe because DDL generation support is not required by the JPA specification, which is a kind of road block for such a feature).

So you'll have to rely on a provider specific extension for that. Hibernate, OpenJPA and EclipseLink clearly do offer such an extension. I can't confirm for DataNucleus but since indexes definition is part of JDO, I guess it does.

I really hope index support will get standardized in next versions of the specification and thus somehow disagree with other answers, I don't see any good reason to not include such a thing in JPA (especially since the database is not always under your control) for optimal DDL generation support.

By the way, I suggest downloading the JPA 2.0 spec.

like image 39
Pascal Thivent Avatar answered Oct 11 '22 11:10

Pascal Thivent


As far as I know, there isn't a cross-JPA-Provider way to specify indexes. However, you can always create them by hand directly in the database, most databases will pick them up automatically during query planning.

like image 39
Tassos Bassoukos Avatar answered Oct 11 '22 11:10

Tassos Bassoukos


EclipseLink provided an annotation (e.g. @Index) to define an index on columns. There is an example of its use. Part of the example is included...

The firstName and lastName fields are indexed, together and individually.

@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;
    ...
}
like image 26
Nathan Avatar answered Oct 11 '22 09:10

Nathan


OpenJPA allows you to specify non-standard annotation to define index on property.

Details are here.

like image 32
expert Avatar answered Oct 11 '22 10:10

expert


To sum up the other answers:

  • Hibernate: org.hibernate.annotations.Index
  • OpenJPA: org.apache.openjpa.persistence.jdbc.Index
  • EclipseLink: org.eclipse.persistence.annotations.Index

I would just go for one of them. It will come with JPA 2.1 anyway and should not be too hard to change in the case that you really want to switch your JPA provider.

like image 36
Jack Avatar answered Oct 11 '22 11:10

Jack