Hibernate: using shared primary key in HQL - java

I'm have a problem with sql created by Hibernate when I use entity mapped shared primary key. I'm using JPA 2.1 and Hibernate 5.2.2
Here's my entities:
#Entity
#Column(name = "employee_table")
public class EmployeeEntity {
#Id
#Column(name = "id")
#SequenceGenerator
#GeneratedValue
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
private EmployeeDetailsEntity employeeDetailsEntity;
}
#Entity
#Table(name = "employee_details")
public class EmployeeDetailsEntity {
#Id
#Column(name = "id")
private Long id;
// additional attributes
}
I want to select all employees which has details:
select e from EmployeeEntity e where e.employeeDetailsEntity is not null;
Select that was generated by Hibernate is:
select employeeen0_.id from employee_table employeeen0_ where employeeen0_.id is not null;
Could you please explaine me what I'm doing wrong and help to solve this?

It looks like you're missing the "other side" of the Employee -> EmployeeDetails relationship mapping:
Employee entity:
private EmployeeDetails employeeDetails;
#OneToOne(mappedBy = "employee", fetch = FetchType.LAZY)
public EmployeeDetails getEmployeeDetails() {
return employeeDetails;
}
EmployeeDetails entity:
private Employee employee;
#OneToOne(optional = false)
#JoinColumn(name = "EMPLOYEE_ID")
public Employee getEmployee() {
return employee;
}

Thanks to all who comments.
The problem is that #PrimaryKeyJoinColumn on EmployeeDetailsEntity using EmployeeEntity primary key and when in your hql you check that EmployeeDetailsEntity in null (or not null) for native sql Hibernate use id of EmployeeEntity because you are shared this PK with another (EmployeeDetailsEntity) entity.
If you don't want (can't) to change you mapping use join in your hql queries or change #OneToOne mapping.

Related

Spring data jpa operation between table views

in my spring data application i have two TABLE VIEW mapped:
the first view
#Entity
#Immutable
#Table(name="VD_CONT")
#NamedQuery(name="VdContr.findAll", query="SELECT d FROM VdContr d")
public class VdContr {
#Id
#Column(name="CONTR_ID")
private Long id;
#Column(name="CF")
private String cf;
#OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="vdcontr")
private List<VdArr> vdArr;
}
and the second view
#Entity
#Immutable
#Table(name="VD_ARR")
#NamedQuery(name="VdArr.findAll", query="SELECT v FROM VdArr v")
public class VdArr {
#Id
#Column(name="ARR_ID")
private Long id;
#Column(name="FK_CONTR_ID")
private Long fkContrId;
#ManyToOne(fetch=FetchType.LAZY)
public VdContr vdcontr;
}
If i put a relationship "OneToMany" and "ManyToOne" (1, first view : many, second view), i receive errors.
My question is: is it possibile create a relationship between two table view?
you need to add a #JoinColumn to VdContr.
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "vdcontr_id", nullable = false)
In general, views are mapped in the same way as tables.
By looking at your classes, the problem is that Hibernate cannot find the correct join column. You need to specify it.
Also, in your VdArr you should delete the fkContrId, because hibernate will need to use this column to map the VdContr relationship.
By looking at your code, the join column is FK_CONTR_ID, so you need to specify it by using #JoinColumn.
#Entity
#Immutable
#Table(name = "VD_ARR")
#NamedQuery(name = "VdArr.findAll", query = "SELECT v FROM VdArr v")
public class VdArr {
#Id
#Column(name = "ARR_ID")
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FK_CONTR_ID")
public VdContr vdcontr;
}

Spring Data. Repository returns wrong Id (entity data is correct)

