Primary Key Column Name with Spring Data JPA and Hibernate - java

I have the following setup with Spring Data JPA and Hibernate as the persistence provider. All of my entities inherit from a base class
#MappedSuperclass
public class BaseEntity {
#Id
private id;
#Version
private String version;
//more common fields
}
For example:
#Entity
public class Foo extends BaseEntity {
}
This leads to a primary key column with name "ID" to be generated on the "FOO" table. I would like to change the naming of the primary key column. It should reflect the name of class or table. So it should be "FOO_ID" instead of just "ID".
I know that I could do this statically by using #Column(name = "FOO_ID"). But that would mean I have to do this for every Entity. Is there a more dynamic way to achieve this?

I know this is an old question, but stumbled across this looking for an answer... Eventually found this solution elsewhere:
#Entity
#AttributeOverride(name="id", column=#Column(name="FOO_ID"))
public class Foo extends BaseEntity {
}

All your subClasses will have the same ID column name because of the inheritance, you can specify a common id colum name for all subClasses in the Base entity Class

Why use inheritance then? Just do it without inheritance.
You could use getters/setters to rename your fields
Ex:
class Foo {
private Long id;
public Long getFooId() {
return this.id;
}
public void setFooId(Long fooId) {
this.id = fooId;
}
}

Related

I am trying to extend a base class which has Id in two different subclass to hit JPA query [duplicate]

I am creating entities that are the same for two different tables. In order do table mappings etc. different for the two entities but only have the rest of the code in one place - an abstract superclass. The best thing would be to be able to annotate generic stuff such as column names (since the will be identical) in the super class but that does not work because JPA annotations are not inherited by child classes. Here is an example:
public abstract class MyAbstractEntity {
#Column(name="PROPERTY") //This will not be inherited and is therefore useless here
protected String property;
public String getProperty() {
return this.property;
}
//setters, hashCode, equals etc. methods
}
Which I would like to inherit and only specify the child-specific stuff, like annotations:
#Entity
#Table(name="MY_ENTITY_TABLE")
public class MyEntity extends MyAbstractEntity {
//This will not work since this field does not override the super class field, thus the setters and getters break.
#Column(name="PROPERTY")
protected String property;
}
Any ideas or will I have to create fields, getters and setters in the child classes?
Thanks,
Kris
You might want to annotate MyAbstractEntity with #MappedSuperclass class so that hibernate will import the configuration of MyAbstractEntity in the child and you won't have to override the field, just use the parent's. That annotation is the signal to hibernate that it has to examine the parent class too. Otherwise it assumes it can ignore it.
Here is an example with some explanations that may help.
#MappedSuperclass:
Is a convenience class
Is used to store shared state & behavior available to child classes
Is not persistable
Only child classes are persistable
#Inheritance specifies one of three mapping strategies:
Single-Table
Joined
Table per Class
#DiscriminatorColumn is used to define which column will be used to distinguish between child objects.
#DiscriminatorValue is used to specify a value that is used to distinguish a child object.
The following code results in the following:
You can see that the id field is in both tables, but is only specified in the AbstractEntityId #MappedSuperclass.
Also, the #DisciminatorColumn is shown as PARTY_TYPE in the Party table.
The #DiscriminatorValue is shown as Person as a record in the PARTY_TYPE column of the Party table.
Very importantly, the AbstractEntityId class does not get persisted at all.
I have not specified #Column annotations and instead are just relying on the default values.
If you added an Organisation entity that extended Party and if that was persisted next, then the Party table would have:
id = 2
PARTY_TYPE = "Organisation"
The Organisation table first entry would have:
id = 2
other attribute value associated specifically with organisations
#MappedSuperclass
#SequenceGenerator(name = "sequenceGenerator",
initialValue = 1, allocationSize = 1)
public class AbstractEntityId implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "sequenceGenerator")
protected Long id;
public AbstractEntityId() {}
public Long getId() {
return id;
}
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "PARTY_TYPE",
discriminatorType = DiscriminatorType.STRING)
public class Party extends AbstractEntityId {
public Party() {}
}
#Entity
#DiscriminatorValue("Person")
public class Person extends Party {
private String givenName;
private String familyName;
private String preferredName;
#Temporal(TemporalType.DATE)
private Date dateOfBirth;
private String gender;
public Person() {}
// getter & setters etc.
}
Hope this helps :)
Mark the superclass as
#MappedSuperclass
and remove the property from the child class.
Annotating your base class with #MappedSuperclass should do exactly what you want.
This is old, but I recently dealt with this and would like to share my solution. You can add annotations to an overridden getter.
#MappedSuperclass
public abstract class AbstractEntity<ID extends Serializable> implements Serializable {
#Column(name = "id", nullable = false, updatable = false)
#Id
private ID id;
public ID getId() {
return id;
}
...
}
#Entity
#Table(name = "address")
public final class Address extends AbstractEntity<UUID> implements Serializable {
...
#Override
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
public final UUID getId() {
return super.getId();
}
...
}

