JPA using `Enum` as an `#Id` with `#IdClass` - jpa

I found a forum with this subject. Using enum as id
But I couldn't sure that I can do it or not. I think my case is a little bit different.
enum Type {
}
class MyEntityId {
String type;
String key;
}
#Entity
#IdClass(MyEntityId.class)
class MyEntity {
#Enumerated(EnumType.String)
#Id
Type type;
#Id
String key;
}
Is this actually legal by the spec?
If it is, can I have no worries for vendor specific behaviors?

Why not read the linked issue ? It quotes the spec (2.1.4) and says NO you cannot have id fields of Enum type; it doesn't matter that you have some (invalid) IdClass (since the types of an IdClass have to match the types of the class). So it is not part of the JPA spec, so you are left with vendor-specific behaviour.

In Hibernate 4 you can use an Enum as an #Id in an IdClass, but only as an Ordinal. Any #Convert tags you add the the enumeration will be ignored.
What you can do is use an #EmbeddedId to get the same effect
#Embeddable
class MyEntityId {
#Enumerated(EnumType.String)
Type type;
String key;
}
#Entity
class MyEntity {
#EmbeddedId
MyEntityId id;
}

Related

Lombok: publicly immutable (over setters) object with all and no arguments constructor, hash, equals and toString

We use Lombok for our entities to generate that common boilerplate, like constructors, hash/equals and toString.
In the same time we'd like to keep our objects immutable. Unfortunately, we can't make the completely immutable (e.g. with final properties) because JPA/Hibernate processors required no-args constructor and sets properties over reflection.
#lombok.Data doesn't fit because it creates public setters
#lombok.Value doesn't fit because it makes properties final and Hibernate can't set them over reflection.
what really fits us is:
#Getter
#AllArgsConstructor
#NoArgsConstructor
#EqualsAndHashCode
#ToString
#Entity
public class Company {
#Id
private int id;
private String name;
}
But this again creates a boilerplate for us, copy-pasting 5 annotations every time and messing the code.
Unfortunately i have not found any way in Lombok to aggregate annotations to some meta-annotation, like in Spring.
Question: is there any out-of-box annotation in Lombok to generate such publicly immutable entities?
Or
is there any way to declare local meta-annotation?
You should be able to use a slightly lighter version:
#Data
#Setter(AccessLevel.NONE)
#Entity
public class Company {
#Id
private int id;
private String name;
}
with the following in lombok.config file:
lombok.noArgsConstructor.extraPrivate = true
I'm not sure whether the extraPrivate configuration works in Lombok 1.18.0. It should, according to the changelog, but I was unable to make it work in a quick attempt.
You may still use #Data annotations, but provide private setters
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
public class Company {
#Id
#Setter(AccessLevel.PRIVATE)
private int id;
#Setter(AccessLevel.PRIVATE)
private String name;
}

How can I share an Entity for other entities' OneToMany

I have an Entity look like this.
#Entity
class Property extends BaseEntity {
#Basic
private String name;
#Basic
private String value;
}
The basic intention is using this Entity as other Entities properties.
#Entity
class MyEntity extends BaseEntity {
#OneToMany
private List<Property> properties;
}
#Entity
class YourEntity extends BaseEntity {
#OneToMany
private List<Property> properties;
}
How can I do this? Do I have to define each owner's field in Property?
#Entity
class Property extends BaseEntity {
#Basic
private String name;
#Basic
private String value;
#ManyToOne(optional = true)
private MyEntity myEntity;
#ManyToOne(optional = true)
private YourEntity yourEntity;
#ManyToOne(optional = true)
private OtherEntity otherEntity;
}
Basically it is a good solution You represented here. There is the option to create a join table which will help you to keep the entity "cleaner" (and also could be used as a ManyToMany. In most of the cases I prefer to use the option You provided [simplicity is a gooooood thing :) ], but other colleagues got different view on this problem.
TL.DR: Your provided code is working and I personally prefer it. There are other ways but those are a bit slower etc.

Cannot use an #IdClass attribute for a #ManyToOne relationship