Good day, here is problem:
CrudRepository returns wrong Id for entities.
Here is base JPA user entity:
#Data
#Entity(name = "user")
#EqualsAndHashCode(exclude = {"roles", "password", "data"})
#ToString(exclude = {"roles", "password", "data"})
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(mappedBy = "user", fetch = FetchType.LAZY)
private SomeData data;
...
There is a relation one-to-one to some data entity.
#Data
#Entity(name = "some_data")
#TypeDefs({
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
#ToString(exclude = {"user", "views", "biData"})
#EqualsAndHashCode(exclude = {"user", "views", "biData"})
public class SomeData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne
#MapsId
private User user;
...
And there is crud repository:
#Repository
public interface SomeDataRepository extends CrudRepository<SomeData, Long> {
Optional<SomeData> findByUserId(Long userId);
}
Method findUserById returns correct SomeData entity from DB, but this entity has the same ID with userId...
And because of it I can't do other activities (insert or update on table "public_view" violates foreign key constraint "fk_view_to_some_data")
It's quite strange.
The problem could be because you use the #MapsId annotation. Here's what the value of the annotation does, as per Javadoc:
The name of the attribute within the composite key to which the relationship attribute corresponds. If not supplied, the relationship maps the entity's primary key.
You could try to set a specific value to your annotation, or map differently your one-to-one relationship. For example, use the #JoinColumn annotation in your SomeData class:
// ... your annotations ...
public class SomeData {
// ... your other fields ...
#OneToOne
#JoinColumn(name = "some_data_id", referencedColumnName = "id")
private User user;
}
Here are some alternatives that you could use: https://www.baeldung.com/jpa-one-to-one

Eager fetch performs left join in hibernate but fires seperate sql queries in springboot/JPA

I see a lot of posts where Eager fetch performs left join of child table parent table in hibernate. But when I use springboot , hibernate fires seperate sql queries - means one select query for parent table and one select query for child table. Why is there a difference? Has there been any upgrades in springboot or is it something I am doing wrong ?
Below are the entities I am using:
Order Entity:
#Entity
#Table(name="Ordertable", schema="cf_2583f365_c3c6_499a_a60d_138e7e7023eb")
public class Order {
#Id
#Column(name = "ORDER_ID")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int orderId;
#Column(name = "DAT_SRC_ID")
private String dataSourceId;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "ORDER_CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID")
private Customer customer;
}
Customer Entity:
#Entity
#Table(name="Customer", schema="cf_2583f365_c3c6_499a_a60d_138e7e7023eb")
public class Customer {
#Id
#Column(name = "CUSTOMER_ID")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long customerId;
#Column(name = "CUSTOMER_NAME")
private String customer_name;
#Column(name = "CUSTOMER_address_id")
private int customer_address_id;
#Column(name = "DAT_SRC_ID")
private String dataSourceId;
#OneToMany(fetch=FetchType.EAGER)
#JoinColumn(name = "ORDER_CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID")
private List<Order> order;
}
Controller:
#RequestMapping(value="/getByCustid/{id}", method=RequestMethod.GET,produces=MediaType.APPLICATION_JSON_VALUE)
public Customer getByCustid (#PathVariable Long id) {
Customer s1 = customerRepository.findByCustomerId(id);
return s1;
}
Repository:
public interface CustomerRepository extends JpaRepository<Customer,Long> {
public Customer findByCustomerId(Long customerId);
}
Below are the queries that are getting executed:
select
customer0_.CUSTOMER_ID as CUSTOMER1_0_,
customer0_.CUSTOMER_address_id as CUSTOMER2_0_,
customer0_.CUSTOMER_NAME as CUSTOMER3_0_,
customer0_.DAT_SRC_ID as DAT_SRC_4_0_
from
Customer customer0_
where
customer0_.CUSTOMER_ID=?
select
order0_.ORDER_CUSTOMER_ID as ORDER_CU3_5_0_,
order0_.ORDER_ID as ORDER_ID1_5_0_,
order0_.ORDER_ID as ORDER_ID1_5_1_,
order0_.ORDER_CUSTOMER_ID as ORDER_CU3_5_1_,
order0_.DAT_SRC_ID as DAT_SRC_2_5_1_
from
Ordertable order0_
where
order0_.ORDER_CUSTOMER_ID=?
EDIT : is this related to #Fetch(FetchMode.JOIN) -- Link:JPA eager fetch does not join
Hibernate: Multiple select queries made by Hibernate for Fetch mode Eager
To check whether FetchMode.JOIN works, I have added FetchMode.JOIN in entities as shown below but still no success with Join Query:
Customer Entity:
#Entity
#Table(name="Customer", schema="cf_2583f365_c3c6_499a_a60d_138e7e7023eb")
public class Customer {
#OneToMany(fetch=FetchType.EAGER)
#JoinColumn(name = "ORDER_CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID")
#Fetch(FetchMode.JOIN)
private List<Order> order;
}
Order Entity:
#Entity
#Table(name="Ordertable", schema="cf_2583f365_c3c6_499a_a60d_138e7e7023eb")
public class Order {
#JsonIgnore
#ManyToOne()
#JoinColumn(name = "ORDER_CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID")
#Fetch(FetchMode.JOIN)
private Customer customer;
}
The findByCustomerId will actually generate a query based on that method instead of using em.find. It will create something along the lines of SELECT c FROM Customer c WHERE c.customerId=:customerId. afterwards it will notice the fetch strategy and obtain the needed references. This is also explained here. The query will do exactlly what you instruct it to do.
If you want to eagerly load the reference you would need to write the query yourself along the lines of SELECT c FROM Customer c JOIN FETCH c.orders o WHERE c.customerId=:customerId, this will automatically retrieve the orders.
However the customerId is actually the primary key or identitifier for your entity and thus you should actually be using the findById or findOne method (depending on your Spring Data JPA version). This will use the EntityManager.find which should take the mapping information into account and create the appropriate query.

