Can I annotate a member inherited from a superclass? - java

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.

Related

Using POJO as a base for hibernate entity

I started developing in Java quite recently, and my client is also a developer who is developing in Java since it was released.
So when he says "we have a good reason why don't we use transient fields in our project", I didn't ask what those reasons are. But, back to the question:
I have two classes:
POJO, which is used solely to generate JSON:
public class BaseSector implements Serializable {
private String id;
private String name;
private String parentId;
Entity:
public class Sector {
#Column(length = 36)
private String id;
#Column(length = 40)
private String name;
#Column(length = 36)
private String parentId;
// ... Bunch of other fields
Is there any way for an Entity class to extend this POJO, and add Column annotations dynamically? Or have POJO as an interface? Or use entity class in POJO constructor?
Earlier we had something like this:
for (Sector sector : sectors) {
BaseSector baseSector = new BaseSector();
baseSector.setId(sector.getId());
baseSector.setName(sector.getName());
baseSector.setParentId(sector.getParentId());
}
But I changed that by using BaseSector in HQL constructor...
Btw, we also have SectorInfo and SimpleSectorInfo which also extend BaseSector, but that's a different subject..
A TRANSIENT field tells your ENTITY class that this particular field should not be persisted in the DB. #Transient annotation is used to ignore a field to not persist in database in JPA, where as transient key word used to ignore a field from serialization. The field annotated with #Transient still can be serialized, but the field declared with transient keyword not to be persisted and not to be serialized.
A POJO can be extended by an ENTITY and vice-versa. This is stated in JPA specification.You can find more examples at the below links :
Link:1 : JPA Non-Entity SuperClass
Link 2 : JPA Specification
You can achieve this by using an annotation : #javax.persistence.MappedSuperclass
It states : A superclass is treated as non-entity class if no mapping related annotations such as #Entity or #MappedSuperclass are used on the class level.
This means your superclass will be treated as a non-entity class here if you do not use the above annotations in your superclass.
How to Construct the classes :
SUPERCLASS which also a POJO for your JSON object
#MappedSuperclass
public class BaseSector implements Serializable {
private String id;
private String name;
private String parentId;
}
ENTITY class :
#Entity
#Table(name = "sector")
public class Sector extends BaseSector {
#Column(length = 36)
private String id;
#Column(length = 40)
private String name;
#Column(length = 36)
private String parentId;
// ... Bunch of other field
}
You can also override some property defined by BaseSector in your ENTITY - Sector
You need to use
#AttributeOverride // for single property
#AttributeOverrides // override more than one property

"Joined subclass strategy" - what is the "object identifier"?

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.

How can one override a method from a Hibernate #MappedSuperclass but keep the originial mapping?

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

What is difference between #Entity and #Embeddable

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.

Mapping Multiple Classes to a Table in Hibernate, Without a DTYPE Column

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

Categories