Specifying an Index (Non-Unique Key) Using JPA - java

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

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{

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.

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).

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

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.

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.

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

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

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.

It's not possible to do that using JPA annotation. And this make sense: where a UniqueConstraint clearly define a business rules, an index is just a way to make search faster. So this should really be done by a DBA.

This solution is for EclipseLink 2.5, and it works (tested):
#Table(indexes = {#Index(columnList="mycol1"), #Index(columnList="mycol2")})
#Entity
public class myclass implements Serializable{
private String mycol1;
private String mycol2;
}
This assumes ascendant order.

Related

Best Way to SELECT subset of properties for associated #Entity

I am trying to choose the properties of associated entities that will be loaded.
For example:
#Entity
#Getter #Setter
public class Book {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "first")
private String first;
#Column(name = "second")
private String second;
#OneToMany(mappedBy = "book", cascade = CascadeType.ALL)
private List<Page> pages = new ArrayList();
}
#Entity
#Getter #Setter
public class Page {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "number")
private Integer number;
#Column(name = "content")
private String content;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "book_id")
private Book book;
}
I would like to have just a SELECT on the Book's first and the content of all associated Page entities
book
- first
- pages
- content
For example, in PostgreSQL this might look something like
SELECT book.first,
array_agg(page.content || ' ')
FROM book
LEFT JOIN page
ON page.book_id = book.id
GROUP BY book.first
I've done my research on how you could go about doing this, most mentioned solution is to use Spring DATA JPA Projections (ref)
I did the following:
public interface FindAllBookProjection {
String getFirst();
List<PageProjection> getPages();
interface PageProjection {
Integer getNumber();
}
}
The issue with projections is that they cause the famous N+1 select problem as the pages are loaded lazily.
I also couldn't find a way to use projections with #Query in the #Repository for the nested properties.
There are other mentions online to use #EntityGraph. From what I understand #EntityGraph will not work as it also selects all properties, specifying only what Entity associations should be loaded.
There are also suggestions on using other libraries like Blaze Persistence (ref) or Hibernate's ResultTransformer (ref) . I would prefer to use only Spring DATA JPA instead of introducing another library for this functionality and writing ResultTransformers seems like adding a lot of boilerplate code to the mix.
To summarize my question is what is the best way to choose what properties are selected for #Entity associations. The main goal is to avoid pulling unnecessary amount of data from the database. The given example above is for demonstration, the data that I am working on includes over 10 columns and spans across 3-4 entities. Having control over the data means better performance.
I am the creator of Blaze-Persistence and I can tell you that if there were an easy way to do this, I would not have created Entity-Views. You can read some of the articles I wrote about this and you will realize that doing this yourself, like you already figured, will require lots of boilerplate code. Doing this efficiently, will require even more code and in the end, you will probably end up with a solution that is inferior to Blaze-Persistence Entity-Views in one way or another and has probably no documentation.
Spring Data Projections is just limited and as I tried to outline many times before, Blaze-Persistence Entity-Views is like Spring Data Projections on steroids.
If you have "just" one or two simple mappings, you might be able to get this done by introducing special #Immutable #Entity, maybe even with #Subselect in this particular case to model what you need, but believe me, this only works good on a small scale. Apart from that, Blaze-Persistence which works on top of JPA/Hibernate enables the use of a lot of advanced SQL features, which you usually can't use from within plain JPA/Hibernate.
In my opinion, ORM libraries should use whole objects, that means loading all data into the program and then transforming/filtering according to logic.
For specific use-cases, where performance is really hindered, I'd use entities in
the database, such as Views/Procedures.
In your case, i'd create a View:
CREATE VIEW book_content as
SELECT book.first as [first],
array_agg(page.content || ' ') as [content]
FROM book
LEFT JOIN page
ON page.book_id = book.id
GROUP BY book.first
And then create a #Repository and #Entity for it in spring.

Hibernate Search save entity to Elastic search

I trye to use Hibernate Search with elastic search, for full text search.
Simple entitys work correctly but with one i have a troube.
When i setup annotation #FullTextField on a data column i got an error when application starting:
"Hibernate ORM mapping:
type 'ru.search.entities.TestEntity':
path '.data':
failures:
- HSEARCH000135: No default value bridge implementation for type 'java.lang.Object'. Use a custom bridge."
This my entity:
#Entity
#Indexed(index = "test")
#Table(name = "test")
#TypeDef(name = "jsonb", typeClass = Jsonb.class)
public class TestEntity {
#Id
#Column(name = "id")
private UUID Id;
#Column(name = "data")
#Type(type = "jsonb")
private Map<String, Object> data;
//constructor
//getters
//setters
}
Without #FullTextField annotation entity puts correctly in postgres and elastic. Elastic of course do not contain data column.
By default when you annotate a Map with #FullTextField (or any #*Field annotation), Hibernate Search will index the values. In you case, values are of type Object. Hibernate Search doesn't know how to index an Object.
I suspect you want to index arbitrary JSON data. If that's the case, I believe there's a solution for you right in the documentation examples:
Read this section first to get some context on what property bridges are and how you can use them.
Then you can find a (more complicated) example that does almost exactly what you want here.
If that's not what you need, I suggest exploring the documentation:
You can find a list of supported property types here
You can find help on mapping other property types here

How to map to an existing Hibernate model using jOOQ fetchInto()?

