In the Hibernate docs, § 5.1.6.2 - Joined subclass strategy it states:
Each subclass must, however, declare a table column holding the object identifier.
The example it gives is as follows:
#Entity #Table(name="CATS")
#Inheritance(strategy=InheritanceType.JOINED)
public class Cat implements Serializable {
#Id #GeneratedValue(generator="cat-uuid")
#GenericGenerator(name="cat-uuid", strategy="uuid")
String getId() { return id; }
...
}
#Entity #Table(name="DOMESTIC_CATS")
#PrimaryKeyJoinColumn(name="CAT")
public class DomesticCat extends Cat {
public String getName() { return name; }
}
I am guessing that name is not the object identifier, so what in the DomesticCat class is the "table column" that holds the "Object identifier"? What would I usually have to do to add this particular required column to my subtypes?
This is probably referring to the "id" field (not shown, but implied by the getId() method). This field is the unique identifier for the row in the "CATS" table containing the information you are looking for. It looks like it is setting the Type of id as String and is expecting a UUID. The sub-types will inherit this field due to their extension of the main class.
Related
I have a mapped super class that I use to define a method it's persistence mapping. I am using the table-per-class inheritance strategy. For example here is some simple class inheritance:
#MappedSuperclass
public class Feline {
private Long id;
private String identifier;
#GeneratedValue(strategy = "GenerationType.AUTO")
#Id
public Long getId() {
return id;
}
public String getIdentifier() {
return identifier;
}
// other getters and setters, methods, etc.
}
The next class does not override the getIdentifier() method and saves the Cat's identifier in the "identifier" column of it's entity table.
#Entity
public class Cat extends Feline {
private Date dateOfBirth;
private Set<Kitten> kittens;
#OneToMany(mappedBy = "mother")
public Set<Kitten> getKittens() {
return kittens;
}
// other getters and setters, methods, etc.
}
In the Kitten class I want to change the identifier to return the Kitten.identifier + " kitten of " + mohter.getIdentifier() or for example "Boots kitten of Ada" and persist this String in the "identifier" column of the entity's table.
#Entity
public class Kitten extends Cat {
private Cat mother;
#ManyToOne
public Cat getMother() {
return mother;
}
#Override
public String getIdentifier() {
return this.getIdentifier() + " kitten of " + mother.getIdentifier();
}
}
When I run this code I get an error message "Caused by: org.Hibernate.MappingException: Duplicate property mapping of identifier found in com.example.Kitten."
Since I am extending a #Mappedsuperclass the identifier field should be mapped to the "identifier" column of each entity table but for some reason this doesn't happen and it tries to map the identifier field twice when I override the getIdentifier() method in the Kitten class.
Both the Cat and Kitten tables have "identifier" columns. I do not understand why I cannot override a method if it returns the correct type to map to the same column.
That won't work. But you can define a discriminator.
Have a look at http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/inheritance.html
The difference between #Entity and #Embeddable annotation when each one is added before class declaration?
the first create class as an entity, second insert column from another table?
the first create class as an table, while second is embedded in another class?
the first sets standard as a class, second define table type
the first create table for that class, second embed something into different class
the first define table property, second create union of two tables
#Entity annotation over a class defines that, it has a distinct separate existence. Thus we can run DB queries, without being dependent on any other class. #Embeddable annotation over a class defines that, it does not have independent existence. Thus we cannot run DB queries, without depending on other class. Here is an example to understand it better:
#Entity
User
-- long id
-- String name
-- String email
#Embedded
-- UserDetails userDetail
#Embeddable
UserDetails
-- Date dateOfBirth
-- String sex
-- String address
-- String maritalStatus
Here you can see without having a User, UserDetails is useless.
Generally, in OOP, we first design the classes and then we design database entities. For some classes (like UserDetails class in the above example), we do not want to have separate tables in DB, where their independent existence is meaningless. In those cases, we mark the class as embeddable.
Typically, embeddable classes share the same table as the Entity in which they are embedded
Entities have an identity and can be queried for. Embeddables have no identity of their own and can only be queried for using the owning entities.
If you open an entity class, you will always find the #Id annotation - it is mandatory. If you open an embeddable class, you will never find an #Id annotation - it is forbidden.
EDIT: It is not entirely correct that embeddables can only be stored as a part of the parent, i.e. in the same table. This is only true for one-to-one relationships. You can have Collections and Maps of embeddable objects in the parent entity and they will be mapped to own collection tables.
An entity class is an ordinary user defined Java class whose instances can be stored in the database.
#Entity
#Table(name="dog")
public class Dog{
#Id
#Column(name = "id")
private int id;
#Embedded
private Animal animal;
public Dog(int id,Animal animal){
this.id=id;
this.animal=animal;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Animal getAnimal() {
return animal;
}
public void setAnimal(Animal animal) {
this.animal = animal;
}
}
Embeddable classes are user defined persistable classes that function as value types. As with other non entity types, instances of an embeddable class can only be stored in the database as embedded objects, i.e. as part of a containing entity object.
#Embeddable
public class Animal {
#Column(name = "name")
private String name;
#Column(name = "location")
private String location;
public Animal(){
}
public Animal(String name,String location){
this.name=name;
this.location=location;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
It is an old topic but I would like to add my answer, which is more from theoretical point of view. In DDD (domain driven design) we usually have Entity and Value Objects. The first ones are identifiable only by a an identity that they have. The second ones are not defined by an identity, which means that if all the components that make that particular objects are the same, than the 2 value objects are the same.
The analogy is that in this case, if we were to apply DDD, the Entity is the class annotated with #Entity and the Value Object is the one with #Embeddable. A demonstration of this is the fact that the embeddable object is added as addditional information to an existing record, which already has its own identity defined externally to the embedded object.
Well #Entity signifies that the entity object has significance all by itself it doesn't require any further association with any other object. Where as #Embeddable object doesn't carry any significance all by itself, it needs association with some other object.
Lets take an example of say i have a Employee Object and it has a collection of Address Object as its member variable. Now when when speak of any address we need to tell whose address it is, which employees address it is. If we just talk about the address it doesn't make any sense. Hope this gives you the difference between the two.
If I have a class such as:
class Person {
private String name;
...constructor, getters, setters, equals, hashcode, tostring...
}
Can I subclass and apply annotations to the name field in the subclass, for example to apply persistence annotations, without re-implementing the rest of the class?
#Entity
class Employee extends Person {
#Column(...)
private String name;
}
That wont work since the fields in super class will not be affected, but you can try this
#Entity
class Employee extends Person {
#Column(name="xxx")
#Override
public void setName(String name) {
super.setName(name);
}
...
No, you can't.
What you are proposing is field shadowing - you can't override a field.
The field name in the subclass has nothing whatsoever to do with the field name in the super class, other than it shares the same name of "name" and thus to distinguish the field in the super class, one must refer to it as super.name in the subclass.
Doing this is generally considered a "bug" (or a potential bug anyway), and best practices are to not shadow fields, because it is so easy to refer to the wrong field without knowing it.
Late answer, but I think overriding the getter method is a solid approach.
This can be a superclass for all tables which has an id field. If you serialize this object into JSON the id will always appear.
#MappedSuperclass
class ModelEntity {
#Id
#Column(
name = "id",
updatable = false,
nullable = false
)
#GeneratedValue(strategy=GenerationType.AUTO)
public Long id
}
But let's say you have the following objects (tables) Person and Occupation where Person has a one to many relationship with Occupation.
#Entity
#Table(name = "occupation")
Occupation extends ModelEntity {
#Column
String company
#Column
String position
}
#Entity
#Table(name = "person")
Person extends ModelEntity {
#Column
String name
#OneToMany
Occupation occupation
}
Provided that id is present in all of the classes that extend ModelEntity if you were to serialize a Person object, you would get something like:
{
"id" : 1,
"name" : "Jordan",
"occupation" : {
"id" : 1,
"company" : "WalMart",
"position" : "Engineer"
}
}
If you did not want the id to show up in the Occupation object, but you did want it to show up in the Person object, you can implement a getId() method at the Occupation class level, and apply desired annotations:
#Transient
public Long getId() {
return id;
}
Now your JSON would appear as follows: even though they both have an id column in the actual database:
{
"id" : 1,
"name" : "Jordan",
"occupation" : {
"company" : "WalMart",
"position" : "Engineer"
}
}
No - you will get two different fields. The annotated name field in Employee will be hiding the name field in the Person class. Person.name will not be annotated.
I have two hibernate classes: a base class, and an extended class that has additional fields. (These fields are mapped by other tables.)
For example, I have:
#Entity
#Table(name="Book")
public class A {
private String ID;
private String Name;
// ...
}
#Entity
#Table(name="Book")
public class B extends A {
public String node_ID;
// ...
}
public class Node {
public String ID; // maps to B.node_ID
// ...
}
How do I map this in Hibernate? The hibernate documentation states three types of inheritence configurations: one table per class, one table with a type column, and a join table -- none of which apply here.
The reason I need to do this is because class A is from generic framework that's reused over multiple projects, and class B (and Node) are extensions specific to one project -- they won't be used again. In the future, I may have perhaps a class C with a house_ID or some other field.
Edit: If I try the above pseudo-code configuration (two entities mapped to the same table) I get an error that the DTYPE column doesn't exist. The HQL has a "where DTYPE="A" appended.
This is possible by mapping the #DiscriminatorColumn and #DiscriminatorValue to the same values for both classes; this can be from any column you use that has the same data regardless of which type (not sure if it works with null values).
The classes should look like so:
#Entity
#Table(name="Book")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="published")
#DiscriminatorValue(value="true")
public class A {
private String ID;
private String Name;
// ...
}
#Entity
#Table(name="Book")
#DiscriminatorValue(value="true")
public class B extends A {
public String node_ID;
// ...
}
For anyone who got here like me and does not want to have the dtype column but instead want to use the same table for more than one entity as is I would recommend using this
Basically you can create a Base like this
#MappedSuperclass
public abstract class BaseBook<T extends BaseBook> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
... any other variables, getters + setters
}
#Entity
#Table(name= "book")
public class BookA extends BaseBook<BookA>{
//Default class no need to specify any variables or getters/setters
}
#Entity
#Table(name= "book")
public class BookB extends BaseBook<BookB>{
#Column(name = "other_field")
private String otherFieldInTableButNotMapedInBase
... Any other fields, getter/setter
}
From the above we have created base super class which does not have any entity or table mapping. We then create BookA to be default with the Entity + Table mapping. From there we can create other Entities all extending from BaseBook but pointing to one table
Is it possible to use an enum as a discriminator value when using SINGLE_TABLE inheritance strategy?
If what you are trying to achieve is to not to duplicate the discriminator values, there is a simple workaround.
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="FREQUENCY",
discriminatorType=DiscriminatorType.STRING
)
public abstract class Event {
}
#Entity
#DiscriminatorValue(value=Frequency.Values.WEEKLY)
public class WeeklyEvent extends Event {
…
}
public enum Frequency {
DAILY(Values.DAILY),
WEEKLY(Values.WEEKLY),
MONTHLY(Values.MONTHLY);
private String value;
…
public static class Values {
public static final String DAILY = "D";
public static final String WEEKLY = "W";
public static final String MONTHLY = "M";
}
}
Nothing to do with Hibernate/JPA really, but better than having to maintain the values in multiple places.
I just wanted to improve the great answer of #asa about the workaround. Usually, we often like to use the discriminator column as an attribute of the abstract class, and mapped with an enum of course. We can still use the solution mentioned above and force some consistencies between enum names (used to map the column) and String values (used as discrimnator values). Here is my suggestion:
public enum ELanguage {
JAVA(Values.JAVA), GROOVY(Values.GROOVY);
private ELanguage (String val) {
// force equality between name of enum instance, and value of constant
if (!this.name().equals(val))
throw new IllegalArgumentException("Incorrect use of ELanguage");
}
public static class Values {
public static final String JAVA= "JAVA";
public static final String GROOVY= "GROOVY";
}
}
And for the entities, here is the code:
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="LANGUAGE_TYPE", discriminatorType=DiscriminatorType.STRING)
public abstract class Snippet {
// update/insert is managed by discriminator mechanics
#Column(name = "LANGUAGE_TYPE", nullable = false, insertable = false, updatable = false)
#Enumerated(EnumType.STRING)
public ELanguage languageType
}
#Entity
#DiscriminatorValue(value=ELanguage.Values.JAVA)
public class JavaSnippet extends Snippet {
…
}
Still not perfect, but a little bit better, I think.
No, unfortunately you can't.
If you try to use an enum as discriminator value, you'll get a Type Mismatch exception ("cannot convert from MyEnum to String"), as the only discriminator types allowed are String, Char and Integer.
Next, I tried using an enum's name and ordinal combined with DiscriminatorType.STRING and DiscriminatorType.INTEGER, respectively. But this didn't work either, as the #DiscriminatorValue annotation (as any other) requires a constant expression:
This doesn't work:
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="FREQUENCY",
discriminatorType=DiscriminatorType.STRING
)
public abstract class Event {}
#Entity
#DiscriminatorValue(value=Frequency.WEEKLY.name())
public class WeeklyEvent extends Event {
// Exception: The value for annotation attribute DiscriminatorValue.value must be a constant expression
}
Doesn't work either:
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="FREQUENCY",
discriminatorType=DiscriminatorType.INTEGER
)
public abstract class Event {}
#Entity
#DiscriminatorValue(value=Frequency.WEEKLY.ordinal())
public class WeeklyEvent extends Event {
// Exception: The value for annotation attribute DiscriminatorValue.value must be a constant expression
}
I would suggest to invert the relationship: define the discriminator value as a constant in the entity, then wrap it in the enum:
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
name = "FIELD_TYPE",
discriminatorType = DiscriminatorType.STRING
)
public class Shape {}
#Entity
#DiscriminatorValue(Square.DISCRIMINATOR_VALUE)
public class Square extends Shape {
public static final String DISCRIMINATOR_VALUE = "SQUARE";
}
#Entity
#DiscriminatorValue(Circle.DISCRIMINATOR_VALUE)
public class Circle extends Shape {
public static final String DISCRIMINATOR_VALUE = "CIRCLE";
}
#AllArgsConstructor
public enum FieldType {
SHAPE(Shape.DISCRIMINATOR_VALUE),
CIRCLE(Circle.DISCRIMINATOR_VALUE);
#Getter
private final String discriminatorValue;
}
Apart from eliminating duplication, this code is also less tightly coupled: entity classes don't depend on enum values and can be added without having to change other code; while the enum can "enumerate" different classes from different sources - depending on the context where it is used.
To my knowledge, this is not possible with annotations:
discriminator value must be of type String
discriminator value must be a compile-time-constant, i.e. return values from methods on enums are not allowed.
yup ,when you define discriminator the annotation's option are name and discrimatorType
#DiscriminatorColumn (name="MYDISCRIMINATOR", discriminatorType= DiscriminatorType.INTEGER)
of which DiscriminatorType can only be:
DiscriminatorType.STRING
DiscriminatorType.CHAR
DiscriminatorType.INTEGER
unfortunate I didn't see this yesterday but well. That's the way it is
You can use DiscriminatorType.INTEGER, and map each subclass with #DiscriminatorValue("X"), where X must be the ordinal value of the enum (0,1,2,3...).
It must be the value as a constant String. You can't use YourEnum.SOME_VALUE.ordinal(), because annotation attribute values must be constants. Yes, it is tedious. Yes, it is error-prone. But it works.