Using enum as id - java

Using JPA, can we define an enum as id of an entity?
I've tried the following:
public enum AssetType {
....
}
#Entity
#IdClass(AssetType.class)
public class Adkeys {
private AssetType type;
#Id
#Enumerated(EnumType.STRING)
#Column(nullable = false)
public AssetType getType() {
return type;
}
}
Using OpenJPA, it complains:
org.apache.openjpa.persistence.ArgumentException: The id class "class aa.AssetType" specified by type "class aa.Adkeys" does not have a public no-args constructor.
So my questions are:
should we able to use enum as id for an entity on JPA? (i.e. there is a bug in OpenJPA)
or do I make a mistake somewhere?
and is there any workaround for such problem?

The JPA spec doesn't say this is possible:
2.1.4 Primary Keys and Entity Identity
The primary key (or field or property of a composite primary key) should be one of the following types: any Java primitive type; any primitive wrapper type; java.lang.String; java.util.Date; java.sql.Date. In general, however, approximate numeric types (e.g., floating point types) should never be used in primary keys. Entities whose primary keys use types other than these will not be portable.
If you really want to have a compile-time fixed number of records for a given entity, you can use a String or int primary key and assign it AssetType.FOO.name() or AssetType.FOO.ordinal()
And non-portable here means that some persistence provider may support other things, but it might not work for another provider. As with the enum - if the persistence provider has special support for it, that does not try to instantiate it, but rather processes it specially after checking if class.isEnum(), then it might work. But it seems your persistence provider doesn't do this.

No, you can't use enums as ID because JPA doesn't allow to define your own mapping for ID columns (they must be int or long or something that JPA can create with new).
IDs must not be the business key (in your case: the type). Using the business key as an ID is a common mistake in DB designs and should be avoided because it will cause all kinds of problems later.
Add an independent ID column to solve the problem.

OpenJPA is the only JPA provider that does not support this.
See Support Enum as Primary Key Type

Do you really want to do this? This construct doesn't allow changing the database enum keys without updating the enum in the code (fail on load), nor the other way around (constraint failure). Why don't you just create an AssetType table with int pk and name, and make the Adkeys have a foreign key to AssetType.id as pk?
You can load the AssetTypes from the db on startup if you need to enumerate them in your app.

Related

JPA/Hibernate "Composite-id class does not override equals()"

I'm using JPA and getting the following warning. I've researched this, and understand why I need to override it and how Hibernate uses these methods. I still have a question though:
Exception:
Composite-id class does not override equals()
Question:
Why does Hibernate only care about classes that don't have composite IDs? Does it by default compare on the #Id field if there is only one present, or is there something more complex going on here?
Because when entities don't have a composite ID, they have a single one, of one of the basic supported types (Integer, Long, String, etc.), and those classes already have a well-defined equals() (and hashCode()) method.
Using JPA when you use composite key, you should use either IdClass or EmbeddedId using any of them you need to create an own class that act as a composite key, in order to be able to compare objects using this composite key which is required by several operations within the EntityManager that key classes must to override equals and hashCode.
Taking from specs:
A composite primary key must correspond to either a single persistent field or property or to a set of such fields or properties as described below. A primary key class must be defined to represent a composite primary key. Composite primary keys typically arise when mapping from legacy databases when the database key is comprised of several columns. The EmbeddedId or IdClass annotation is used to denote a composite primary key.
And when using composite primary keys must follow.
The primary key class must be public and must have a public no-arg constructor.
The primary key class must be serializable.
The primary key class must define equals and hashCode methods. The semantics of value equality for these methods must be consistent with the database equality for the database types to which the key is mapped.

Hibernate : Difference between # Embedded annotation technique and #OneToOne annotation Technique

What is the difference between #Embedded annotation technique and #OneToOne annotation technique because in Embedded the java class contain "Has a" relationship in class and with the help of #Embedded annotation we persist the has a object in database. and in OneToOne relationship we also persist the has a object in database.
#OneToOne is for mapping two DB tables that are related with a one to one relationship. For example a Customer might always have one record in a Name table.
Alternatively if those name fields are on the Customer table (not in a separate table) then you might want an #embedded. On the face of it you could just add the name fields as standard attributes to the Customer entity but it can be useful if those same columns appear on multiple tables (for example you might have the name columns on a Supplier table).
Its the difference between composition and aggregation. #Embedded objects are always managed within the lifecycle of their parents. If the parent is updated or deleted, they are updated or deleted as well. #OneToOne objects may mimic composition via the cascadeType option of their #Join annotation, but by default they are aggregated, aka their lifecycle is separate from that of their parent objects.
#Embedded is used with Value Objects (Objects which have a meaning only when attached to an Object) whereas one to one mapping is between two objects having their own existence and meaning.
For e.g.
Value Object and #Embedded: If we have a User class and this class has an address Object in it, it can be considered as a value object as the address alone does not have any significance until unless associated with a user. Here address object can be annotated with #Embedded.
One to One mapping and #OneToOne: If we have a User class and this class has a 'Father' Object or a 'Mother' object, we would want to annotate the 'Father' or 'Mother' instance as #OneToOne as 'Father' or 'Mother' have their own meaning and existence and are not Value objects to User class.
A closely related difference is between #OneToMany and #ElementCollection. Both are used to save instance variables of Collection type in Java class. The difference being, #ElementCollection is to be used when the elements of Collection being saved are Value Objects whereas #OneToMany is used when the elments and object have well defined meaning and existence.
Use #OneToOne, only if fields can be reused. Otherwise, go for #Embeddable.
A quote from Beginning Hibernte, 3rd Edition:
There is nothing intrinsically wrong with mapping a one-to-one association between two entities where one is not
a component of (i.e., embedded into) the other. The relationship is often somewhat suspect, however. You should
give some thought to using the embedded technique described previously before using the #OneToOne annotation.
#Embeddable:
If the fields in an entity (X) are contained within the same table as another entity (Y), then entity X is called "component" in hibernate terms or "embedded" in JPA terms. In any case, JPA or hibernate do not allow to use 2nd table to store such embedded entities.
Generally, we think of normalizing a table when data is being reused by more than one table. Example: A Customer (id, name, street, city, pin, landmark) can be normalized into Customer(id, name) and CustomerAddress(cust_id, street, city, pin, landmark). In this case, we can reuse CustomerAddress by linking the same using cust_id with other tables. But if this reuse is not required in your application, then we can just keep all columns in one table.
So, a thumb rule is,
If reuse -> #OneToOne,
If no reuse -> #Embeddable
#Embedded is typically to represent a composite primary key as an embeddable class:
#Entity
public class Project {
#EmbeddedId ProjectId id;
:
}
#Embeddable
Class ProjectId {
int departmentId;
long projectId;
}
The primary key fields are defined in an embeddable class. The entity contains a single primary key field that is annotated with #EmbeddedId and contains an instance of that embeddable class. When using this form a separate ID class is not defined because the embeddable class itself can represent complete primary key values.
#OneToOne is for mapping two DB tables that are related with a one to one relationship. #Id will be the primary key.

