JPA Hibernate One-to-One relationship - java

I have a one-to-one relationship but hibernatetool complains when generating the schema. Here's an example that shows the problem:
#Entity
public class Person {
#Id
public int id;
#OneToOne
public OtherInfo otherInfo;
rest of attributes ...
}
Person has a one-to-one relationship with OtherInfo:
#Entity
public class OtherInfo {
#Id
#OneToOne(mappedBy="otherInfo")
public Person person;
rest of attributes ...
}
Person is owning side of OtherInfo. OtherInfo is the owned side so person uses mappedBy to specify the attribute name "otherInfo" in Person.
I get the following error when using hibernatetool to generate the database schema:
org.hibernate.MappingException: Could not determine type for: Person, at table: OtherInfo, for columns: [org.hibernate.mapping.Column(person)]
at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:292)
at org.hibernate.mapping.SimpleValue.createIdentifierGenerator(SimpleValue.java:175)
at org.hibernate.cfg.Configuration.iterateGenerators(Configuration.java:743)
at org.hibernate.cfg.Configuration.generateDropSchemaScript(Configuration.java:854)
at org.hibernate.tool.hbm2ddl.SchemaExport.<init>(SchemaExport.java:128)
...
Any idea why? Am I a doing something wrong or is this a Hibernate bug?

JPA doesn't allow the #Id annotation on a OneToOne or ManyToOne mapping. What you are trying to do is one-to-one entity association with shared primary key. The simplest case is unidirectional one-to-one with shared key:
#Entity
public class Person {
#Id
private int id;
#OneToOne
#PrimaryKeyJoinColumn
private OtherInfo otherInfo;
rest of attributes ...
}
The main problem with this is that JPA provides no support for shared primary key generation in OtherInfo entity. The classic book Java Persistence with Hibernate by Bauer and King gives the following solution to the problem using Hibernate extension:
#Entity
public class OtherInfo {
#Id #GeneratedValue(generator = "customForeignGenerator")
#org.hibernate.annotations.GenericGenerator(
name = "customForeignGenerator",
strategy = "foreign",
parameters = #Parameter(name = "property", value = "person")
)
private Long id;
#OneToOne(mappedBy="otherInfo")
#PrimaryKeyJoinColumn
public Person person;
rest of attributes ...
}
Also, see here.

This should be working too using JPA 2.0 #MapsId annotation instead of Hibernate's GenericGenerator:
#Entity
public class Person {
#Id
#GeneratedValue
public int id;
#OneToOne
#PrimaryKeyJoinColumn
public OtherInfo otherInfo;
rest of attributes ...
}
#Entity
public class OtherInfo {
#Id
public int id;
#MapsId
#OneToOne
#JoinColumn(name="id")
public Person person;
rest of attributes ...
}
More details on this in Hibernate 4.1 documentation under section 5.1.2.2.7.

You just need to add #JoinColumn(name="column_name") to Host Entity relation . column_name is the database column name in person table.
#Entity
public class Person {
#Id
public int id;
#OneToOne
#JoinColumn(name="other_info")
public OtherInfo otherInfo;
rest of attributes ...
}
Person has a one-to-one relationship with OtherInfo:
mappedBy="var_name" var_name is variable name for otherInfo in Person class.
#Entity
public class OtherInfo {
#Id
#OneToOne(mappedBy="otherInfo")
public Person person;
rest of attributes ...
}

I think you still need the primary key property in the OtherInfo class.
#Entity
public class OtherInfo {
#Id
public int id;
#OneToOne(mappedBy="otherInfo")
public Person person;
rest of attributes ...
}
Also, you may need to add the #PrimaryKeyJoinColumn annotation to the other side of the mapping. I know that Hibernate uses this by default. But then I haven't used JPA annotations, which seem to require you to specify how the association wokrs.

I have a better way of doing this:
#Entity
public class Person {
#OneToOne(cascade={javax.persistence.CascadeType.ALL})
#JoinColumn(name = "`Id_OtherInfo`")
public OtherInfo getOtherInfo() {
return otherInfo;
}
}
That's all

I'm not sure you can use a relationship as an Id/PrimaryKey in Hibernate.