I have a Gfh_i18n entity, with a composite key (#IdClass):
#Entity #IdClass(es.caib.gesma.petcom.data.entity.id.Gfh_i18n_id.class)
public class Gfh_i18n implements Serializable {
#Id #Column(length=10, nullable = false)
private String localeId = null;
#Id <-- This is the attribute causing issues
private Gfh gfh = null;
....
}
And the id class
public class Gfh_i18n_id implements Serializable {
private String localeId = null;
private Gfh gfh = null;
...
}
As this is written, this works. The issue is that I also have a Gfh class which will have a #OneToMany relationship to Gfh_i18n:
#OneToMany(mappedBy="gfh")
#MapKey(name="localeId")
private Map<String, Gfh_i18n> descriptions = null;
Using Eclipse Dali, this gives me the following error:
In attribute 'descriptions', the "mapped by" attribute 'gfh' has an invalid mapping type for this relationship.
If I just try to do, in Gfh_1i8n
#Id #ManyToOne
private Gfh gfh = null;
it solves the previous error but gives one in Gfh_i18n, stating that
The attribute matching the ID class attribute gfh does not have the correct type es.caib.gesma.petcom.data.entity.Gfh
This question is similar to mine, but I do not fully understand why I should be using #EmbeddedId (or if there is some way to use #IdClass with #ManyToOne).
I am using JPA 2.0 over Hibernate (JBoss 6.1)
Any ideas? Thanks in advance.
You are dealing with a "derived identity" (described in the JPA 2.0 spec, section 2.4.1).
You need to change your ID class so the field corresponding to the "parent" entity field in the "child" entity (in your case gfh) has a type that corresponds to either the "parent" entity's single #Id field (e.g. String) or, if the "parent" entity uses an IdClass, the IdClass (e.g. Gfh_id).
In Gfh_1i8n, you should declare gfh like this:
#Id #ManyToOne
private Gfh gfh = null;
Assuming GFH has a single #Id field of type String, your ID class should look like this:
public class Gfh_i18n_id implements Serializable {
private String localeId = null;
private String gfh = null;
...
}

Is it legal to have a top-level JPA class that is abstract and does not have a discriminator?

For example, is the following top-level JPA class valid:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
name = "type",
discriminatorType = DiscriminatorType.STRING
)
public abstract class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
public Long getId() {
return id;
}
}
Yes, it is perfectly legal. According specification (JPA 2.0) DiscriminatorValue belongs only to concrete entity class.
I can no no reason why it shouldn't be. Try it out.
The discriminator value of the specified discriminator column will be the entity name of the concrete implementation(s) if the discriminatorvalue annotation has not been specified on the entities (as described in the spec).
Does this answer your question?

Why JPA-2.0 Primary Key Classes have to implement Serializable but my example works without?

In many sources I have read PrimaryKey Classes and even JPA2 entities should be serializable.
IN my example (legacy database) there is a relationship between employee and languages:
Employee Class:
#Entity
#IdClass(EmpleadoId.class)
#Table(name = "NO_INFGRAEMPL")
public class Empleado {
#Id
#Column(name = "IGECOMPANIA", unique = true)
private String compania;
#Id
#Column(name = "IGENUMEROIDENTIFIC", unique = true)
private String numeroIdentificacion;
//...
}
Employee Compound PrimaryKey Class:
public class EmpleadoId {
private String compania;
private String numeroIdentificacion;
//...
}
Employee Language SKill Class:
#Entity
#IdClass(IdiomaEmpleadoId.class)
#Table(name = "NO_IDIOMEMPLE")
public class IdiomaEmpleado {
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns(value = {
#JoinColumn(name= "IEMCOMPANIA", referencedColumnName = "IGECOMPANIA"),
#JoinColumn(name = "IEMEMPLEADO", referencedColumnName = "IGENUMEROIDENTIFIC")
})
private Empleado empleado;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "IEMIDIOMA")
private Idioma idioma;
#Column(name = "IEMNIVELLECTURA")
private String nivelLectura;
//...
}
Employee Language Skill Compound PrimaryKey Class:
public class IdiomaEmpleadoId {
private EmpleadoId empleado;
private String idioma;
//...
}
Language Class:
#Entity
#Table(name = "NO_IDIOMAS")
public class Idioma {
#Id
#Column(name = "IDICODIGO")
private String codigo;
#Column(name = "IDIDESCRIPCION")
private String descripcion;
//...
}
I am using EclipseLink JPA2 Provider under a J2SE application and it is not giving me any exceptions.
My questions are:
Why is it not giving me exceptions? Is it not enforced to have Serializable?
Is it safe to continue this way or should I definitely implemente serializable?.
In which ones?, JPA2 Entities or PrimaryKey Classes?
Thanks a lot for the help.
JPA specification contains such a requirement (JSR-317 secion 2.4 Primary Keys and Entity Identity):
The primary key class must be serializable.
If EclipseLink really doesn't enforce this requirement, it's an implementation detail of EclipseLink and I wouldn't recommend you to rely on it.
However, there are no requirements on serializability of entities, except for the following one which looks more like a recommendation than a requirement:
If an entity instance is to be passed by value as a detached object (e.g., through a remote interface), the
entity class must implement the Serializable interface.
Nothing is required to be serializable, but it seems it is requried by the spec (10x to axtavt) for primary keys, although there is no direct need for it.
Serialization is needed if the objects are transferred over-the-wire or persisted to disk, so I can't see the reason behind that decision. However, you should conform to it.
Primary key classes have to implement serializable and composite-ID class must implement serializable are two different questions.
I am going to answer you both, and hope it will help you to distinguish and understand holistically.
Primary key classes have to implement serializable:
Note: It could work without its iplementation also.
JPA specification contains such a requirement (JSR-317 secion 2.4 Primary Keys and Entity Identity):
The primary key class must be serializable.
However, there are no requirements on serializability of entities, so it's a recommendation than a requirement
exception:
If an entity instance is to be passed by value as a detached object (e.g., through a remote interface), the entity class must implement the Serializable interface.
Composite-ID class must implement serializable.
The id is used as a key to index loaded objects in the session.
The session object needs to be serializable, hence all objects referenced by it must be serializable as well.
In case of CompositeIds the class itself is used as the id.