In JPA's CollectionTable example
#Entity
public class Person {
#Id protected long id;
#ElementCollection
#Column(name="name")
protected Set<String> nickNames = new HashSet();
}
It creates a join table Person_nickNames with such values :
It is hard for edit/delete values . We cannot use tools such as phpmyadmin to click the row and edit the value because there is no PK.
Is it possible for JPA to generate a surrogate primary key in the join table ?
environments : JPA 2 with Hibernate 4.3 implementation
Hi Smallufo
Write #GeneratedValue(strategy = GenerationType.AUTO) after #Id
This will make a surrogate primary key.
I think that should be possible. Added an ID column as IDENTITY in the PERSON_NICKNAMES table and with the below Person mapping class I am able to generate unique IDs in Person_nicknames table.
#Entity
#Table(name="Person")
public class Person {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
#ElementCollection
#CollectionTable(name="Person_Nicknames", joinColumns=#JoinColumn(name="person_id"))
#Column(name="name")
private Set<String> nickNames;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<String> getNickNames() {
return nickNames;
}
public void setNickNames(Set<String> nickNames) {
this.nickNames = nickNames;
}
}
When I save the person and nickname records I see following in person_nicknames table.
> ij> select * from Person_nicknames;
ID |PERSON_ID |NAME
1 |6 |nName0
2 |6 |nName1
3 |7 |nName0
4 |7 |nName1
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 two entities named Users and Dependents. I want to establish a OneToOne relationship between these two entities. As the real meaning of OneToOne states that -
Every user in the Users entity should have one and only one dependent.
And every dependent in the Dependents entity should only be related to
one and only one user.
But when I add #OneToOne to Dependents entity it does not stop me from adding two dependents to the same user. What is the real use of #OneToOne
or any other relationship annotations like #ManyToMany, #OneToMany, #ManyToOne?
Users.java
#Entity
#Table
public class Users {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String teamName;
private Integer salary;
public Users() {
}
public Users(Integer id, String name, String teamName, Integer salary) {
this.id = id;
this.name = name;
this.teamName = teamName;
this.salary = salary;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTeamName() {
return teamName;
}
public void setTeamName(String teamName) {
this.teamName = teamName;
}
public Integer getSalary() {
return salary;
}
public void setSalary(Integer salary) {
this.salary = salary;
}
}
Dependents.java
#Entity
#Table
public class Dependents {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private String relationship;
#OneToOne
private Users user;
public Dependents() {
}
public Dependents(int id, String name, String relationship) {
this.id = id;
this.name = name;
this.relationship = relationship;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRelationship() {
return relationship;
}
public void setRelationship(String relationship) {
this.relationship = relationship;
}
public Users getUser() {
return user;
}
public void setUser(Users user) {
this.user = user;
}
}
And in my DependentsService.java I am saving the Dependents object like-
public Dependents addNewDependent(Integer userId, Dependents dependent) {
dependent.setUser(usersRepository.getOne(userId));
return dependentsRepository.save(dependent);
}
Here I am fetching the user from the Users entity with the passed userId and storing it in Dependents object. When I pass the same userId for two or more dependents it will fetch the same user from Users entity and store it in Dependents entity. This violated OneToOne relationship. Can someone please explain to me, how can I achieve true OneToOne relationship? And also please explain what is the true purpose of relationship annotations like - #OneToOne, #OneToMany, #ManyToOne and #ManyToMany?
From Hibernate documentation:
From a relational database point of view, the underlying schema is identical to the unidirectional #ManyToOne association, as the client-side controls the relationship based on the foreign key column.
...
A much more natural mapping would be if the Phone were the parent-side, therefore pushing the foreign key into the PhoneDetails table. This mapping requires a bidirectional #OneToOne association
...
When using a bidirectional #OneToOne association, Hibernate enforces the unique constraint upon fetching the child-side. If there are more than one children associated with the same parent, Hibernate will throw a org.hibernate.exception.ConstraintViolationException.
So you should use a bidirectional one-to-one association.
Additional info: The best way to map a #OneToOne relationship with JPA and Hibernate
Hibernate won't do any extra checks to make sure, the record already exists. It is your responsibility to write the code which satisfies the OneToOne relation (it depends on your UI screens as well). If you still want to throw some exception, make your primary key as Foreign key in dependent table. Then you get DB exception.
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.
This is my sql table structure,
create table TBL_DEPARTMENT_ONE(
ID integer primary key generated always as identity (start with 50, increment by 1),
name varchar(100)
)
create table TBL_EMPLOYEE_THREE(
ID integer primary key generated always as identity (start with 100, increment by 1),
name varchar(100),
dept_ID integer references TBL_DEPARTMENT_ONE
)
Here we i'v done a structure of one to many relation between Employee and Department where many employees can belong to one Department,
Now, here is the JPA mapping code as follows,
For Employee,
#Entity
#Table(name="TBL_EMPLOYEE_THREE")
public class EmployeeEntityThree implements Serializable{
public EmployeeEntityThree(){}
public EmployeeEntityThree(String name,String mobileNo,DepartmentEntityOne dept){
this.empName = name;
this.department = dept;
this.mobileNo = mobileNo;
}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="ID")
private Integer employeeId;
#Column(name="MOBILE_NO")
private String mobileNo;
#ManyToOne(cascade={CascadeType.PERSIST,
CascadeType.MERGE},
fetch= FetchType.LAZY,targetEntity=DepartmentEntityOne.class)
#JoinColumn(name="DEPT_ID")
private DepartmentEntityOne department;
.....
...
}
the code below is of Department Entity,
#Entity
#Table(name="TBL_DEPARTMENT_ONE")
public class DepartmentEntityOne implements Serializable{
public DepartmentEntityOne(){ }
public DepartmentEntityOne(String name){
this.deptName = name;
}
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="ID")
private Integer deptId;
#Column(name="NAME")
private String deptName;
#OneToMany(cascade= { CascadeType.MERGE,
CascadeType.PERSIST},
fetch= FetchType.LAZY,mappedBy="department")
#MapKeyColumn(name="xxxxx")
private Map<String,EmployeeEntityThree> employees;
...
..
}
This is the code in my main method for testing,
DepartmentEntityOne deptOne = new DepartmentEntityOne("Mechanical Engineering");
Map<String,EmployeeEntityThree> empMap = new HashMap<String,EmployeeEntityThree>();
EmployeeEntityThree[] array = new EmployeeEntityThree[]{
new EmployeeEntityThree("Amar","9000000001",deptOne),
new EmployeeEntityThree("Akbar","9000000002",deptOne),
new EmployeeEntityThree("Anthony","9000000003",deptOne)
};
empMap.put(array[0].getMobileNo(),array[0]);
empMap.put(array[1].getMobileNo(),array[1]);
empMap.put(array[2].getMobileNo(),array[2]);
deptOne.setEmployees(empMap);
em = emf.createEntityManager();
em.persist(deptOne);
The code works fine with all the inserts done successfully
Now my Question is for the Entity Department
where is have used an #MapKeyColumn(name="xxxx"), where "xxxx" is some garbage value,
Here what should be the name = ?
because prior to this it was name = "mobileNo" which is the property in Employee entity.
This worked too.
So what shoud be the actualy vaue for #MapKetColumn(name= ?)
You should refer to the name column of the Employee table.
#MapKeyColumn(name="NAME")
private Map<String,EmployeeEntityThree> employees;
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.