I have a case where I'm persisting a large jsonb field into a PostGres table, but do not want to read it when I fetch the entity; if I do fetch it, my service goes OOM. A better design might be to separate this into a 1 to 1 table, but I can't do that at this time.
To plead that this is not a duplicate question, here's some of my research:
I'm not able to mark the column LAZY since I have a simple column not a join`
JPA/Hibernate write only field with no read
I tried the empty setter in this suggestion, which makes sense - but it still appears to read the column and I OOM: https://www.zizka.ch/pages/programming/java/hibernate/hibernate-write-only.html
I also tried omitting the setter altogether in my #Data class: Omitting one Setter/Getter in Lombok
So, I can not see the field, but I can't seem to keep it from being read into memory in the background. It seems like there must be some simple setting in JPA or Hibernate to exclude a column from read. Before I go try to make a complex repository hierarchy just to see if it works, I thought I would ask here in case I get lucky.
Thanks in advance!
Lazy loading attributes
Hibernate can load attribute lazily, but you need to enable byte code enhancements:
First you need to set the property hibernate.enhancer.enableLazyInitialization to true
Then you can annotate the field with #Basic( fetch = FetchType.LAZY ).
Here's the example from the documentation:
#Entity
public class Customer {
#Id
private Integer id;
private String name;
#Basic( fetch = FetchType.LAZY )
private UUID accountsPayableXrefId;
#Lob
#Basic( fetch = FetchType.LAZY )
#LazyGroup( "lobs" )
private Blob image;
//Getters and setters are omitted for brevity
}
You can also enable this feature via the Hibernate ORM gradle plugin
Named Native queries
You could also decide to not map it and save/read it with a named native query. It seems a good trade off for a single attribute - it will just require an additional query to save the json.
Example:
#Entity
#Table(name = "MyEntity_table")
#NamedNativeQuery(
name = "write_json",
query = "update MyEntity_table set json_column = :json where id = :id")
#NamedNativeQuery(
name = "read_json",
query = "select json_column from MyEntity_table where id = :id")
class MyEntity {
....
}
Long id = ...
String jsonString = ...
session.createNamedQuery( "write_json" )
.setParameter( "id", id )
.setParameter( "json", jsonString )
.executeUpdate();
jsonString = (String)session.createNamedQuery( "read_json" )
.setParameter( "id", id )
.getSingleResult();
In this case, schema generation is not going to create the column, so you will need to add it manually (not a big deal, considering that there are better tools to update the schema in production).
MappedSuperclass
You can also have two entities extending the same superclass (this way you don't have to copy the attributes). They have to update the same table:
#MappedSuperclass
class MyEntity {
#Id
Long id;
String name
...
}
#Entity
#Table(name = "MyEntity_table")
class MyEntityWriter extends MyEntity {
String json
}
#Entity
#Table(name = "MyEntity_table")
class MyEntityReader extends MyEntity {
// No field is necessary here
}
Now you can use MyEntityWriter for saving all the values and MyEntityReader for loading only the values you need.
I think you will have some problems with schema generation if you try to create the tables because only one of the two will be created:
If MyEntityWriter is the first table created, then no problem
If MyEntityWriter is the second table created, the query will fail because the table already exist and the additional column won't be created.
I haven't tested this solution though, there might be something I haven't thought about.
I have a custom POJO on which I am mapping the database records using JOOQ .fetchInto(TestClassDto.class). Most of the fields in my POJO are exactly similar to a database table's columns. However, there are a few that are different, therefore, I added java persistence and used #Column to explicitly map such columns on my POJO as described here.
Unfortunately, this is not working if I use #Column on a few specific fields. Only the fields that are annotated with #Column are mapped and the rest are ignored and set Null even though they are similar to the table column name and should be mapped implicitly.
Could you give me a hint if I am missing anything?
Sample POJO:
#Getter
#Setter
public class TestClassDto {
#Column(name = "field_AB_XYZ") // explicit mapping is required, thus need #Column
private Long myfieldAB;
/* Here, mapping could be implicitly done without using #Column because
** database column name and POJO property is same but it stays empty if I
** ignore #Column */
#Column(name = "hello_world")
private Long helloWorld;
}
Lastly, If I completely remove #Column from POJO's properties, helloWorld property is filled (implicitly) but myfieldAb remains NULL (because mapping is not found as expected).
Below is sample query:
dslContext.select()
.from(SOMETHING)
.where(SOMETHING.NAME.eq("Something"))
.fetchInto(TestClassDto.class)
As of jOOQ 3.15, you either have to annotate
all of your attributes...
none of your attributes...
... with the #Column annotation. There's a pending feature request to mimick JPA more closely and make the #Column annotation optional for some attributes: https://github.com/jOOQ/jOOQ/issues/4586.
In the meantime, instead of using those JPA annotations, you could add auxiliary getters/setters for your column:
public void setFieldAbXyz(Long v) {
this.myfieldAB = v;
}
public Long getFieldAbXyz() {
return myfieldAB;
}
I built an application with Quarkus and I'm using Hibernate with Panache for the models. Everything goes well, the application starts, but when I call a webservice to get a list using Panache functionalities (.listAll()), I get an empty list and I see the following message in the console:
HHH000183: no persistent classes found for query class: from com.myproject.model.TeamEntity
My models are defined with #Entity annotations that should allow Hibernate to find by itself the entity mappings. Here is an example with the Team model:
#Entity
#Table(name = "TEAM")
public class TeamEntity extends PanacheEntityBase {
#Id
#GeneratedValue(strategy = SEQUENCE, generator = "TEAM_SEQ_GEN")
#SequenceGenerator(name = "TEAM_SEQ_GEN", sequenceName = "TEAM_SEQ", allocationSize = 10)
#Column(name = "ID_TEAM", nullable = false)
private int id;
#Column(name = "NAME", nullable = false)
private String name;
...
}
I don't have any persistence.xml file in the project, only the application.properties linked with Quarkus. Here are the relevant properties extracted from mine:
quarkus.datasource.db-kind=oracle
quarkus.datasource.jdbc.url=jdbc:oracle:thin:/#MYWALLET
%dev.quarkus.datasource.jdbc.url=jdbc:oracle:thin:MYUSER/MYPASSWORD#localhost:1521/SAA
quarkus.datasource.jdbc.driver=oracle.jdbc.OracleDriver
quarkus.datasource.jdbc.min-size=2
quarkus.datasource.jdbc.max-size=10
quarkus.datasource.jdbc.new-connection-sql=alter session set current_schema=MYSCHEMA
quarkus.hibernate-orm.dialect=org.hibernate.dialect.Oracle12cDialect
Does someone know where the problem could come from ? Hibernate should detect entities with annotations and use them in queries automatically.
It came out that the problem was on Quarkus Datasource configuration in the application.properties file. More particularly from this specific line to define the schema used at first connection (I have to admit that was not good looking):
quarkus.datasource.jdbc.new-connection-sql=alter session set current_schema=MYSCHEMA
Replacing the line above with the following solved the problem:
quarkus.hibernate-orm.database.default-schema=MYSCHEMA
In conclusion, I think Hibernate cannot find / does not take the entities defined if this property is not defined, maybe because it makes some kind of detection beforehand. That's only a supposition, if someone knows more precisely how Hibernate works for that specific case, I would be very interested !
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
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.