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
Related
It seems Spring JPA recommends adding annotations on fields, e.g.
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
while Hibernate recommends using it on methods, e.g.
#Id
public Long getId() { return id; }
It seems the way Hibernate recommends doesn't work as it claims. Here is the code from that doc with completion I add.
#Entity
public class Body {
private Long id;
private Heart heart;
#Id
public Long getId() { return id; }
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
public Heart getHeart() {
return heart;
}
public Body() {
super();
}
public void setId(Long id) {
this.id = id;
}
public void setHeart(Heart heart) {
this.heart = heart;
}
}
#Entity
public class Heart {
private Long id;
#Id
public Long getId() { return id;}
public Heart() {
super();
}
public void setId(Long id) {
this.id = id;
}
}
the doc claims
The #PrimaryKeyJoinColumn annotation does say that the primary key of the entity is used as the foreign key value to the associated entity.
although the code doesn't create foreign keys.
In contrast, the way Spring JPA recommends works well.
#Entity
public class Body {
#Id
private Long id;
#OneToOne
private Heart heart;
public Body() {
super();
}
}
#Entity
public class Heart {
#Id
private Long id;
public Heart() {
super();
}
}
Why is that? Am I missing or misunderstanding something?
Here is the part of the doc I linked at the beginning.
I am trying to implement soft delete in my spring - hibernate project.
My plan is to override the delete method with the #SQLDelete annotation and filter the logical deleted entities with hibernate #Where annotation in my queries.
I experience some difficulties when I try to define the #Where clause in the super class, It's seems that the entities dont inherit the #Where clause from the abstract base class.
Note: If I move the #Where annotation to the entity class everything work as expected
The base entity class:
#MappedSuperclass
#Where(clause = " IS_DELETED = false")
public abstract class BaseEntity {
#Column(name = "IS_DELETED")
private boolean isDeleted;
public BaseEntity() {
}
public boolean getIsDeleted() {
return this.isDeleted;
}
public void setIsDeleted(boolean isDeleted) {
this.isDeleted = isDeleted;
}
}
The entity class:
#Entity
#Table(name = "Events")
#SQLDelete(sql ="UPDATE events " +
"SET IS_DELETED = true " +
"WHERE id = ?")
public class Event extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
public Event() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Thanks for any kind of help :)
Did you try this?
#Where(clause = "IS_DELETED = 0")
or
#Where(clause = "isDeleted = 0")
?
I need help on hibernate mapping for a bean property refers to multiple classes.
In my application we are implementing permissions. These permission are not specific to certain user it may based on groups(contains list of users) and roles. So, Permissions will apply to users, roles and groups.
Following are ddl and entity classes. Please review and help me.
DDL:
--stores the application users
CREATE TABLE users (
id serial PRIMARY KEY,
name text,
CONSTRAINT uk_users_name UNIQUE (name)
);
--stores the application groups
CREATE TABLE groups (
id serial PRIMARY KEY,
name text,
CONSTRAINT uk_groups_name UNIQUE (name)
);
--stores the application roles
CREATE TABLE roles (
id serial PRIMARY KEY,
name text,
CONSTRAINT uk_roles_name UNIQUE (name)
);
--stores the application object types
CREATE TABLE app_object_types (
id serial PRIMARY KEY,
name text,
CONSTRAINT uk_app_object_types_name UNIQUE (name)
);
INSERT INTO app_object_types (name) VALUES ('USERS');
INSERT INTO app_object_types (name) VALUES ('GROUPS');
INSERT INTO app_object_types (name) VALUES ('ROLES');
CREATE TABLE app_permissions (
id serial PRIMARY KEY,
object_type_id integer REFERENCES app_object_types(id), -- To represent the object type
object_id integer, -- Objecct_id refers users -> id, groups -> id, roles - id
permission_name text,
CONSTRAINT uk_permissions UNIQUE (object_type_id, object_id, permission_name)
);
Entity Classes:
#Entity
#Table(name = "users")
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getName() {
return name;
}
public void setName(int name) {
this.name = name;
}
}
#Entity
#Table(name = "groups")
public class Groups {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getName() {
return name;
}
public void setName(int name) {
this.name = name;
}
}
#Entity
#Table(name = "roles")
public class Roles {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getName() {
return name;
}
public void setName(int name) {
this.name = name;
}
}
#Entity
#Table(name = "app_object_types")
public class AppObjectTypes {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getName() {
return name;
}
public void setName(int name) {
this.name = name;
}
}
#Entity
#Table(name = "app_permissions")
public class AppPermissions {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne
private String permissionName;
#ManyToOne
private AppObjectTypes appObjectTypes;
private int objectId;
private Class<?> dependentObject;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPermissionName() {
return permissionName;
}
public void setPermissionName(String permissionName) {
this.permissionName = permissionName;
}
public AppObjectTypes getAppObjectTypes() {
return appObjectTypes;
}
public void setAppObjectTypes(AppObjectTypes appObjectTypes) {
this.appObjectTypes = appObjectTypes;
}
public int getObjectId() {
return objectId;
}
public void setObjectId(int objectId) {
this.objectId = objectId;
}
public Class<?> getDependentObject() {
return dependentObject;
}
public void setDependentObject(Class<?> dependentObject) {
this.dependentObject = dependentObject;
}
}
I want to map user (or) group (or) role bean object to AppPermissions -> dependentObject using hibernate. I don't know it is possible or not please help me.
I would suggest you consider the use of #Inheritance here on your AppPermission entity in order to specialize each subclass based on the dependent object types.
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "OBJECT_TYPE")
public class AppPermission {
#Id
#GeneratedValue
private Long permissionId;
private String name;
#Column(name = "OBJECT_TYPE", insertable = false, updatable = false)
private String objectType;
}
#Entity
#DiscriminatorValue("USER")
public class UserAppPermission extends AppPermission {
#ManyToOne(optional = false)
private User user;
}
#Entity
#DiscriminatorValue("ROLE")
public class RoleAppPermission extends AppPermission {
#ManyToOne(optional = false)
private Role role;
}
#Entity
#DiscriminatorValue("GROUP")
public class GroupAppPermission extends AppPermission {
#ManyToOne(optional = false)
private Group group;
}
The first difference here with these mappings from yours is that your AppPermission table will be constructed differently from your current schema and would look like the following (note 4 tables):
Table: AppPermission
id NOT NULL IDENTITY(1,1)
name VARCHAR(255)
OBJECT_TYPE VARCHAR(31)
Table: UserAppPermission
id NOT NULL BIGINT (FK -> AppPermission)
user_id NOT NULL BIGINT (FK -> User)
Table: RoleAppPermission
id NOT NULL BIGINT (FK -> AppPermission)
role_id NOT NULL BIGINT (FK -> Role)
Table: GroupAppPermission
id NOT NULL BIGINT (FK -> AppPermission)
group_id NOT NULL BIGINT (FK -> Group)
The whole point of a database is to help us maintain referential integrity. That's why when a table depends on a row from another table, the dependent table rows that relate to the row you wish to remove should be removed first to avoid constraint violations. This is precisely why I have split the relations into separate tables and here I've defined each relation as "optional=false" so that basically it represents a join-table.
Another additional benefit is that if your AppPermission has attributes you need to store specific to the type of dependent object, you can freely add those attributes to the subclass and those attributes are stored separately in that specific subclass's table.
This setup also eliminates your AppObjectType table because that is now driven as part of Hibernate's discriminator pattern. Be aware that if you have other "object-types" you'll need to introduce their specific implementations too with this setup.
Lastly, I exposed (which you don't have to) the OBJECT_TYPE as an non-insertable and non-updatable field because Hibernate manages that for you. But I've exposed it allowing you to make polymorphic queries and determine the object type of the resulting object without having to perform instanceof checks if you wish.
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!
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.