Is there a configurable alternative to using #Version? - java

I'm placing an annotated field with #Version on it in all my JPA domain classes, however this just seems like additional boiler plate. Is there a way to get around this perhaps via configuration?
TIA,
Ole

As far as the JPA specification tells us you can't change the #Version annotation via "configuration". You either use #Version in your program code or you don't.
Referring to the official JPA specification (final version, JPA 2.1) in Section 3.4.2 (page 90) we find:
An entity is automatically enabled for optimistic locking if it has a property or field mapped with a Version mapping.
[...]
If only some entities contain version attributes, the persistence provider runtime is required to check those entities for which version attributes have been specified. The consistency of the object graph is not guaranteed, but the absence of version attributes on some of the entities will not stop operations from completing.
However, you can use the concept of inheritance to provide the #Versiononly in one spot via an abstract base class. This class you be written as follows:
#MappedSuperclass
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractBaseEntity {
public static final long INVALID_OBJECT_ID = -42;
#Version
private int version;
#Id
#SequenceGenerator(name = "sequence-object", sequenceName = "ID_MASTER_SEQ")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence-object")
#Column(name = "id")
protected Long objectID = INVALID_OBJECT_ID;
public final int getVersion() {
return version;
}
#Override
public long getObjectID() {
return objectID;
}
// ... maybe other methods or fields ...
}
Thus, all your #Entity annotated sub-classes that inherit from AbstractPersistentEntity are provided with both properties: (i) objectIDand (ii) version at once. For instance, class SomeClass can be written as:
#Entity
public class SomeClass extends AbstractBaseEntity /*implements SomeInterface*/ {
// ... specific methods or fields ...
}
For details on the use of #MappedSuperclass see also this answer.
Hope it helps.

Related

JPA Inheritance Two Or More Superclass

I made a research about Inheritance in JPA and resources that I found uses just one superclass for each entity. But there is not an example that uses 2 or more superclass.
What about this:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = “Abstract_One”)
public abstract class AbstractOne {
#Id
protected Long id;
…
}
#Entity(name = “A”)
#DiscriminatorValue(“A”)
public class A extends AbstractOne {
#Column
private int a;
…
}
#Entity(name = “B”)
#DiscriminatorValue(“B”)
public class B extends A {
#Column
private int b;
…
}
Is it possible to do that?
If it is possible, which Inheritance Strategy allows that and gives the best data consistency?
I can imagine only the following example
#MappedSuperclass
public class A
{
...
#Id
#Column(name = "RECID")
public Long getId()
...
}
#MappedSuperclass
public class B extends A
{
...
#Column(name = "COL1")
public String getColumn1()
...
}
#Entity(name="INH_TAB1")
public class C extends B
{
...
#Column(name = "COL2")
public String getColumn2()
...
}
Also at the excellent book "Java Persistence with Hibernate" by Bauer, King, Gregory I found the following plase what can be useful in the context of this question:
6.5 Mixing inheritance strategies
You can map an entire inheritance hierarchy with the TABLE_PER_CLASS,
SINGLE_TABLE, or JOINED strategy. You can’t mix them — for example, to switch from a
table-per-class hierarchy with a discriminator to a normalized table-per-subclass
strategy. Once you’ve made a decision for an inheritance strategy, you have to stick with it. This isn’t completely true, however. By using some tricks, you can switch
the mapping strategy for a particular subclass. For example, you can map a class
hierarchy to a single table, but, for a particular subclass, switch to a separate
table with a foreign key–mapping strategy, just as with table-per-subclass.
However, I can not imagine any real case when such complex inheritance hierarchy will be required/useful and also it can affect performance.

How to inherit from multiple base abstract classes in JPA