Using Hibernate annotations to persist custom value for Enumerated attribute

I have lots of Java enums that I persist with Hibernate. As far as I know there are two different standard ways to persist these enums:
#Enumerated(EnumType.ORDINAL)
This is the default, and it just persists the ordinal value from the enum.
#Enumerated(EnumType.STRING)
This persists the name of the enum value.
Those have worked fine for me so far, but now I have a new enum where I want to persist a custom value from the enum that is neither the ordinal nor the name. Is this possible? I searched around and saw lots of people asking how to persist the name, which is easily accomplished using EnumType.STRING, but I want to persist an int that can be used for comparison in my SQL queries. I tried overriding toString() to return my custom value, but that did not work.
I'll paste my java enum below. I want to persist the int value member from the enum.
Thanks in advance!
public enum Permission {
VIEW (4),
CHANGE(6),
FULL(7);
private int value;
Permission(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
You can implement a UserType with desired behaviour and configure Hibernate to use it with #Type annotation.
See, for example, UserType for persisting a Typesafe Enumeration with a VARCHAR column
.
Yes, I'm missing a JPA solution, too.
Workaround:
private int permissionValue;
private transient Permission permission;
Then convert in the getter/setter of permission.
Maybe you also need to implement some lifecycle methods? (I'm not sure.)
http://download.oracle.com/docs/cd/B32110_01/web.1013/b28221/undejbs003.htm#BABIAAGE

Read Hibernate entity identity value

Does Hibernate have an API for reading the value of an entity's identity field? In my case, this would be the Serializable value returned by the field annotated with #Id. For example, suppose I had a Person entity:
class Person {
#Id private long id;
// ... other fields, getters/setters, etc ...
}
Person p = new Person();
p.setId(42L);
Hibernate.unknownFunction(p); // returns 42L
Sure I could read the annotations to find the #Id field myself, but this seems like something that might be built in.
session.getIdentifier(object)
Return the identifier value of the given entity as associated with this session. An exception is thrown if the given entity instance is transient or detached in relation to this session.
The object needs to have an ID, and to be associated with the current session, otherwise an exception is thrown. But that is logical, I think.
Well, if you need a method that return id in arbitrary classes, design a interface to satisfy this. for example:
public interface IdHolder {
Integer getId();
}
With such interface, you could make some utility methods to retrive id from arbitrary classes.
The cglib is a robust but a bit of tricky way to do it.
I can't say 100% no - but I really doubt it since not all Entities are annotated with #Id; there are other variants that can be used such as #EmbeddedId. Given this, can't you just use reflection to get at your id value?

Persisting Serializable Objects in Hibernate

I am attempting to persist objects that contain some large Serializable types. I want Hibernate to automatically generate my DDL (using Hibernate annotations). For the most part, this works, but the default database column type used by Hibernate when persisting these types is tinyblob. Unfortunately, this causes crashes when attempting to persist my class, because these types will not fit within the length of tinyblob.
However, if I manually set the type (using #Column(columnDefinition="longblob"), or more portably #Column(length=500000)), it works fine. Is there any way to make the default binary type longblob instead of tinyblob, so that I don't need to manually specify the #Column annotation on each field?
ExampleClass.java:
public class ExampleClass
{
#Column(columnDefinition="longblob")
ExampleSerializable ser1;
#Column(columnDefinition="longblob")
ExampleSerializable ser2;
...
}
ExampleSerializable.java:
public class ExampleSerializable implements java.io.Serializable
{
// MANY Fields
}
EDIT
Since there seems to be some confusion: annotating each field with #Column(columnDefinition="longblob") (or more portably: #Column(length=500000)), already works. I am looking for a solution that does not require me to annotate each and every field.
I think (didn't test it) that Hibernate will generate a tinyblob, blob, mediumblob column depending on the column length (respectively 255, 65535, 16777215) which defaults to 255. I would try to specify it (and this would be a portable solution).
Now, if really you want to force things, you'll have to extend the MySQL dialect (but this will harm portability).

Categories

Resources