Hibernate. Generic Composite PK issue

I have an issue with composite primary key in Hibernate.
Example:
I have a class that represents base primary key for all instances of this.
public abstract class PrimaryKey implements Serializable { /* ... */ }
It has nothing but implements java.io.Serializable interface and can be used in generics or another class's methods as parameter to narrow an accepted classes.
Another primary key class should inherit it and add your specific fields as keys. For example:
public class PassportPK extends PrimaryKey {
private String number;
private String series;
public PassportPK() {}
public PassportPK(String number, String series) {
this.number = number;
this.series = series;
}
// Getters/setters are below.
}
Then it's used in the appropriate entity like this:
#Entity
#Table(name = "T_PASSPORTS")
#IdClass(PassportPK.class)
public class Passport implements Serializable {
#Id
#Column(name = "F_NUMBER")
private String number;
#Id
#Column(name = "F_SERIES")
private String series;
public Passport() {}
// Getters/setters are below.
}
Everything works fine if I have a deal with entities like this.
But some entities in my project have a simple primary key like int, long, String, and etc.
In this case I'd like to have a generic primary key like this one:
public class SimplePK<T extends Serializable> extends PrimaryKey {
/**
* Represents a simple key field of entity (i.e. int, long, String, ...);
*/
private T id;
public SimplePK() {}
public SimplePK(T id) {
this.id = id;
}
public T getId() {
return this.id;
}
public void setId(T id) {
this.id = id;
}
}
QUESTION: How can I resolve it in annotation mapping way?
p.s. When I'm trying to resolve it like in the previous example (via #IdClass(SimplePK.class) I catch a "org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in class path resource [application-context.xml]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: Property com.testprj.entity.SimplePK.id has an unbound type and no explicit target entity. Resolve this Generic usage issue or set an explicit target attribute (eg #OneToMany(target=) or use an explicit #Type" exception.
p.p.s. I use Hibernate with Spring framework for wiring components.
I would be grateful for any help!
I think that you can't use a generic type inside the id Class.
Used the #IdClass to indicate the Class for the Composite Primary Key, if you want to used only one attribute like primary key you have to use #Id over the declaration and remove #IdClass.
Example:
#Entity
#Table(name = "T_PASSPORTS")
public class Passport implements Serializable {
#Id
private String id; //Or int, long...
public Passport() {}
// Getters/setters are below.
}

Hibernate and #AttributeOverride