I faced a problem how I can create JPA entity which extends multiple base abstract classes (without creating additional table). I know there is an annotation #MappedSuperclass, but it gives an ability to create only one base class as soon as we use extends and multiple inheritance is not a Java feature.
For example, I have two bases:
#MappedSuperclass
public abstract class Authored {
#ManyToOne
private User user;
}
and
#MappedSuperclass
public abstract class Dated {
private String creationDate;
}
I expect that some of my models will extend only Authored, some -- only Dated, and some -- both.
Though it's only possible to write
#Entity
public class MyEntity extends Authored {
...
}
or
#Entity
public class MyEntity extends Dated {
...
}
Some discussions propose to inherit classes in line (e.g. Authored and AuthoredAndDated) but this seems too dirty, none of this bases logically can't extend another one.
ADDITION
I have to note that this style is supported in other frameworks like Django (due to multiple inheritance in python) so it's strange that JPA doesn't support it.
I am sorry to disappoint you, but there is no other solution than creating AuthoredAndDated as you suggested.
We faced in the same issue for our entities and went with the same procedure.
We have a
#MappedSuperclass
public class TimestampedObject {
#CreationTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_at")
private Date createdAt;
#UpdateTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "updated_at")
private Date updatedAt;
}
and a
#MappedSuperclass
public class IdObject {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id", updatable = false, columnDefinition = "serial")
private Long id;
}
Thus we created a TimestampedIdObject for this purpose.
Edit:
If you find another suitable solution, it would be great if you could post it here, as we have the same issue...
You should use an #Embeddable / #Embedded for goal by replacing inheritance with composition.
First, do not use #MappedSuperClass, but use #Embeddable instead for your classes you want to share the attributes with:
#Embeddable
public class Authored {...}
#Embeddable
public class Dated {...}
In the next step your Entity should not inherit from Authored or Dated but instead get an attribute referencing them:
#Entity
public class MyEntity {
#Embedded
private Authored authored;
#Embedded
private Dated dated;
}
If you want to get behaviour out of this, where you can generically access without those new attributes, you would need to introduce an interface exposing the necessary methods.
For expample if MyEntity should be able to provide details on last updates and creation, you would introduce an interface Authorable which defines to methods to access the relevant data.
public interface Authorable { /* necessary methods */ }
MyEntity will implement this interface then:
#Entity
public class MyEntity implements Authorable {
/* previous content plus implemented mehtods from interface */
}

Java persistence mapped superclass with optional properties