How to manage OnetoOne inserting data in child only

I am very new to hibernate and I am working with JPA and Hibernate4. Trying to insert parent object in child as onetoone relationship.
I went through some tutorials but All the example in the web shows, inserting both parent and child tables.
I want to insert data in child table only.
I have two tables called user and department.
User table consists of user details with department as onetoone relationship, as follows,
#Entity
#Table(name = "User")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "_id")
private String id;
#Column(name = "firstName")
private String firstName;
#Column(name = "lastName")
private String lastName;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "departmentId")
private Department departmentId;
// getters and setters...
}
Below is my Department entity,
#Entity
#Table(name = "Department")
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "_id")
private String id;
#Column(name = "name")
private String name;
// getters and setters...
}
In department table there is only 4 data. I want to insert data only in user data while insert into it and don't want to insert in Department.
How can I do that.Please assist.
You have to use mappedBy for this, as mentoned below in child Table, Department in your case
#OneToOne(mappedBy="department")
private UserEntity user;
These posts explain you better this,
JPA JoinColumn vs mappedBy
Understanding mappedBy annotation in Hibernate
You need to specify the relationship owner using mappedBy property in the OneToOne mapping in the owner side, here in your case in the Department class, you should add:
#OneToOne(mappedBy="department")
private UserEntity user;
I updated your code, to included the stated annotation and also renamed the Department property in your UserEntity class from departmentId to department to avoid confusion between relationship owner and its id:
#Entity
#Table(name = "User")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "_id")
private String id;
#Column(name = "firstName")
private String firstName;
#Column(name = "lastName")
private String lastName;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "departmentId")
private Department department;
// getters and setters...
}
Below is the Department entity,
#Entity
#Table(name = "Department")
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "_id")
private String id;
#Column(name = "name")
private String name;
#OneToOne(mappedBy="department")
private UserEntity user;
// getters and setters...
}
This will give you the right mapping with the expected behaviour.
In the #OneToOne annotation, the default value for parameter optional is true. So your annotation is the same as #OneToOne(fetch = FetchType.EAGER, optional = true). This means you can simply leave the Department in a UserEntity instance empty. In that case, persisting it results in persisting only a user entity and no department.
Even if you created a Department instance and assigned it to a UserEntity instance, persisting the UserEntity would not automatically persist the Department, since you don't have any cascade parameter in your annotation. If you don't automatically cascade persists, you would have to persist the Department first and then persist the corresponding user entity.
Maybe you're asking about using existing departments for your user entities. In that case, you first need to get the department via Hibernate (or the JPA API) from an entity manager. The entity instance you get is managed by Hibernate, and you can then set it in a UserEntity and persist that, to have it refer to the department.
Finally, I think one department will probably have more than one user. It might make more sense to have a #ManyToOne annotation instead of #OneToOne, indicating multiple users can refer to the same department, but that depends on your domain model.