I have 3 classes:
#MappedSuperclass
public class BaseEntity {
#Id
#GeneratedValue
private Long id;
...
}
#Entity
public class Person extends BaseEntity {
private String name;
...
}
#Entity
#AttributeOverride(name = "id", #Column = (name = "idStudent"))
public class Student extends Person {
private float avgGrades;
...
}
I would like to override ID property so in Student table there would be a idStudent column instead of id. Unfortunately, the code above doesn't work. It looks like #AttributeOverride only works for the class you extending (and no one step further). Is there any way to override attribute name in situation I've descried (override attribute in class which exteds our class being extended) ?
Your problem is very easy to understand, if you know what the default inheritance type is: SINGLE_TABLE.
That means all entities that extending Person are in the same table. And thus Person already defines the ID column. Because you would otherwise violate the contract of the primary key column of your Person table.
I cite the JavaDoc of #AttributeOverride as well:
May be applied to an entity that extends a mapped superclass or to an embedded field or property to override a basic mapping or id mapping defined by the mapped superclass or embeddable class (or embeddable class of one of its attributes).
It always helps to read the JavaDoc first, before asking questions here.
What can you do about it? Make your Person a #MappedSuperclass (or create a BasePerson that is one).

How to create a generic entity model class that supports generic id including auto generated ids?