Try this
#Entity
#Table(name="tblperson")
public class Person {
public int id;
public OtherInfo otherInfo;
#Id //Here Id is autogenerated
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#OneToOne(cascade = CascadeType.ALL,targetEntity=OtherInfo.class)
#JoinColumn(name="otherInfo_id") //there should be a column otherInfo_id in Person
public OtherInfo getOtherInfo() {
return otherInfo;
}
public void setOtherInfo(OtherInfo otherInfo) {
this.otherInfo= otherInfo;
}
rest of attributes ...
}
#Entity
#Table(name="tblotherInfo")
public class OtherInfo {
private int id;
private Person person;
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#OneToOne(mappedBy="OtherInfo",targetEntity=Person.class)
public College getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
rest of attributes ...
}

Related

HIbernate one to many / Many to one using #Embeddable Annotation

When I'm trying to save the Customer Object the exception states that it cannot insert NULL into ()...
I believe the exception is caused due to the #Embeddable Annotation used
But when I am explicit setting the value of customer_id it works fine but the value is not saved as foreign key in Customerphone table.
#Entity
#Table(name = "CUSTOMER")
public class Customer implements java.io.Serializable {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "Customer")
public Set<CustomerPhone> getCustomerPhones() {
return this.CustomerPhones;
}
}
#Entity
#Table(name="CUSTOMER_PHONE")
public class CustomerPhone implements java.io.Serializable {
private CustomerPhoneId id;
#EmbeddedId
#AttributeOverrides( {
#AttributeOverride(name="customerId", column=#Column(name="CUSTOMER_ID", nullable=false, precision=22, scale=0) ),
#AttributeOverride(name="phoneTypeCd", column=#Column(name="PHONE_TYPE_CD", nullable=false, length=5) ) } )
public CustomerPhoneId getId() {
return this.id;
}
public void setId(CustomerPhoneId id) {
this.id = id;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="CUSTOMER_ID", nullable=false, insertable=false, updatable=false)
public Customer getCustomer() {
return this.stpCustomer;
}
}
#Embeddable
public class CustomerPhoneId implements java.io.Serializable {
private BigDecimal customerId;
#Column(name="CUSTOMER_ID", nullable=false, precision=22, scale=0)
public BigDecimal getCustomerId() {
return this.customerId;
}
}
Your ID does not have a Generation schema, like #GeneratedValue(strategy=GenerationType.AUTO).
In these cases, you need to set an ID manually, in your case, you need to set customerId manually before persisting.

java.lang.IllegalArgumentException: expecting IdClass mapping