Shared primary key generation. Hibernate

I'm having problems with generating primary keys with one-to-one relations that use shared primary key.
Here's code:
#Entity
#Table(name = "osoba")
public class Osoba implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "osoba_id")
private Integer osobaId;
#PrimaryKeyJoinColumn
#OneToOne(cascade = CascadeType.PERSIST)
public Pracownik pracownik;
...
and second class:
#Entity
#Table(name = "pracownik")
public class Pracownik
{
#OneToOne
#JoinColumn(name = "osoba_id")
#MapsId("osobaId")
private Osoba osoba;
#Id
#Column(name = "osoba_id")
private Integer osobaId;
...
I've been similar issues and I thought that i've done everything correctly but i still get
org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save(): entity.Pracownik
when trying to persist Pracownik objects.
You need to follow example from #MapsId documentation (in your case, with #Id instead of #EmbeddedId):
#Entity
#Table(name = "pracownik")
public class Pracownik {
#Id
#Column(name = "oboba_id")
private Integer id;
#OneToOne
#MapsId
private Osoba osoba;
...
}
Inverse side of #OneToOne relationship should be mapped with mappedBy, as usually:
#Entity
#Table(name = "osoba")
public class Osoba implements Serializable {
...
#OneToOne(mappedBy = "osoba", cascade = CascadeType.PERSIST)
public Pracownik pracownik;
...
}
This old question but it works for me. Mayby it will help someone.
SQL Script (Oracle)
DROP TABLE HIBERNATE.PRACOWNIK;
DROP TABLE HIBERNATE.OSOBA;
DROP SEQUENCE HIBERNATE.OSOBA_SEQ;
CREATE TABLE HIBERNATE.OSOBA (
osoba_id NUMBER(15),
CONSTRAINT OSOBA_PK PRIMARY KEY (osoba_id)
);
CREATE TABLE HIBERNATE.PRACOWNIK (
pracownik_id NUMBER(15),
CONSTRAINT PRACOWNIK_PK PRIMARY KEY (pracownik_id),
CONSTRAINT PRACOWNIK_FK FOREIGN KEY (pracownik_id) REFERENCES OSOBA(osoba_id)
);
CREATE SEQUENCE HIBERNATE.OSOBA_SEQ START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE;
Osoba.java
#Entity
#Table(name = "osoba")
public #Data class Osoba {
#Id
#Column(name = "osoba_id")
#GeneratedValue(generator="osoba-generator", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name="osoba-generator", allocationSize = 1, sequenceName = "OSOBA_SEQ")
private Long osobaId;
#OneToOne(fetch=EAGER, mappedBy="osoba", cascade=ALL)
private Pracownik pracownik;
}
Pracownik.java
#Entity
#Table(name="pracownik")
public #Data class Pracownik {
#Id
#Column(name = "pracownik_id")
#GeneratedValue(generator="pracownik-generator")
#GenericGenerator(name="pracownik-generator", strategy="foreign", parameters=
#Parameter(name = "property", value = "osoba")
)
private Long pracownikId;
#OneToOne(fetch=FetchType.EAGER)
#PrimaryKeyJoinColumn
private Osoba osoba;
}
#Data is Lombok

Categories