I have three kinds of primary keys for tables:
INT auto generated primary key which use AUTO_INCREMENT capacity from database vendor (MySQL)
CHAR(X) primary key to store a user readable value as key (where X is a number and 50 <= X <= 60)
Complex primary keys, composed by 2 or 3 fields of the table.
Also, there are some group of fields that may be present (or not):
version, INT field.
createdBy, VARCHAR(60) field, and lastUpdatedBy, VARCHAR(60) field (there are more fields but these covers a basic example).
Some examples of above:
Table1
id int primary key auto_increment
version int
value char(10)
createdBy varchar(60)
lastUpdatedBy varchar(60)
Table2
id char(60) primary key
shortDescription varchar(20)
longDescription varchar(100)
Table3
field1 int primary key
field2 int primary key
amount decimal(10, 5)
version int
With all this in mind, I need to create a generic set of classes that supports these requirements and allows CRUD operations using Hibernate 4.3 and JPA 2.1.
Here's my current model (getters/setters avoided to shorten the code sample):
#MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
protected T id;
}
#MappedSuperclass
public abstract class VersionedEntity<T> extends BaseEntity<T> {
#Version
protected int version;
}
#MappedSuperclass
public abstract class MaintainedEntity<T> extends VersionedEntity<T> {
#Column
protected String createdBy;
#Column
protected String lastUpdatedBy;
}
#Entity
public class Table1 extends MaintainedEntity<Long> {
#Column
private String value;
}
#Entity
public class Table2 extends BaseEntity<String> {
#Column
private String shortDescription;
#Column
private String longDescription;
}
I'm currently testing save instances of Table1 and Table2. I have the following code:
SessionFactory sf = HibernateUtils.getSessionFactory();
Session session = sf.getCurrentSession();
session.beginTransaction();
Table1 newTable1 = new Table1();
newTable1.setValue("foo");
session.save(newTable1); //works
Table2 newTable2 = new Table2();
//here I want to set the ID manually
newTable2.setId("foo_id");
newTable2.setShortDescription("short desc");
newTable2.setLongDescription("long description");
session.save(newTable2); //fails
session.getTransaction().commit();
sf.close();
It fails when trying to save Table2 and I get the following error:
Caused by: java.sql.SQLException: Field 'id' doesn't have a default value
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:996)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3887)
The error message is obvious because a CHAR(X) field doesn't have a default value and won't have it (AFAIK). I tried changing the generation strategy to GenerationType.AUTO and got the same error message.
How can I remodel these classes in order to support these requirements? Or even better, how could I provide a generation strategy that depends on the key of the entity I'm saving, which could be auto generated or provided by me?
Involved technologies:
Java SDK 8
Hibernate 4.3.6
JPA 2.1
MySQL and Postgres databases
OS: Windows 7 Professional
Note: the above may (and probably will) change in order to be supported for other implementations of JPA 2.1 like EclipseLink.
Did not try this, but according to Hibernate's api this should not be complicated by creating custom implementation of IdentityGenerator.
It's generate method gets and object for which you are generating the value so you can check the type of the id field and return appropriate value for your primary key.
public class DynamicGenerator implements IdentityGenerator
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
if (shouldUseAutoincrementStartegy(object)) { // basing on object detect if this should be autoincrement or not, for example inspect the type of id field by using reflection - if the type is Integer use IdentityGenerator, otherwise another generator
return new IdentityGenerator().generate(seession, object)
} else { // else if (shouldUseTextKey)
String textKey = generateKey(session, object); // generate key for your object
// you can of course connect to database here and execute statements if you need:
// Connection connection = session.connection();
// PreparedStatement ps = connection.prepareStatement("SELECT nextkey from text_keys_table");
// (...)
return textKey;
}
}
}
Having this simply use it as your generation strategy:
#MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
#Id
#GenericGenerator(name="seq_id", strategy="my.package.DynamicGenerator")
protected T id;
}
For Hibernate 4, you should implement IdentifierGenerator interface.
As above is accepted for Hibernate it should be still possible to create it in more generic way for any "jpa compliant" provider. According to JPA api in GeneratedValue annotation you can provide your custom generator. This means that you can provide the name of your custom generator and you should implement this generator for each jpa provider.
This would mean you need to annotate BaseEntity with following annotation
#MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
#Id
#GeneratedValue(generator="my-custom-generator")
protected T id;
}
Now you need to register custom generator with name "my-custom-generator" for each jpa provider you would like to use.
For Hibernate this is surly done by #GenericGenerator annotation as shown before (adding #GenericGenerator(name="my-custom-generator", strategy="my.package.DynamicGenerator" to BaseEntity class on either id field or BaseEntity class level should be sufficient).
In EclipseLink I see that you can do this via GeneratedValue annotation and registering it via SessionCustomizer:
properties.put(PersistenceUnitProperties.SESSION_CUSTOMIZER,
"my.custom.CustomIdGenerator");
public class CustomIdGenerator extends Sequence implements SessionCustomizer {
#Override
public Object getGeneratedValue(Accessor accessor,
AbstractSession writeSession, String seqName) {
return "Id"; // generate the id
}
#Override
public Vector getGeneratedVector(Accessor accessor,
AbstractSession writeSession, String seqName, int size) {
return null;
}
#Override
protected void onConnect() {
}
#Override
protected void onDisconnect() {
}
#Override
public boolean shouldAcquireValueAfterInsert() {
return false;
}
#Override
public boolean shouldOverrideExistingValue(String seqName,
Object existingValue) {
return ((String) existingValue).isEmpty();
}
#Override
public boolean shouldUseTransaction() {
return false;
}
#Override
public boolean shouldUsePreallocation() {
return false;
}
public void customize(Session session) throws Exception {
CustomIdGenerator sequence = new CustomIdGenerator ("my-custom-generator");
session.getLogin().addSequence(sequence);
}
}
Each provider must give a way to register id generator, so you would need to implement and register custom generation strategy for each of the provider if you want to support all of them.
You can "workaround" this forcing derived class to implement method which will ensure the Id is assigned and annotate this method with #PrePersist. You can provide default implementation for classes for which the Id will be auto generated.
Somethig like:
#MappedSuperclass
public abstract class BaseEntity<T> implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
protected T id;
#PrePersist
public void ensureIdAssigned() {
ensureIdAssignedInternal();
}
public abstract void ensureIdAssignedInternal();
}
#MappedSuperclass
public abstract class AutoIdMaintaintedEntity<T> extends MaintainedEntity<T> { // provide default implementation for Entities with Id generated by #GeneratedValue(strategy=GenerationType.IDENTITY) on BaseEntity superclass
public void ensureIdAssignedInternal() {
// nothing here since the Id will be automatically assigned
}
}
#Entity
public class Table1 extends AutoIdMaintaintedEntity<Long> {
#Column
private String value;
}
#Entity
public class Table2 extends BaseEntity<String> {
#Column
private String shortDescription;
#Column
private String longDescription;
public void ensureIdAssignedInternal() {
this.id = generateMyTextId();
}
private String generateMyTextId() {
return "text id";
}
}
Inheritance hierarchies fight ORM.
So keep things simple and stay a little closer to the database implementation. Don't map a hierarchy of abstract superclasses for this, but embed annotated POJO's for chunks of shared columns. They may well turn out to be handy to work with in the rest of your code as well.
Create #Embeddable classes for the shared fields, and make the class for your composite ID #Embeddable too.
#Embeddable
public class Maintained implements Serializable{
private String maintainedBy;
private String updatedBy;
// getters and setters
}
#Embeddable
public class CompositeId implements Serializable{
#Column
private int id1;
#Column
private int id2;
...
}
The simplest version of your implementation classes then look like this:
#Entity
public class Table1 {
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Id
protected Long id;
#Version
private String version;
#Embedded
private Maintained maintained;
...
public Maintained getMaintained(){
return maintained;
}
}
For the String ID, no auto generation:
#Entity
public class Table2 {
#Id
private String id;
#Column
private String shortDescription;
#Column
private String longDescription;
...
}
And the composite ID as #EmbeddedId:
#Entity
public class Table3 {
#EmbeddedId
private CompositeId id;
#Version
private String version;
#Column
private int amount;
...
}
As an extra benefit, you can mix and match adding more of these traits if you like, since you're no longer constrained by the inheritance tree.
(But you can keep the hierarchy in place containing getters and setters and default delegates if existing code relies on it and/or benefits from it.)