I have configured composite primary key for my entity Employee as follows
Employee.java:
#Entity
#Table(name="employee")
#Proxy(lazy=false)
#IdClass(EmployeeId.class)
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private EmployeeId employeeId;
private Person person;
private Branch branch;
private boolean isActive;
public Employee() {
}
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name="person", column = #Column(name="person_id")),
#AttributeOverride(name="branch", column = #Column(name="branch_id"))})
public EmployeeId getEmployeeId() {
return employeeId;
}
public void setEmployeeId(EmployeeId employeeId) {
this.employeeId = employeeId;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="person_id")
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="branch_id")
public Branch getBranch() {
return branch;
}
public void setBranch(Branch branch) {
this.branch = branch;
}
#Column(name="is_active")
public boolean getIsActive() {
return isActive;
}
public void setIsActive(boolean isActive) {
this.isActive = isActive;
}
}
EmployeeId.java:
#Embeddable
public class EmployeeId implements Serializable {
private static final long serialVersionUID = 1L;
private Person person;
private Branch branch;
public EmployeeId() {
}
public EmployeeId(Person argPerson, Branch argbranch) {
this.person = argPerson;
this.branch = argbranch;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="person_id", insertable=false, updatable=false)
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="branch_id", insertable=false, updatable=false)
public Branch getBranch() {
return branch;
}
public void setBranch(Branch branch) {
this.branch = branch;
}
}
I created a SessionFactory bean using class org.springframework.orm.hibernate5.LocalSessionFactoryBean and mapped all hbm.xml as a MappingLocations.
My code throws the following error:
Caused by: java.lang.IllegalArgumentException: expecting IdClass mapping
at org.hibernate.metamodel.internal.AttributeFactory$3.resolveMember(AttributeFactory.java:971)
at org.hibernate.metamodel.internal.AttributeFactory$5.resolveMember(AttributeFactory.java:1029)
at org.hibernate.metamodel.internal.AttributeFactory.determineAttributeMetadata(AttributeFactory.java:451)
at org.hibernate.metamodel.internal.AttributeFactory.buildIdAttribute(AttributeFactory.java:128)
at org.hibernate.metamodel.internal.MetadataContext.buildIdClassAttributes(MetadataContext.java:337)
at org.hibernate.metamodel.internal.MetadataContext.applyIdMetadata(MetadataContext.java:269)
at org.hibernate.metamodel.internal.MetadataContext.wrapUp(MetadataContext.java:190)
at org.hibernate.metamodel.internal.MetamodelImpl.initialize(MetamodelImpl.java:219)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:296)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:476)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:707)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:723)
at org.springframework.orm.hibernate5.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:504)
at org.springframework.orm.hibernate5.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:488)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFac
How can I avoid this error? I am using spring-orm-4.3.1-RELEASE and hibernate-core-5.2.0.Final.
Update
I have created a sample project and I am getting the following error while running...
Caused by: org.hibernate.AnnotationException: Property of #IdClass not found in entity sample.domain.Employee: employee
Refer the code: https://www.dropbox.com/s/axr8l01iqh0qr29/idclass-using-hibernate5.tar.gz?dl=0
What I did wrong? Kindly provide your inputs here
Your situation corresponds to the chapter 2.4.1 Primary Keys Corresponding to Derived Identities of the JPA 2.1 Specification.
The identity of Employee is derived from identities of Person and Branch. You haven't shown the code of either of them, so I'll assume they have simple primary keys. In that relationship, Person and Branch are "parent entities" and Employee is a "dependant" entity.
The ID of Employee may be mapped using either IdClass or EmbeddedId, not both at the same time.
See chapter 2.4.1.1 Specification of Derived Identities.
If you want to use IdClass, then:
The names of the attributes of the id class and the Id attributes of the dependent entity class must correspond as follows:
The Id attribute in the entity class and the corresponding attribute in the id class must have the same name.
...
If an Id attribute in the entity is a many-to-one or one-to-one relationship to a parent entity, the corresponding attribute in the id class must be of (...) the type of the Id attribute of the parent entity.
So your classes would look like this (getters, setters, superfluous annotations etc. omitted)
#Entity
#IdClass(EmployeeId.class)
public class Employee {
#Id
#ManyToOne
private Person person;
#Id
#ManyToOne
private Branch branch;
}
public class EmployeeId {
private Long person; // Corresponds to the type of Person ID, name matches the name of Employee.person
private Long branch; // Corresponds to the type of Branch ID, name matches the name of Employee.branch
}
If you use EmbeddedId, then:
If the dependent entity uses an embedded id to represent its primary key, the attribute in the embedded id corresponding to the relationship attribute must be of the same type as the primary key of the parent entity and must be designated by the MapsId annotation applied to the relationship attribute. The value element of the MapsId annotation must be used to specify the name of the attribute within the embedded id to which the relationship attribute corresponds.
And the code would look like this:
#Entity
public class Employee {
#EmbeddedId
private EmployeeId id;
#ManyToOne
#MapsId("personId") // Corresponds to the name of EmployeeId.personId
private Person person;
#ManyToOne
#MapsId("branchId") // Corresponds to the name of EmployeeId.branchId
private Branch branch;
}
#Embeddable
public class EmployeeId {
private Long personId; // Corresponds to the type of Person ID
private Long branchId; // Corresponds to the type of Branch ID
}
A composite key mapping can be either done with an IdClass or an Embeddable. If you want to use an IdClass you have to annotate your fields in Employee with #Id.
#IdClass(EmployeeId.class)
class Person{
#Id
private Person person;
#Id
private Branch branch;
}
If you want to use an Embedded as a composite key please remove the #IdClass(EmployeeId.class) annotation from Person. You also don't need the person and branch field in your Person class because those are defined in your Embedded class.
Change to:
#Entity
#Table(name = "employee")
#Proxy(lazy = false)
#IdClass(EmployeeId.class)
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private EmployeeId id;
private Person person;
private Branch branch;
private boolean isActive;
public Employee() {
}
#EmbeddedId
#AttributeOverrides({#AttributeOverride(name = "person", column = #Column(name = "person_id") ),
#AttributeOverride(name = "branch", column = #Column(name = "branch_id") )})
public EmployeeId getId() {
return id;
}
public void setId(EmployeeId id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "person_id")
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "branch_id")
public Branch getBranch() {
return branch;
}
public void setBranch(Branch branch) {
this.branch = branch;
}
#Column(name = "is_active")
public boolean getIsActive() {
return isActive;
}
public void setIsActive(boolean isActive) {
this.isActive = isActive;
}
}
The IdClass shouldnt be defined as Embeddable -
#Entity
#Table(name="employee")
#IdClass(EmployeeId.class)
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#ManyToOne
private Person person;
#Id
#ManyToOne
private Branch branch;
private boolean isActive;
public Employee() { }
//....
}
And -
public class EmployeeId implements Serializable {
private static final long serialVersionUID = 1L;
private Person person;
private Branch branch;
public EmployeeId() {}
public EmployeeId(Person argPerson, Branch argbranch) {
this.person = argPerson;
this.branch = argbranch;
}
}
Read your comment - Can I make a suggestion that you map Employee to person_id and branch_id, and not the JPA objects Person and Branch? This will let us test if your hbm config is correct. Id also suggest posting your hbm config as I think there is information missing from this problem
So the table will be similar to -
#Entity
#Table(name="employee")
#IdClass(EmployeeId.class)
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long personId;
#Id
private Long branchId;
private boolean isActive;
public Employee() { }
//....
}
And -
And -
public class EmployeeId implements Serializable {
private static final long serialVersionUID = 1L;
private Long personId;
private Long branchId;
public EmployeeId() {}
public EmployeeId(Person argPerson, Branch argbranch) {
this.person = argPerson;
this.branch = argbranch;
}
}
This link could help you
JPA - EmbeddedId with #ManytoOne
Relationship mappings defined within an embedded id class are not supported.Then you need to change the embeddedId class like this
#Embeddable
public class EmployeeId implements Serializable {
private static final long serialVersionUID = 1L;
private Long personId;
private Long branchId;
public EmployeeId() {
}
public EmployeeId(Long argPerson, Long argbranch) {
this.personId = argPerson;
this.branchId = argbranch;
}
#Column(name = "person_id")
public Long getPersonId() {
return personId;
}
public void setPersonId(Long personId) {
this.personId = personId;
}
#Column(name = "branch_id")
public Long getBranchId() {
return branchId;
}
public void setBranchId(Long branchId) {
this.branchId = branchId;
}
}
JPA Composite Primary Key
Specifies a composite primary key class that is mapped to multiple fields or properties of the entity.
The names of the fields or properties in the primary key class and the
primary key fields or properties of the entity must correspond and
their types must be the same.
The answer is in here. read description for you. enter link description here
(Sample code)
#Entity
#Table(name = "EMP_PROJECT")
#IdClass(ProjectAssignmentId.class)
public class ProjectAssignment {
#Id
#Column(name = "EMP_ID", insertable = false, updatable = false)
private int empId;
#Id
#Column(name = "PROJECT_ID", insertable = false, updatable = false)
private int projectId;
#ManyToOne
#JoinColumn(name = "EMP_ID")
Professor employee;
#ManyToOne
#JoinColumn(name = "PROJECT_ID")
Project project;
....
}
public class ProjectAssignmentId implements Serializable {
private int empId;
private int projectId;
...
}
Mention #IdClass annotation with the class which holds the ID.
Check the answer at this post

