Removing duplication in Hibernate annotations - java

We are using Hibernate 3.6.x and Joda-time, persisting DateTime with a org.jadira.usertype.dateandtime.joda.PersistentDateTimeWithZone like so:
#Columns(columns = {
#Column(nullable = false, name = "requestedOn"),
#Column(nullable = false, name = "requestedOnTZ") })
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTimeWithZone")
private DateTime requestedOn;
Is there a way to hook into Hibernate in order to hide the annotations above behind one annotation, like:
#DateTimeWithZone(time = "requestedOn", zone = "requestedOnTZ")
private DateTime requestedOn;
?

I don't think you can. Those are standard annoatations that the API expects to find while processing your code (scanning for annotations in this case. You can hack your hibernate and modify it's annotation scanner to take into account a custom annotation you've made, but this will spell trouble later when you want to migrate to a newer version of Hibernate.

Related

#CreatedDate annotation does not work with spring-data-elasticsearch

I'm using spring-boot-data-elasticsearch to persist the documents into elasticsearch.
Below is my Document Object for it.
#Builder
#Document(indexName = "testidx", createIndex = false)
public class Book {
#Id
private final String _id;
#NotNull
#CreatedDate
#Field(type = FieldType.Date, format = DateFormat.basic_date_time)
private final Instant regTime;
#NotNull
#Field(type = FieldType.Text)
private final String bookName;
}
However, when I saved the object into the ElasticSearchRepository, the regTime(which has to be created and saved automatically by #CreatedDate), has not been saved.
elasticRepository.save(
Book.builder()
.bookName("test")
.build()
);
GET /testidx/_search
{
"_index" : "testidx",
"_type" : "_doc",
"_id" : "M3IS_XgBGpcXDVxetXXn",
"_score" : 1.0,
"_source" : {
"_class" : "{test class Name}",
"bookName" : "test",
}
}
Could you please let me know what I have been missed?
I'm working on the spring-boot 2.4.5 (and spring-boot-starter-data-elasticsearch 2.4.5 also) and ElasticSearch 7.12.0.
Please check the documentation about auditing (https://docs.spring.io/spring-data/elasticsearch/docs/4.2.0/reference/html/#elasticsearch.auditing).
Your entity needs to implement the Persistable interface and needs to reimplement the isNew() method (see section 9.2.1).
Note: I just saw that in the documented entity sample the #CreatedDate and #CreatedBy annotations are missing. I will fix the docs, the annotation is needed of course.
Then do you have #EnableElasticsearchAuditing on your Spring Boot application or any other configuration class? This is needed as well.
Unfortunately spring-data-elasticsearch does not support all persistence related annotations, neither all of the mapping-annotations of JPA, nor the auditing-annotations of Spring-Data, where #CreatedDate belongs to.
Solution
So I would drop it and just use the basic #Field annotation.
Then your property regTime should show up in JSON request as sibling above bookName and be persisted as expected.
However, without the special feature of Spring-Data's #CreatedDate annotation (convenience for auditing), you have to care for initialization yourself, set the value before persisting, e.g. using regTime = Instant.now() (either in field definition, in constructor, or else).
Background
See the docs for Meta Model Object Mapping, 6.1.1. Mapping Annotation Overview which currently (version 4.2) only includes these field annotations:
#Id
#Field
#GeoPoint
See also the official guide 8. Elasticsearch Repositories.
Formerly there was an enum SUPPORTED_ID_PROPERTY_NAMES in SimpleElasticsearchPersistentProperty which listed these field-annotations.
See similar question: Does Spring Data Elasticsearch supports #Id annotation from JPA?
This tutorial helped me as introduction to elasticsearch-data:
Spring Boot Elastic Search Example - Java Developer Zone

Spring, hibernate entity fields depending on profile

i have something like this:
#Entity
#Table(name = "schedules")
public class ScheduleDO {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "begin_time", columnDefinition = "TIMESTAMP WITH TIME ZONE",
nullable = false)
private OffsetDateTime beginTime;
}
And i want to be able to change the "beginTime" variable anotation based on active profile. Something like this:
#Profile("dev")
#Column(name = "begin_time_dev", columnDefinition = "TIMESTAMP WITH TIME ZONE",
nullable = false)
#Profile("test")
#Column(name = "begin_time_test", columnDefinition = "TIMESTAMP WITH TIME ZONE",
nullable = false)
private OffsetDateTime beginTime;
is something like that possible?
Actually JPA/Hibernate know nothing about spring so profiles is out of their scope. Moreover entities are not beans so spring don't use them
The only solution I can see is to define a placeholder {profile_begin_time_test} and add an interceptor (see the example).
In the method
public String onPrepareStatement(String sql)
In the sql generated by hibernate replace the {profile_begin_time_test} placeholder with desired real column name. The placeholder replacement can be configured to use value based on spring profiles.
You could do this by creating a custom Hibernate UserType and registering it on your beginTime field via the Hibernate #Type annotation.
In your new UserType, you could then get the current profile from the Spring Environment to determine the target column name. You'll have to statically register the Environment in your application somehow first, as your UserType will have been instantiated by Hibernate and won't know about your Spring application context. All doable though!
I think that you could possibly :
1)Create two different fields and different setters.
2)Add all JPA annotations on method level rather than on fields.
3)Since spring 4.1 you can profile methods, so use profiles on setters.
The #Profile annotation may be used in any of the following ways: as a type-level annotation on any class directly or indirectly annotated with #Component, including #Configuration classes. As a meta-annotation, for the purpose of composing custom stereotype annotations. As a method-level annotation on any #Bean method
PROFILE DOCS

