I have read the O/R Mapping of Hibernate and I just can't seem to get past the part on polymorphism.
According to https://docs.jboss.org/hibernate/orm/5.0/manual/en-US/html/ch05.html,
Implicit polymorphisms means that instances of the class will be
returned by a query that names any superclass or implemented interface
or class, and that instances of any subclass of the class will be
returned by a query that names the class itself
whereas
Explicit polymorphisms means that class instances will be returned
only by queries that explicitly name that class. Queries that name the
class will return only instances of subclasses mapped
I just want to understand how these 2 work. Can somebody explain these terms using an example(doesn't have to be too complex) with the use of code? I would appreciate your help
First of all the org.hibernate.annotations.Entity annotation is deprecated now. You should use the #Polymorphism annotation instead.
Now, imagine that you have the following schema:
create table TST_STUDENT
(
st_id int not null,
st_name varchar(200),
primary key (st_id)
);
insert into TST_STUDENT values (1, 'Kostya'), (2, 'Yulia'), (3, 'Borya'), (4, 'Misha');
create table TST_TEACHER
(
tcr_id int not null,
tcr_first_name varchar(200),
tcr_last_name varchar(200),
primary key (tcr_id)
);
insert into TST_TEACHER values (1, 'Mikhail', 'Bulgakov'), (2, 'Leo', 'Tolstoy');
and the following mapping:
public interface Person
{
Long getId();
String getName();
}
#Entity
#Table(name = "TST_STUDENT")
public class Student implements Person
{
#Id
#Column(name = "st_id")
private Long id;
#Column(name = "st_name")
private String name;
public Student()
{
}
// getters / setters
}
and Teacher entity:
import org.hibernate.annotations.Polymorphism;
import org.hibernate.annotations.PolymorphismType;
#Entity
#Table(name = "TST_TEACHER")
// #Polymorphism(type = PolymorphismType.EXPLICIT)
public class Teacher implements Person
{
#Id
#Column(name = "tcr_id")
private Long id;
#Column(name = "tcr_first_name")
private String name;
#Column(name = "tcr_last_name")
private String lastName;
public Teacher()
{
}
// getters / setters
}
Now, if you run the following query:
List<Person> persons = em.createQuery("select p from com.your.entities.Person p", Person.class).getResultList();
you will get all rows from the TST_STUDENT table plus all rows from the TST_TEACHER table.
But, if you uncomment this line:
#Entity
#Table(name = "TST_TEACHER")
#Polymorphism(type = PolymorphismType.EXPLICIT) // now we use explicit polymorphism for the Teacher entity
public class Teacher implements Person
The mentioned above query will return only rows from the TST_STUDENT table.
This is what this annotation mean.
By default, when you query a base class entity, the polymorphic query will fetch all subclasses belonging to the base type. You can even query interfaces or base classes that don’t belong to the JPA entity inheritance model.
P.S. See also this part of documentation.
Related
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=?
Writing the case it will be more simple to explain.
I am using Seam 2.3.1 v Hibernate JPA 2.0 and in our project. I have a base Person Entity Class.
#Entity
#Name("person")
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "person")
public class Person extends BaseEntity {
private String name;
private String surName;
private String email;
private String phone;
// getter & setters
}
And I have 3 more Entity extends from Person as #Inheritance(strategy = InheritanceType.JOINED): Personel, Musteri, DisPaydas
#Entity
#Name("personel")
public class Personel extends Person {
private String appellation;
// getter & setters
}
I want to List personels, musteris and dispaydas in my bean however when I set them in my Group Entity, I want to save them as Person.
In fact in DB there is no difference between person_id and personel_id, they are same. However when I listing it, they are List<Personel>
In summary:
I want to get List<Person> from List<Personel>
or Person from Personel object.
You're going to have to define an #Id and #Column in the Person class for the person_id.
However, since the column has a different name in the child entity, you'll need to change it using an #AttributeOverride to point to the personel_id #Column.
It couldn't hurt to use a #DiscriminatorColumn in the parent and #DiscriminatorValue in the children, but I don't think Hibernate requires those.
I'm using Ebean for my object-mapping and I've made my SQL tables like so
create table company (
id int auto_increment not null primary key,
name varchar(100)
);
create table employee (
id int auto_increment not null primary key,
name varchar(100) not null,
company_id int not null,
constraint foreign key (company_id) references company (id)
on delete restrict on update restrict
);
This is the company Ebean model
import javax.persistence.Entity;
import javax.persistence.Id;
import com.avaje.ebean.Model;
#Entity
public class Company extends Model
{
#Id
public Integer id;
public String name;
}
And employee model
#Entity
public class Employee extends Model
{
#Id
public Integer id;
public String name;
#ManyToOne
public Company company;
public static Finder<Long, Employee> find = new Finder<Long, Employee>(Long.class, Employee.class);
}
When I run the following
Company company = new Company();
company.name = "Microsoft";
company.save();
Employee employee = new Employee();
employee.name = "Mr John";
employee.company = company;
employee.save();
Employee mrJohn = Employee.find.where().eq("id", 1).findUnique();
System.out.println(mrJohn.company.id);
System.out.println(mrJohn.company.name);
The first System.out.println gives 1 (which is the correct id of the company assigned to the employee) but the second shows null (which I expected should have the value "Microsoft"), the output being
1
null
The question therefore is why is only the id of the Company model retrieved, and not the other associated data?
You can use fetch() to eagerly fetch other parts of the graph. In this case fetch the company name like:
Employee.find.fetch("company","name").where().eq("id",1).findUnique();
In short field access can not be intercepted (unless you enhance the caller). So using field access for company.name meant that customer.name was a GETFIELD operation and that it was not being intercepted by Ebean and hence lazy loading was not invoked (and hence a null was returned).
Changing over to use getters/setters meant that lazy loading was invoked when customer.getName() was called.
Java does not support properties (hance the getters and setters). You can look at other JVM languages that do like Groovy and Kotlin.
Groovy supports properties, an example using Groovy with #CompileStatic is:
https://github.com/ebean-orm/avaje-ebeanorm-examples/blob/master/e-groovy
https://github.com/ebean-orm/avaje-ebeanorm-examples/blob/master/e-groovy/src/main/groovy/org/example/domain/Customer.groovy
Kotlin supports properties, an example is:
https://github.com/ebean-orm/avaje-ebeanorm-examples/tree/master/e-kotlin-maven
Cheers, Rob.
I faced this problem today and I found out the reason it's fetch lazily didn't invoke when we access public variable.
Fetch lazily can be invoked by calling getter method of attributes. So I need to create getter in my model fetch data using getter method.
#Entity
public class Company extends Model
{
#Id
public Integer id;
public String name;
public Integer getId() {
return id;
}
public String getName() {
return name;
}
}
Then fetch company:
System.out.println(mrJohn.company.getName());
I have a generic Database structure which can store several user-defined records. For example, the main table is RECORD and the columns are STRING01, STRING02, [...], NUM01, NUM02 etc.
I know this is a bit weird, but it has advantages as well as disadvantages. However, this structure exists and can't be changed. Now I want to create some JPA classes.
First, I created an abstract class RECORD as follows (the Annotations are placed on the gettersthe example is just simplified):
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="TYPE", discriminatorType=DiscriminatorType.STRING)
public abstract class Record {
#Id
private long id;
#Column(name="STRING01")
private String string01;
#Column(name="STRING02")
private String string02;
#Column(name="NUM01")
private BigDecimal num01;
}
Then, I created specific classes inherited from RECORD:
#Entity
#DiscriminatorValue("Person")
public class Person extends Record {
#Transient
public String getFirstName() {
return getString01();
}
public void setFirstName(String name) {
setString01(name);
}
#Transient
public BigDecimal getWeight() {
return getNum01();
}
public void setWeight(BigDecimal weight) {
setNum01(weight);
}
}
This works fine, as I can query RECORD for a PERSON's primary key (via EntityManager.find()) and get a Result as instance of PERSON. I can query for FirstName and Weight without having to know the generic column names.
However, if I write my own JPA Query like SELECT p FROM Person p WHERE p.firstName = 'Michael', it fails. firstName is transient, and here I have to use the generic name string01.
Is there some way of overriding the base class' attribute name in JPA? Maybe there's a vendor-specific solution (I'm using EclipseLink)?
You can try and map multiple attributes to the same column.
The additional attribute would then be annotated with #Column( name = "column name", insertable = false, updatable = false, nullable = false ).
Alternatively, you might be able to replace/enhance the JPQL resolver in order to internally map p.firstName to p.string01, but that would be EclipseLink specific, and I don't really know if that's even possible. Take this as just a hint what to look for.