I'm trying to use the jOOQ fetchInto() method to map to an existing Hibernate model Organization (class and its inheritances are below).
Organization organization = jooq().select().from(ORGANIZATION).fetchOne().into(Organization.class);
The problem I have is that I can't really understand what happens in DefaultRecordMapper as I feel I'm not entirely familiar with all the terms that are used. I'm trying to figure out how it applies to the Hibernate classes that are in my codebase.
So far what I've tried:
Use the jOOQ generated POJO's to see if it retrieves and maps the data at all (works).
Add a constructor, getters and setters to the Organization Hibernate model.
Add #Column annotation to name in the Organization Hibernate model.
What works:
id field gets mapped correctly.
What doesn't work:
name field doesn't get mapped (null).
createdAt and modifiedAt fields do not get mapped (null).
My question is: Is there something I am overlooking with the mapping and what are the things I should look at concerning the classes, fields, constructors and annotations with Hibernate models? I want to eventually map all the Hibernate models in the codebase and use fetchInto to do that.
Thanks! :)
#Entity
public class Organization extends BaseModel {
#Required public String name;
//... a lot of other code
}
#MappedSuperclass
public class BaseModel extends Model {
/** The datetime this entity was first saved. Automatically set by a JPA prePersist */
#NoBinding
#Column
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
public DateTime createdAt;
/** The datetime this entity was last modified. Automatically set by a JPA preUpdate */
#NoBinding
#Column
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
public DateTime modifiedAt;
//...
}
#MappedSuperclass
public class Model extends GenericModel { // Both Model and GenericModel are from the Play Framework
#Id
#GeneratedValue
public Long id;
public Model() {
}
public Long getId() {
return this.id;
}
public Object _key() {
return this.getId();
}
}
jOOQ doesn't support all the many JPA and Hibernate specific annotations. Historically, it supported a few JPA annotations (because why not), but full interop would be excessive and investing product development time in the wrong places. jOOQ is by no means a JPA implementation.
Step 0: Why didn't (some) of the mappings work?
As mentioned before, not all JPA specification is implemented. For example, a known issue is that #Column annotations are still mandatory in jOOQ:
https://github.com/jOOQ/jOOQ/issues/4586
There might be other such limitations, which could be considered bugs. Feel free to report them if you want to continue down this path: https://github.com/jOOQ/jOOQ/issues/new/choose
But things like #MappedSuperclass or #Type are unlikely to ever be supported by jOOQ.
Step 1: Do you really need it?
You've decided to create and run your query with jOOQ. I imagine your actual query is much more complex than what you're showing, because for that particular query, you don't need jOOQ.
Do you really need to map to Hibernate entities? Because even when you use Hibernate, the recommended approach is to use entities only when you're going to modify them and store the delta back to the database. If that's the case, see step 2 below. If it's not the case, why not use jOOQ's own mapping functionality to work with any style of jOOQ supported POJO?
Step 2: Use Hibernate to execute the jOOQ query
If you're using jOOQ only to build a rather complex SQL query and you need Hibernate entities as a result, then use Hibernate to execute the jOOQ query as documented here. A small utility should be enough:
public static <E> List<E> nativeQuery(EntityManager em, org.jooq.Query query, Class<E> type) {
Query result = em.createNativeQuery(query.getSQL(), type);
List<Object> values = query.getBindValues();
for (int i = 0; i < values.size(); i++)
result.setParameter(i + 1, values.get(i));
return result.getResultList();
}

Hibernate JPA uniqueness

Let's say that this is a class that has unique constrained field.
#Entity
public class Thing {
#Column(name = "name", unique = true)
private String name;
#ManyToOne
private Owner owner;
}
Example works just fine if new Things are created with unique names. But when different owners want to create things with the same name this approach fails.
Is it possible to set unique constraint to differ records of Things in the database based on the Owners using Hibernate/JPA functionalities (I could not find any) or should I write my own logic and dump the unique from #Column.
Perhaps it could be done with Hibernate Validator? Reading the docs I haven't found much about unique constraints.
You're looking for #UniqueConstraint
http://docs.oracle.com/javaee/5/api/javax/persistence/UniqueConstraint.html

Hibernate not JPA compliant regarding #Access?

According to my JPA 2.0 book (and online documentation), I should be able to mix field and property access within a single entity or entity hierarchy. The annotation of #Access on the class specifies the default access. When placed on a field or property getter #Access can specify that the default should be overridden for this field.
#Entity
#Access(AccessType.FIELD)
Class Foo {
#Id
int id;
#Column(name = "myfield")
String myField;
#Column(name = "myProp")
#Access(AccessType.PROPERTY)
public int getMyProp () {
return 3;
}
public void setMyProp (int p) {
// do nothing
}
}
This class should result in a table with three columns. However it doesn't with Hibernate...the "myProp" column is missing from the table because apparently Hibernate takes its field vs property cue from the entity ID and runs with it...totally ignoring the JPA spec with regards to #Access.
Can anyone confirm this or did I make a stupid mistake somewhere?
I've seen similar (not the same but similar) issues like HHH-5004 so I wouldn't exclude that this might be a new one (the TCK doesn't seem exhaustive). But what version of Hibernate are you using? Did you try with the latest?
Based on the docs your code seems to be right. The #Access(AccessType.FIELD) annotation on top is unnecessary, because you annotated the field int id;
This tells hibernate to use field access. I tried a very similar example with annotations and xml config mixed. This leads to the same behaviour, so it's probably a bug in hibernate.
I tried with hibernate 3.5.3

Categories

Resources