I'm using the javax.persistence package to map my Java classes.
I have entities like these:
public class UserEntity extends IdEntity {
}
which extends a mapped superclass named IdEntity:
#MappedSuperclass
public class IdEntity extends VersionEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// Getters and setters below...
}
The IdEntity super class extends another mapped super class named VersionEntity to make all entities inherit version properties:
#MappedSuperclass
public abstract class VersionEntity {
#Version
private Integer version;
// Getters and setters below...
}
Why?
Because now I can make generic queries on the IdEntity class for all entities, and it will look like this: (example)
CriteriaBuilder builder = JPA.em().getCriteriaBuilder();
CriteriaQuery<IdEntity> criteria = builder.createQuery(IdEntity.class);
Now to the problem.
Some of my entities will have timestamps like created_at and deleted_at. But not all entities.
I could provide these properties in my entity classes like this:
public class UserEntity extends IdEntity {
#Basic(optional = false)
#Column(name = "updated_at")
#Temporal(TemporalType.TIMESTAMP)
private Date updatedAt;
}
But as I have a lot of entities, this will make me put a lot of redundant code in all entities that should have timestamps. I wish there was some way I could make the relevant classes inherit these fields in some way.
One possible solution is to create a parallell IdEntity superclass, maybe named IdAndTimeStampEntity and make those entities that should have timestamps inherit from this new superclass instead, but hey that's not fair to my colleague-developers because now they have to know which super class to choose from when writing generic queries:
CriteriaBuilder builder = JPA.em().getCriteriaBuilder();
CriteriaQuery<???> criteria = builder.createQuery(???); // Hmm which entity should I choose IdEntity or IdAndTimeStampEntity ?? *Annoyed*
And the generic entity queries become not so generic..
My question: How can I make all of my entities inherit id and
version fields, but only a sub part of all entities inherit
timestamp fields, but keep my queries to a single type of entities?
Update #1
Question from Bolzano: "can you add the code which you specify the path(holds table info) for entities ?"
Here is a working example of querying a UserEntity which is a IdEntity
CriteriaBuilder builder = JPA.em().getCriteriaBuilder();
CriteriaQuery<IdEntity> criteria = builder.createQuery(IdEntity.class);
Root<IdEntity> from = criteria.from(IdEntity.class);
criteria.select(from);
Path<Integer> idPath = from.get(UserEntity_.id); //generated meta model
criteria.where(builder.in(idPath).value(id));
TypedQuery<IdEntity> query = JPA.em().createQuery(criteria);
return query.getSingleResult();
I would pick a solution that didn't enforce a class-based object model like you've outlined. What happens when you don't need optimistic concurrency checking and no timestamps, or timestamps but no OCC, or the next semi-common piece of functionality you want to add? The permutations will become unmanageable.
I would add these common interactions as interfaces, and I would enhance your reusable find by id with generics to return the actual class you care about to the caller instead of the base superclass.
Note: I wrote this code in Stack Overflow. It may need some tweaking to compile.
#MappedSuperclass
public abstract class Persistable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// getter/setter
}
public interface Versioned {
Integer getVersion();
}
public interface Timestamped {
Date getCreated();
Date getLastUpdated();
}
#Embeddable
public class TimestampedEntity {
#Column(name = "create_date")
#Temporal
private Date created;
#Column
#Temporal
private Date lastUpdated;
// getters/setters
}
#Entity
public class UserEntity extends Persistable implements Versioned, Timestamped {
#Version
private Integer version;
#Embedded
private TimestampedEntity timestamps;
/*
* interface-defined getters. getTimestamps() doesn't need to
* be exposed separately.
*/
}
public class <CriteriaHelperUtil> {
public <T extends Persistable> T getEntity(Class<T> clazz, Integer id, SingularAttribute idField) {
CriteriaBuilder builder = JPA.em().getCriteriaBuilder();
CriteriaQuery<T> criteria = builder.createQuery(clazz);
Root<T> from = criteria.from(clazz);
criteria.select(from);
Path<Integer> idPath = from.get(idField);
criteria.where(builder.in(idPath).value(id));
TypedQuery<T> query = JPA.em().createQuery(criteria);
return query.getSingleResult();
}
}
Basic Usage:
private UserEntity ue = CriteriaHelperUtil.getEntity(UserEntity.class, 1, UserEntity_.id);
ue.getId();
ue.getVersion();
ue.getCreated();
// FooEntity implements Persistable, Timestamped
private FooEntity fe = CriteriaHelperUtil.getEntity(FooEntity.class, 10, FooEntity_.id);
fe.getId();
fe.getCreated();
fe.getVersion(); // Compile Error!
#MappedSuperclass
public class IdEntity{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Version
private Integer version;
}
#MappedSuperclass
public class IdAndTimeStampEntity extends IdEntity{
Date created;
}
#Entity
public class UserEntity extends IdAndTimeStampEntity{
String name;
}
#Entity
public class FooEntity extends IdEntity{...
Pros of this solution:
In simple and clear way uses OOP without need to embed duplicate code implementing intefaces in every subclass. (Every class is also interface)
Optimistic locking version column is mostly used approach. And should be part of base class. Except read only entities like codetables.
Usage:
public <T extends IdEntity> T persist(T entity) {
if (entity instanceof IdAndTimeStampEntity) {
((IdAndTimeStampEntity) entity).setCreated(new Date());
}
if (!em.contains(entity) && entity.getId() != null) {
return em.merge(entity);
} else {
em.persist(entity);
return entity;
}
}
I wish there was some way I could make the relevant classes inherit these fields in some way.
You could make a custom annotation #Timed and use an annotation processor to add the timestamp field and annotations, either by using a bytecode manipulation framework or creating a delegating subclass. Or, for example if you use Lombok, create a Lombok annotation.
That way, your team members only have to remember to use the #Timed annotation when you have entities with timestamps. Whether you like such approach or not is up to you.

How to create meta annotations on field level?

I have this hibernate class with annotations:
#Entity
public class SimponsFamily{
#Id
#TableGenerator(name = ENTITY_ID_GENERATOR,
table = ENTITY_ID_GENERATOR_TABLE,
pkColumnName = ENTITY_ID_GENERATOR_TABLE_PK_COLUMN_NAME,
valueColumnName = ENTITY_ID_GENERATOR_TABLE_VALUE_COLUMN_NAME)
#GeneratedValue(strategy = GenerationType.TABLE, generator = ENTITY_ID_GENERATOR)
private long id;
...
}
Since I don´t won´t to annotate every id field of my classes that way, I tried to create a custom anotation:
#TableGenerator(name = ENTITY_ID_GENERATOR,
table = ENTITY_ID_GENERATOR_TABLE,
pkColumnName = ENTITY_ID_GENERATOR_TABLE_PK_COLUMN_NAME,
valueColumnName = ENTITY_ID_GENERATOR_TABLE_VALUE_COLUMN_NAME)
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface EntityId {
#GeneratedValue(strategy = GenerationType.TABLE, generator = ENTITY_ID_GENERATOR)
public int generator() default 0;
#Id
public long id() default 0;
}
so that I can use this annotation in my class:
#Entity
public class SimponsFamily{
#EntityId
private long id;
...
}
I do have to write the #Id and the #GeneratedValue annotions on field level since they do not support the TYPE RetentionPolicy. This solutions seems to work.
My questions:
How are the field level annotations in my custom annotations(and values) transferred to my usage of EntityId annotation?
What about the default values which I set in my custom annotation, are they used since I do not specify attributes at the usage?
It is a preferred way to use annotations on field level in annotations?
I think I can aswer your third question.
One common way to do what you want (avoid duplicating ID mapping) is to create a common superclass that holds the annotated id and version (for optimistic locking) fields, and then have all persistent objects extend this superclass.
To ensure the superclass is not considered an Entity on its own, it must be annotated with #MappedSuperclass.
Here is a sample (sorry for typos, I don't have an IDE at hand right now) :
#MappedSuperclass
public class PersistentObject {
#Id // Put all your ID mapping here
private Long id;
#Version
private Long version;
}
#Entity
public class SimpsonsFamily extends PersistentObject {
// Other SimpsonFamily-specific fields here, with their mappings
}

Applying annotations to fields inherited from #MappedSuperclass

Has:
#MappedSuperclass
class Superclass {
#Id
#Column(name = "id")
protected long id;
#Column(name="field")
private long field;
}
and
#Entity
class Subclass extends Superclass {
}
How to annotate inherited id with #GeneratedValue and field with #Index within Subclass?
How to annotate inherited id with #GeneratedValue and field with #Index within Subclass?
AFAIK, you can't. What you can do is overriding attributes and associations (i.e. change the column or join column) using the AttributeOverride and AssociationOverride annotations. But you can't do exactly what you're asking.
For the GeneratedValue, consider using XML mapping to override the strategy if you don't want to declare it in the mapped superclass.
For the Index (which is not a standard annotation by the way), did you actually try to declare it at the table level using Hibernate's Table annotation instead (I'm assuming you're using Hibernate)?
#Table(appliesTo="tableName", indexes = { #Index(name="index1", columnNames=
{"column1", "column2"} ) } )
creates the defined indexes on the
columns of table tableName.
References
JPA 1.0 Specification
Section 2.1.9.2 "Mapped Superclasses"
Section 9.1.10 "AttributeOverride Annotation"
Section 9.1.11 "AttributeOverrides Annotation"
Section 9.1.12 "AssociationOverride Annotation"
Section 9.1.13 "AssociationOverrides Annotation"
Hibernate Annotations Reference Guide
2.4. Hibernate Annotation Extensions
Chapter 3. Overriding metadata through XML
As for #GeneratedValue, it is possible to do like this:
#MappedSuperclass
class Superclass {
#Id
#Column(name = "id")
#GeneratedValue(generator = "id_generator")
protected long id;
#Column(name = "field")
private long field;
}
#Entity
#SequenceGenerator(name = "id_generator", sequenceName = "id_seq")
class Subclass extends Superclass {
}
You might be able to do this if you apply the annotations to the accessor methods instead. (I haven't tried this, so I can't guarantee that it'll work.)
#MappedSuperclass
public class Superclass {
#Id
#Column(name = "id")
public long getId() {
return id;
}
.
#Entity
public class Subclass extends Superclass {
#GeneratedValue
public long getId() {
return super.getId();
}
Just in case anyone else searches for this, I used the following code which adds in some overhead, but for processing Field annotations only shouldn't add that much:
private List<Field> getAllFields() {
List<Field> fieldList = new ArrayList<Field>();
// Add all fields from the current class
fieldList.addAll(Arrays.asList(mElement.getClass().getDeclaredFields()));
// Use an index to iterate over mElement's parent types
Class clazz = mElement.getClass();
// Get any fields from the parent class(es)
while (clazz.getSuperclass() != null) {
fieldList.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
// Set it to that parent class
clazz = clazz.getSuperclass();
}
return fieldList;
}
The returned list would contain all fields for all parent and child classes with mElement being the object you are searching for annotations from. Hope this helps.

Categories