I'm working with Hibernate Annotations and the issue that I'm trying to solve goes as follows:
I need to have 2 different #Entity classes with the same columns mapping but with a different Identifier.
The first one should use id as identifier.
The second should use name as identifier.
So, I have an abstract class, annotated with #MappedSuperclass that have all of the columns including id and name, and in addition 2 #Entity classes that extends the super class and overriding the getters of the id and name.
#MappedSuperclass
public class MappingBase {
protected Integer id;
protected String name;
#Column (name = "ID")
public void getId() {
return this.id;
}
#Column (name = "NAME")
public void getName() {
return this.name;
}
}
#Entity
#Table (name = "TABLE")
public class Entity1 extends MappingBase {
#Id
#Column (name = "ID")
public void getId() {
return this.id;
}
}
#Entity
#Table (name = "TABLE")
public class Entity2 extends MappingBase {
#Id
#Column (name = "NAME")
public void getName() {
return this.name;
}
}
Note: I must have the members (id,name) in the super class.
I know that i can add #Transient to the id and name getters but this means that i must add both of them in each class and it's not a good design :(
In addition, the following insertable="false, updateable=false can help but i don't understand what is the meaning of this...
Please help me!
Hibernate/JPA allows us to annotate either properties or accessors. If we have #Id annotation on a property, JPA will lookup all the properties of the class. Similarly, if we have #id annotation on a getter method, JPA will lookup all the getters.
We can solve the above problem by annotating properties instead. The superclass and the two subclasses will be as follows-
#MappedSuperclass
public abstract class AbstractMappingBase {
//properties other than id and name
public abstract Integer getId();
public abstract String getName();
//other getters and setters
}
#Entity
public class Entity1 extends AbstractMappingBase {
#Id
private Integer id;
private String name;
#Override
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Entity
public class Entity2 extends AbstractMappingBase {
private Integer id;
#Id
private String name;
#Override
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Here JPA will look for properties instead of getters. There are no duplicate properties between superclass and its subclasses. So it will work fine.
You are much better off defining your base class as #Embeddable and using #Embedded in your implementation classes with the use of #AttributeOverride.
If i remember correctly, I simply defined 2 #Entity classes with the same table that inherits from one abstract #MappedSuperclass class. The super class contains the id member and each Entity class define it's own #Id #Column definition. It should work!
Related
I use Spring Boot 2.6.1, Hibernate 5.6.1 and Postgresql 14.1 on docker. I have an entity called person that extends a base entity class.
#MappedSuperclass
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
protected long id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
#Entity
public class Person extends BaseEntity {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The problem is when Hibernate creates my schema on Postgresql it ignores the #GeneratedValue(strategy = GenerationType.AUTO) and creates a simple column with no auto increment or identity mechanism. So the query
INSERT INTO person(name) VALUES('Bob')
executes with error.
ERROR: null value in column "id" of relation "person" violates not-null constraint
I have 2 classes, an Abstract class annotated with #MappedSuperclass and a child class which extends the class. When i run my program, i get the following error:
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: models.Comment column: comment_id (should be mapped with insert="false" update="false")
But I'm looking through my code and I don't see any repeated column names. This is my first time using the Mapped supper class annotation and I'm not sure if I am doing something wrong/using the annotation. Here are the two classes I have:
#MappedSuperclass
public abstract class AbstractModel {
protected long id;
protected Date creationDate = new Date();
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#Temporal(TemporalType.TIMESTAMP)
public Date getCreationDate() {
return creationDate;
}
}
and
#Entity
#Table(name="comment")
#AttributeOverride(name = "id", column = #Column(name = "comment_id"))
public class Comment extends AbstractModel{
private User user;
private Post post;
private String content;
/*
* Getters and setters
*
*/
#Override
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public long getId(){
return id;
}
#ManyToOne
#JoinColumn(name="user_id")
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
#ManyToOne
#JoinColumn(name="post_id")
public Post getPost() {
return post;
}
public void setPost(Post post) {
this.post = post;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
Any help with this would be greatly appreciated
You defined 'id' property twice.
Move annotations from getId() method from Comment class to AbstractModel and remove getId() method from your Comment class.
When you use proerty access you need all setters and getters for your columns setCreationDate in AbstractModel class is missing.
In my application I am trying to implement Hibernate mappings through annotation. There I have a base class which is abstract, in this class the Id attribute is present. I am inheriting this base class with a child class. The code is given below:
#MappedSuperclass
#Inheritance(strategy=InheritanceType.JOINED)
public abstract class Base implements IBase {
private static final long serialVersionUID = -1433573674276444516L;
private int id;
public Base() {
}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="ID")
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
And the child class is:
#Entity
#Table(name="USER")
public class User extends Base implements IUser {
private static final long serialVersionUID = 344528694909088439L;
private String name;
public User() {
}
#Column(name="NAME", nullable=false)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
It is creating the USER Table and working fine.
I was wondering whether or not I am doing it in right way.
Thanks.
If the goal is just to have several independant entities to inherit a common field from a base class, then no, you're not doing it correctly. The annotation #Inheritance is unnecessary. #Inheritance is necessary when you have an entity (Vehicle, for example), and several sub-entities (Car, Bike, for example).
Is it possible to map a subclass to its superclass by OneToOne relationship base on their primary key properties in Hibernate? How can I implement this?
You can do it with the JOINED inheritance strategy like this:
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
public class Cat implements Serializable {
private int id;
#Id
#GeneratedValue
public int getId() {
return id;
}
public void setId(final int id) {
this.id = id;
}
}
#Entity
public class DomesticCat extends Cat {
private String name;
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
}
This way, the id will be both in the cat and the domesticcat table, both as a primary key, and with a foreign key between the two. This gives you a one to one relationship (without using #OneToOne).
You should look at Inheritance Mapping in the Hibernate reference to understand inheritance mapping.
I'm using Hibernate version 3.3.2.GA with annotations.
I have inheritance between two classes, the former:
#Entity
#Table(name = "SUPER_CLASS")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
name="DISCR_TYPE",
discriminatorType= DiscriminatorType.STRING
)
#org.hibernate.annotations.Entity(mutable = false)
public class SuperClass { }
The subclass is mapped with a secondary table:
#Entity
#DiscriminatorValue("VALUE")
#org.hibernate.annotations.Entity(mutable = false)
#SecondaryTable(name = "V_SECONDARY_TABLE",
pkJoinColumns = #PrimaryKeyJoinColumn(name = "ID", referencedColumnName = "ID"))
public class SubClass extends SuperClass {
#Embedded
public Field getField() {
return getField;
}
}
Where the field is composed of two different fields
#Embeddable
public class Field {
#Column("FIELD_1") String field1
#Column("FIELD_2") String field2
}
Now when I create a query on SubClass the FIELD_1 and FIELD_2 fields are searched on the SuperClass, even if they're defined in the subclass.
I can't set the table in the #Column annotation in the field, because the Field class it's reused somewhere. I need to specify it in SubClass class.
How do I specify that the field should be searched in the secondary table?
Also on Hibernate Forum
You should use table attribute
#Column("FIELD_1", table="V_SECONDARY_TABLE")
UPDATE
When a embeddable column is used by more than one entity, you should use #AttributeOverride if you need to re-map just a single column or #AttributeOverrides if more than one column
#Entity
#SecondaryTable(name="OTHER_PERSON")
#AttributeOverride(name="address.street", column=#Column(name="STREET", table="OTHER_PERSON"))
public class Person {
private Address address;
#Id
#GeneratedValue
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
#Embedded
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
#Embeddable
public static class Address implements Serializable {
private String address;
public String getStreet() { return street; }
public void setStreet(String street) { this.street = street; }
}
}