Getting Javassist types instead of actual Hibernate entity types

I have stumbled upon a really annoying situation: I am using Hibernate & Spring as backend for my app and it seems that in some cases, the entities that are in a relationship with a particular entity are not fetched as normal entity objects from the DB, but as Javassist types. E.g.:
I have the Campaign entity with the following relationships:
#Entity
#Table(name = "campaign")
public class Campaign implements Serializable {
[..]
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(uniqueConstraints = #UniqueConstraint(columnNames = {
"campaign_id", "dealer_id" }), name = "campaign_has_dealer", joinColumns = { #JoinColumn(name = "campaign_id", nullable = false) }, inverseJoinColumns = { #JoinColumn(name = "dealer_id", nullable = false) })
private List<Dealer> dealers = new ArrayList<Dealer>();
#ManyToMany
// (fetch = FetchType.LAZY)
#JoinTable(uniqueConstraints = #UniqueConstraint(columnNames = {
"campaign_id", "sales_area_id" }), name = "campaign_has_sales_area", joinColumns = { #JoinColumn(name = "campaign_id", nullable = false) }, inverseJoinColumns = { #JoinColumn(name = "sales_area_id", nullable = false) })
private List<SalesArea> salesAreas = new ArrayList<SalesArea>();
}
Upon retrieving the salesAreas connected to this Campaign, I get a list of SalesArea_$$_javassist_56, while for the dealers, I get normal Hibernate entities. Since the client part is based on GWT, we use RequestFactory for retrieving stuff. I initially thought it was a problem with the proxies, locators and so on but I have set a breakpoint in the service where these are retrieved and they are Javassist objects directly after selecting them. It seems that even removing the FetchType.LAZY annotation (although definitely not a desirable solution), the same thing happens. This happened also with other types of relationships, not only #ManyToMany.
We are using GWT 2.3, Spring 3, Hibernate 3.6.3 and JPA 2.0 for annotations.
Any suggestions would be appreciated.
Thanks in advance
As far as I can see the big problem that you're having is not so much the fetch type of your association, but rather that the proxied types don't work well with RequestFactory.
Yes, it could be solved by changing the fetch strategy but that sounds rather like a weak workaround that may break upon weird circumstances.
I don't remember exactly how to solve it, but I did, and as far as I remember there was an extension point in the ServiceLayerDecorator class. Basically there you check if the object you're returning is a Hibernate proxy (check Hibernate and HibernateProxy classes) and then return the non-proxy type instead in ServiceLayerDecorator. (http://code.google.com/p/google-web-toolkit/issues/detail?id=6767)
As for your fetch strategy, I'd largely recommend #BatchSize(N) where N is big (maybe 1000), but this is an independent subject.
Good luck!
If you call to the static method:
HibernateProxyHelper.getClassWithoutInitializingProxy(entity);
you get the class of the proxied entity and the class itself if it wasn't proxied.
With Hibernate's proxy model and now with it's use of Javassist to help avoid the slower traditional Hibernate run time reflection operations things will never quite be as elegant as the clean, intuitive experience people who use full bytecode enhancement solutions like JDO implementations (eg DataNucleus) enjoy.
Personally I can never see the sense in persisting (pardon the pun) with solutions that cause so many problems and fill the web with questions about broken code that requires strange, unintuitive workarounds but still people do...
However, back to the question: one solution to your problem, if you're using JPA, is to use DataNucleus/JPA which brings many of the benefits of DataNucleus/JDO (clean underlying implementation - no proxies, no Javassist classes etc.,) in a JPA compliant implementation - i.e. you don't need to change your existing source code to start using it.

When should I use #Basic(optional = false) in JPA 2.0

I have a Inheritance with Single Table mapping in JPA, Say Class A and B extends some abstract entity, so I have to make columns from A & B nullable at DB end but if someone is trying to persist A then all fields of A should be not null and i want to enforce this by code.
Can I use following code to achieve this -
#Entity
#DiscriminatorValue("1")
public Class A extends SomeAbstractEntity{
#Basic(optional = false)
private String nameOfA;
}
I read this answer #Basic(optional = false) vs #Column(nullable = false) in JPA and thought this may be achievable but wanted to know what is the best way.
It's quite funny, but it looks like in this case (with single table inheritance) #Basic(optional = false) is not enforced by Hibernate (though in other cases it works as expected).
If so, the only option to enforce this rule is to use #NotNull constraint from JSR-303 Bean Validation. JSR-303 smoothly integrates with JPA 2.0, so that constraints are checked automatically when entities are persisted, see Hibernate Validator.

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

Categories

Resources