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.
Related
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 have created Owned One-to-Many Relationships between Department and Employee using JPA annotation.
Entities are:
Department(Parent)
String Id
String Name
List Employee(#OneToMany)
Employee(Child)
Key key
String firstName
String lastName
String Address
Department dept(#ManyToOne)
When Employee's firstName is entered I want to display that Employee detail(lastName and address) and also from which Department he Belongs. i did not find any solution to get the parent info from child Entity. Is there any way to get the information about parent entity from its child entity ?
I think one possible solution is to get the parent id from child and in another query get the Parent Detail. but i want all the information about employee in single query. is this possible?
here is my code.
Department Entity:
#Entity
public class Department {
#Id
private String id;
private String description;
#OneToMany(mappedBy = "department", fetch=FetchType.EAGER, cascade = CascadeType.ALL)
private List<Employee> employee = new ArrayList<Employee>();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<Employee> getEmployee () {
return employee ;
}
public void setEmployee (List<Employee > employee) {
this.employee = employee ;
}
}
Employee Entity:
#Entity
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
private String firstName;
private String lastName;
private String Address;
private String parent_Id;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Department department;
public Key getKey() {
return key;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getAddress() {
return Address;
}
public void setAddress(String Address) {
this.Address = Address;
}
}
Android Code to insert Entity in Background
Departmentendpoint.Builder builder1 = new Departmentendpoint.Builder(AndroidHttp.newCompatibleTransport(), new JacksonFactory(), null);
Departmentendpoint dptendpoint = CloudEndpointUtils.updateBuilder(builder1).build();
Department dpt = new Department();
dpt.setDescription("Water Service");
List<Employee> m = new ArrayList<Employee>();
Employee mgr2= new Employee();
mgr2.setFirstName("Avinash"+i);
mgr2.setLastName("Patel"+i);
mgr2.setAddress("Vadodara");
m.add(mgr2);
dpt.setEmployee(m);
try {
dptendpoint.insertDepartment(dpt).execute();
} catch (IOException e) {
e.printStackTrace();
}
You can get a parent id from a child entity's key, but you have to get the parent entity from the datastore.
You can store a department name in every employee entity, which will save you one get operation at the expense of extra data and more complex code (you have to account for situations like a change in a department name).
A better solution is to keep department entities in memcache. This way you save a trip to the datastore and keep your data model and code simple.
The basic idea with Entity relationship with GAE Datastore is:
A child entity must have a key with parent set to a given parent Enitty like:
Entity parent = createParentEntity();
Enttity child = new Entity(KeyFactory.createKey(parent.getKey, kind, key));
A parent entity must have the child Entity key as its property:
Entity parent = createParentEntity();
Entity child = createChildEnity();
parent.setProperty("fieldName", child.getKey());
This way you can retrieve the parent from the child Entity or you can fetch the child from the parent entity. Hope this helps.
I'm having a problem when I query an entity who has a OneToOne relationship with another one. This is the scenario:
Database tables:
create table people (
id decimal(10,0) NOT NULL,
email varchar(512) NOT NULL
);
create table users (
email varchar(512) NOT NULL
);
Test data:
insert into users (email) values ('jhon#domain.com');
insert into users (email) values ('mary#domain.com');
insert into people (id, email) values (1, 'jhon#domain.com');
insert into people (id, email) values (2, 'mary#domain.com');
Entities:
#Entity(name = "people")
public class Person implements Serializable {
#Column
#Id
private long id;
#Column
private String email;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
#Entity(name = "tbl_users")
public class User implements Serializable {
#Id
private String email;
#OneToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn(name = "email", referencedColumnName = "email")
private Person person;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Invocation:
...
User user = entityManager.find(User.class, "jhon#domain.com");
...
After de invocation, the hibernate's logs shows:
select user1_.email as email2_0_, person2_.id as id1_1_, person2_.email as email1_1_
from users user1_ left outer join people person2_ on user1_.email=person2_.id
where user1_.email=?
As you can see, the join is wrong because is comparing users.email with people.id (user1_.email=person2_.id), so it returns an User without its corresponding Person.
Any ideas about how can I fix it?
Thanks a lot !!
Strictly speaking, the JPA specification does not allow references to non-primary key columns. It may work in some JPA implementations, but it's not proper.
However, i think you can do this by making the relationship bidirectional, and owned by the side with the non-primary key:
#Entity
public class Person {
#Id
private long id;
#OneToOne
#JoinColumn(name = "email")
private User user;
public String getEmail() {
return user.getEmail();
}
public void setEmail(String email) {
// left as an exercise for the reader
}
}
#Entity
public class User {
#Id
private String email;
#OneToOne(mappedBy = "user")
private Person person;
}
I haven't actually tried that, though, so caveat hackor.
I think you should rethink your datamodel. The relationship between User and Person looks more like one of Inheritance.
For the issue with your mappings as they stand see here for some further disussion:
JPA providers: why do relationships/FKs to non-PK columns work in Hibernate and EclipseLink?
Does the JPA specification allow references to non-primary key columns?
Let's say we are living in a world a person could have only one vehicle(Forgive me for my lame example)
Let's say I have this UserDetails Class
public class UserDetails {
#Id
#GeneratedValue
#Column(name = "USER_ID")
private int id;
#Column(name = "USER_NAME")
private String name;
#OneToOne
private Vehicle vehicle;
public Vehicle getVehicle() {
return vehicle;
}
public void setVehicle(Vehicle vehicle) {
this.vehicle = vehicle;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
And this is My Vehicle class
#Entity
public class Vehicle {
#Id
#GeneratedValue
private long vechile_id;
private String vehicleName;
public long getVechile_id() {
return vechile_id;
}
public void setVechile_id(long vechile_id) {
this.vechile_id = vechile_id;
}
public String getVehicleName() {
return vehicleName;
}
public void setVehicleName(String vehicleName) {
this.vehicleName = vehicleName;
}
}
Upon Saving it to the database it works fine, but when I went to delete the Table for vehicle this error showed up on my workbench
NOTE That there are only one entries on both UserDetails and Vehicle Table.
ERROR 1217: Cannot delete or update a parent row: a foreign key constraint fails
SQL Statement:
drop table `hibernate`.`vehicle`
How come am I not allowed to drop the table? Should I delete The UserDetails table first?
If you'd delete the Vehicle table, that would make the UserDetails table loose it's referential integrity because the vehicle column's foreign keys would point to nowhere. Drop the fk constraint or the vehicle column from UserDetails then you can delete the table you want.
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.