Java hibernate: #OneToMany association doesn't work

In my code i have a oneToMany relation between customer class and item class. This means that, a customer may have one or many items.
Here is the customer code:
#Entity
#Data
public class customer {
#Id
#GeneratedValue
int id;
String name;
String lastname;
#Embedded
Address address;
#OneToMany
#Column(name="ITEM_ID")
List<item> item;
}
and it's the item class:
#Entity
#Data
public class item {
#Id
#GeneratedValue
int id;
String name;
String Serialnumber;
int price;
#ManyToOne
customer customer;
}
Then i have made some tests to try my queries in the models.
insert into item(id,name,Serialnumber,price) values(1,'bike','123',200);
insert into item(id,name,Serialnumber,price) values(2,'car','123',200);
insert into customer(id,name,lastname,Country,City,Street,No,item_id)
values(1,'Salman','Lashkarara','Iran','Tehran','Shariati','12',1);
insert into customer(id,name,lastname,Country,City,Street,No,item_id)
values(2,'Saba','Lashkarara','Iran','Tehran','Shariati','12',2);
insert into customer(id,name,lastname,Country,City,Street,No,item_id)
values(3,'Saba','Lashkarara','Iran','Tehran','Shariati','12',1);
But when i run my code, i face with the following error:
Column "ITEM_ID" not found; SQL statement:
insert into customer(id,name,lastname,Country,City,Street,No,item_id) values(1,'Salman','Lashkarara','Iran','Tehran','Shariati','12',1)
Please pay especial attention, that it is a java mvc-spring application and i create my models using the code, so there is no database to check the field item_id.
As you can see i have already added the #Column(name="ITEM_ID") to define the column.
You have to use #JoinColumn for association columns:
#OneToMany
#JoinColumn(name="ITEM_ID")
List<item> item;
some other options
#OneToMany(cascade=CascadeType.All, fetch=FetchType.EAGER)
#JoinColumn(name="ITEM_ID")
List<item> item;
in Item class
#ManyToOne(mappedBy="item")
customer customer;
you could do this i also have user class and bcr class, one user have many bcr so below code will help you
bcr.java
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_who_enter_demand", nullable = false)
public User getUserByUserWhoEnterDemand() {
return this.userByUserWhoEnterDemand;
}
public void setUserByUserWhoEnterDemand(User userByUserWhoEnterDemand) {
this.userByUserWhoEnterDemand = userByUserWhoEnterDemand;
}
user.java
#OneToMany(fetch = FetchType.LAZY, mappedBy = "userByUserWhoEnterDemand")
public Set<BudgetControlRegister> getBudgetControlRegistersForUserWhoEnterDemand() {
return this.budgetControlRegistersForUserWhoEnterDemand;
}
public void setBudgetControlRegistersForUserWhoEnterDemand(Set<BudgetControlRegister> budgetControlRegistersForUserWhoEnterDemand) {
this.budgetControlRegistersForUserWhoEnterDemand = budgetControlRegistersForUserWhoEnterDemand;
}
You can't map a table column without table:
#Entity
#Table(name = "ITEM_TABLE")
public class item {
...
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "CUSTOMER_ID_ITEM_TABLE")
private customer customer;
...
}
#Entity
#Table(name = "CUSTOMER_TABLE")
public class customer {
...
#OneToMany
#Column(name="ITEM_ID")
List<item> item;
}