How do I override the GenerationType strategy using Hibernate/JPA annotations?

I'm considering using Annotations to define my Hibernate mappings but have run into a problem: I want to use a base entity class to define common fields (including the ID field) but I want different tables to have different ID generation strategies:
#MappedSuperclass
public abstract class Base implements Serializable {
#Id
#Column(name="ID", nullable = false)
private Integer id;
public Integer getId(){return id;}
public void setId(Integer id){this.id = id;}
...
}
#Entity
#Table(name="TABLE_A")
public class TableA extends Base {
// Table_A wants to set an application-defined value for ID
...
}
#Entity
#Table(name="TABLE_B")
public class TableB extends Base {
// How do I specify #GeneratedValue(strategy = AUTO) for ID here?
...
}
Is there some way to do this? I've tried including the following into TableB but hibernate objected to my having the same column twice and it seems wrong:
#Override // So that we can set Generated strategy
#Id
#GeneratedValue(strategy = AUTO)
public Integer getId() {
return super.getId();
}
In the code above, it looks like you're mixing annotations on fields (superclass) and methods (subclass). The Hibernate reference documentation recommends avoiding this, and I suspect it might be causing the problem. In my experience with Hibernate, it's safer and more flexible to annotate getter/setter methods instead of fields anyway, so I suggest sticking to that design if you can.
As a solution to your problem, I recommend removing the id field from your Base superclass altogether. Instead, move that field into the subclasses, and create abstract getId() and setId() methods in your Base class. Then override/implement the getId() and setId() methods in your subclasses and annotate the getters with the desired generation strategy.
Hope this helps.
On the method in the child dont add the second #Id tag.
#Override // So that we can set Generated strategy
#GeneratedValue(strategy = AUTO)
public Integer getId() {
return super.getId();
}
My resolution:
Refactor the Base class into:
#MappedSuperclass
abstract class SuperBase<K> {
public abstract K getId();
}
#MappedSuperclass
class Base<K> extends SuperBase<K> {
#Id #GeneratedValue(AUTO)
public K getId() { ... }
}
Then, you can extends from Base for most of your entity classes, and if one needs to override the #GeneratedValue, just extend from SuperBase and define it.
If you put your annotations on the getter rather than the field, when you override the method in the subclass, the annotations placed there will be used rather than the ones in the superclass.

Categories

Resources