I have a couple of base classes for my Hibernate entities:
#MappedSuperclass
public abstract class Entity<T> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private T id;
public T getId() { return this.id; }
}
#MappedSuperclass
public abstract class TimestampedEntity<T> extends Entity<T> {
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_at")
private Date createdAt;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "updated_at")
private Date updatedAt;
// Getters and setters for createdAt and updatedAt....
}
Each entity class obviously extends these base classes and gets the id property and the createdAt and updatedAt properties...
#Entity
public class SomeEntity extends TimestampedEntity<Long> {
// Insert SomeEntity's fields/getters/setters here. Nothing out of the ordinary.
}
My problem is that when saving a new instance of SomeEntity, Hibernate is ignoring any property values from the superclasses. It will attempt to insert its own value for the id column (which is obviously not in sync with the table's identity column) and attempts to insert nulls for both createdAt and updatedAt even though they've definitely been set.
SomeEntity e = new SomeEntity(/* ... */);
e.setCreatedAt(new Date());
e.setUpdatedAt(new Date());
// Here Hibernate runs an INSERT statement with nulls for both createdAt and updatedAt
// as well as a wildly out of sequence value for id
// I don't think it should be trying to insert id at all, since the GenerationType is IDENTITY
Long id = (Long)session.save(e);
What am I doing wrong here? How can I make Hibernate pick up the property values from the MappedSuperclasses?
You have those fields declared as private. If that's really what you intend (as opposed to protected or package-protected), move those annotations to the getters.
Having annotations on getters is defined as a hibernate best practice.
See: Hibernate Annotation Placement Question
An instance of SomeEntity doesn't actually have the field 'updatedAt' because that field is a private member of its super class. The persistence provider sees that the super class needs columns for those fields, but it also sees that the instantiated concrete class doesn't make use of those columns since the concrete class can't 'see' the field 'updateAt'.
Related
This question is similar to Pete_Gore's but for spring hibernate. I want to create an Entity with an Embedded object which the Embedded object can be nullable, but if it is defined then the properties in the Embedded object should be non-nullable.
Entity.java
#Entity
public class EntityObject {
#Column
private String name;
#Embedded
private EmbeddedObject embeddedObject;// This should be nullable
//Getters, setters, and constructor
}
Embedded.java
#Embeddable
public class EmbeddedObject {
#Column(nullable = false)
private String exampleProperty;// This should be non-nullable
//Getters, setters, and constructor
}
This scenario works when exampleProperty is nullable but when I set it to be non-nullable I get an error similar to the following when creating an EntityObject with a null EmbeddedObject.
NULL not allowed for column "exampleProperty"; SQL statement:
Add in your embeddable class:
#Formula("0")
int aNUll;
The way I achieved this is by separating the Embeddable class into its own table. This is done by declaring a #SecondaryTable on the main Entity class. And then each field of the Embedded class must specify that it uses that table. So your example would be something like this:
Entity:
#Entity
#SecondaryTable(name = "embedded_table", pkJoinColumns = #PrimaryKeyJoinColumn(name = "entity_name"))
public class EntityObject {
#Column
private String name;
#Embedded
private EmbeddedObject embeddedObject; // this is nullable
//Getters, setters, and constructor
}
Embedded:
#Embeddable
public class EmbeddedObject {
#Column(nullable = false, table = "embedded_table")
private String exampleProperty; // this is not nullable
//Getters, setters, and constructor
}
In general, the idea is that Embedded/Embeddable are trying to merge multiple classes into a single database table. Whereas SecondaryTable instructs JPA to split what would otherwise be a single table, into two. As a single table, any entity row would require values for all non-null fields, even ones for the embedded type. As separate tables, the relational semantics permit that no embedded table row exist for an entity row where the embedded object is null. But, if the embeded object is present, a corresponding row will be added to the embedded table, at which point all non-null values must be defined.
I got some inspiration for this from here: https://www.baeldung.com/jpa-mapping-single-entity-to-multiple-tables
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();
}
...
}
I know there are several questions on this argument, but I think mine is a bit different.
I'm trying to use JPA notation instead of XML mapping. In my queries always there's an undesired dtype column, and I don't want to use neither discriminator column and formula.
I have four classes as follow:
The first named ObjectUUID. All classes extend this super class.
This class is used only to define id field, as follow:
#MappedSuperclass
public class ObjectUUID {
#Id
#GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy = "uuid")
protected String id;
// getter and setter and other static methods
}
Then I have another class, named TrackSys where I define other fields (as insert date and update date) as follow:
#MappedSuperclass
public class TrackSys extends ObjectUUID{
#Column(name="dt_ins")
private CustomCalendar dtInsert;
#Column(name="dt_upd")
private CustomCalendar dtUpdate;
// getter and setter
}
the third and the forth classes are beans mapped on DB, as follow:
#Entity
#Table(name="patient")
public class PatientUUID extends TrackSys {
}
#Entity
#Table(name="patient")
public class Patient extends PatientUUID {
#Column(name="surname")
private String surname;
#Column(name="name")
private String name;
#Column(name="cf")
private String cf;
// getter and setter
}
I define a repository to query my patient table, as follow:
public interface PatientRepository extends JpaRepository<Patient, Long> {
List<Patient> findBySurname(String surname);
}
When my Patient query runs, the generated SQL is the follow:
select patient0_.id as id2_2_, patient0_.dt_ins as dt_ins3_2_,
patient0_.dt_upd as dt_upd4_2_, patient0_.cf as cf7_2_,
patient0_.surname as surname8_2_, patient0_.name as name11_2_,
from patient patient0_ where patient0_.dtype='Patient'
and patient0_.surname=?
Now...
I don't want dtype column and I don't want to use discriminator formula.
With the XML mapping this is possible without particular properties to specify.
With JPA annotation I try in order:
Use #Inheritance(strategy = InheritanceType.SINGLE_TABLE) but dtype is always present
Use #Inheritance(strategy = InheritanceType.JOINED) but dtype is always present
Now, my feeling versus dtype is only hate, how can I get this simple query, as follow:
select patient0_.id as id2_2_, patient0_.dt_ins as dt_ins3_2_,
patient0_.dt_upd as dt_upd4_2_, patient0_.cf as cf7_2_,
patient0_.surname as surname8_2_, patient0_.name as name11_2_,
from patient patient0_ where patient0_.surname=?
This must be quite naive but I have a doubt on when to use #Entity and #Embeddable.
Say I have a User and Notification class.
#Entity
public class User{
//other properties
#onetomany
private List<Notification> notifications;
}
#Entity
public class Notification{
//properties
}
I understand that there will be tables for class User and Notification, and a third table for mapping.
What if I do it like this?
#Entity
public class User {
//other properties
#ElementCollection
private List<Notification> notifications;
}
#Embeddable
public class Notification{
//properties
}
I know this won't create a table for Notification. But I can still store my notification objects. I went through the documentation, but couple of doubts:
Is it based on whether I want to see class B as a seperate table?
Is there a performance difference b/w creating a table and an embeddable object?
What can I not do with embeddable object that I can do with a table other than directly querying the table?
NOTES
For anyone reading this question, this question too might help you.
Is it based on whether I want to see class B as a separate table?
Yes, when you use #Embedded, You embed that #Embeddable entity in #Entity class, which makes it to add columns for embedded entity in same table of #Entity class.
Is there a performance difference b/w creating a table and an embeddable object?
When you use #Embedded, for table creation, one query is required, also for inserting and selecting a row. But if you don't use it, multiple queries are required, hence, use of #Embedded yields more performance, we can say.
What can I not do with embeddable object that I can do with a table other than directly querying the table?
Removing the respective embedded entity may be, but there may be integrity constraint violations for this.
In JPA, there’s a couple ways to create composite key fields. Lets see the method using the #Embeddable annotation.
Let’s start with the Entity class.
#Entity
#Table
public class TraceRecord {
#Id
private TraceRecordPk id;
#Version
#Transient
private int version;
#Column(columnDefinition = "char")
private String durationOfCall;
#Column(columnDefinition = "char")
private String digitsDialed;
#Column(columnDefinition = "char")
private String prefixCalled;
#Column(columnDefinition = "char")
private String areaCodeCalled;
#Column(columnDefinition = "char")
private String numberCalled;
}
This is a pretty simple Entity class with an #Id and #Version field and a few #Column definitions. Without going into too much detail, you’ll see that the #Version field is also annotated #Transient. I’ve done this simply because my table also doesn’t have a column for tracking versions, but my database is journaled, so I’m not too concerned about versioning. You’ll also notice that the #Column fields have a value of “char” set on the columnDefinition attribute. This is because the fields in my table are defined as char and not varchar. If they were varchar, I wouldn’t need to do this since a String maps to a varchar field by default.
The #Id field is what I’m interested in right now. It’s not a standard Java type, but a class I’ve defined myself. Here is that class.
#Embeddable
public class TraceRecordPk implements Serializable {
private static final long serialVersionUID = 1L;
#Temporal(TemporalType.DATE)
#Column
private Date dateOfCall;
#Column(columnDefinition="char")
private String timeOfCall;
#Column(columnDefinition="char")
private String callingParty;
/**
* Constructor that takes values for all 3 members.
*
* #param dateOfCall Date the call was made
* #param timeOfCall Time the call was made
* #param callingParty Extension from which the call originated
*/
public TraceRecordPk(Date dateOfCall, String timeOfCall, String callingParty) {
this.dateOfCall = dateOfCall;
this.timeOfCall = timeOfCall;
this.callingParty = callingParty;
}
}
To make this class capable of being an #Id field on an Entity class, it needs to be annotated with #Embeddable like I mentioned earlier. The 3 fields I’ve selected for my composite key are just normal #Column definitions. Rather than create getters/setters for each field, I’ve simply implemented a constructor that takes values for all 3 fields, making any instance immutable. When annotating a class with #Embeddable, that class will need to implement Serializable. So I’ve added a default serialVersionUID to accomodate.
Now that you have a class created and annotated with #Embeddable, you can now use it as the type for an #Id field in your Entity class. Simple stuff eh.
At the moment I have an Hibernate entity class as follows:
#Entity
#Table(name = "entity")
public class Entity implements Serializable {
private static final long serialVersionUID = 2040757598327793105L;
#Id
#Column
private int id;
#Column
private String data;
#Column(name = "last_modified")
#Temporal(TemporalType.TIMESTAMP)
private Date lastModified;
}
I've found that even when the non-timestamp fields are not modified (i.e. the data field) a call to merge still updates the timestamp. I would like the timestamp to only update when other data fields have changed.
Is there anyway I can prevent calls to merge making a SQL UPDATE when all other data fields are not modified, or do I have to explicitly check for this myself in the code?
Update (thanks to comment):
Since v4 of Hibernate #Entity annotation is deprecated and for allowing dynamic updates you should use #DynamicUpdate(true) (in conjunction with #SelectBeforeUpdate(true))
If you want to prevent unmodified fields to be included in UPDATE queries, add this on your entity:
#org.hibernate.annotations.Entity(dynamicUpdate=true) // update only changed fields
public class ...