Unknown Entity Error With ManyToOne Relationship

OK, so I've designed a basic CRUD an an exercise. It has 2 tables Jobs and Employees. I'm trying to create a many to one relationship, but when I click the link to go to the Employee Entry page it throws an error that kicks off with the #ManyToOne referencing an Unknown Entity.
Here is what I've got in my Employees.java
String jobName;
#ManyToOne(fetch=FetchType.EAGER)
#Fetch(value = FetchMode.JOIN)
#JoinColumn(name = "Job_Name")
#Column (name='jobName')
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
Any idea what i"m doing wrong and how to resolve this?
As per your comment,i think you can define relationship between these two entities like below.
#Entity
#Table(name="employee")
class Employee{
#Id
#GeneratedValue
private Integer id;
#ManyToOne
#JoinColumn(name = "job_name")
private Job job;
// other column and getter and setter
}
#Entity
#Table(name="job")
class Job{
#Id
#GeneratedValue
private Integer id;
#Column(name="job_name")
private String jobName;
//provide other column and getter setter
}

Mock JOINED inheritance strategy without actual inheritance

I want to use mixed #Inheritance strategy, but Hibernate doesn't support it.
Is there any way to implement JOINED inheritance without actual class inheritance.
For example:
#Entity
#Table(name="A")
#Inheritance(strategy=InheritanceType.JOINED)
public class A {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ID_SEQ")
private Long id;
//getters
//setters
}
#Entity
#Table(name="B")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class B {
#Id
private Long id;
//getters
//setters
}
So, basically in B I just want to refer to #Id generated in A without extending from A.
I found the solution. JPA doesn't allow you to combine #Id and #OneToOne. However, #MapsId annotation does the trick:
#Entity
public class A {
#Id
private Long id;
//getters
//setters
}
#Entity
public class B {
#Id
private Long id;
#MapsId
#OneToOne(optional=false, fetch=FetchType.EAGER)
#JoinColumn(nullable=false, name="id")
private A a;
//getters
//setters
}
I think you can accomplish this by making a #OneToOne relationship or a #OneToMany and point the table name like this
#Id #OneToOne
#JoinColumn(name = "id")
private A a;

Categories