I have #OneToOne relationship with classes:
#Entity
#Table(name = "persons", schema = "persons_info")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "first_name",
nullable = false)
#JsonProperty("first_name")
private String firstName;
#Column(name = "last_name",
nullable = false)
#JsonProperty("last_name")
private String lastName;
#Basic
#Column(name = "birth_date", nullable = false)
private Date birthDate;
#OneToOne(cascade = CascadeType.ALL,
orphanRemoval = true)
#JoinColumn(name = "address_id",
referencedColumnName = "id")
private Address address;
// setters, getters, equals...
}
Address:
#Entity
#Table(name = "addresses", schema = "persons_info")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String city;
#Column(nullable = false)
private String street;
#Column(nullable = false)
private String house;
#Column(nullable = false)
private String flat;
#JsonIgnore
#OneToOne(mappedBy = "address")
private Person person;
// setters, getters, equals...
}
I have a default JpaRepository for person:
#Repository
public interface PersonRepository
extends JpaRepository<Person, Long> {
}
And when i calling repository.findAll() i taking n+1 problem:
Hibernate:
select
a1_0.id,
a1_0.city,
a1_0.flat,
a1_0.house,
p1_0.id,
p1_0.birth_date,
p1_0.first_name,
p1_0.last_name,
a1_0.street
from
persons_info.addresses a1_0
left join
persons_info.persons p1_0
on a1_0.id=p1_0.address_id
where
a1_0.id=?
Hibernate:
select
a1_0.id,
a1_0.city,
a1_0.flat,
a1_0.house,
p1_0.id,
p1_0.birth_date,
p1_0.first_name,
p1_0.last_name,
a1_0.street
from
persons_info.addresses a1_0
left join
persons_info.persons p1_0
on a1_0.id=p1_0.address_id
where
a1_0.id=?
How i can fix that? (I want fetching with JOIN like this example: SELECT * FROM persons INNER JOIN address ON person.address_id = address.id)
Solved by adding #Query annotation:
#Repository
public interface PersonRepository
extends JpaRepository<Person, Long> {
#Query("""
SELECT p FROM Person p
LEFT JOIN FETCH p.address a""")
public List<Person> findAll();
}
Now that's looks like:
Hibernate:
select
p1_0.id,
a1_0.id,
a1_0.city,
a1_0.flat,
a1_0.house,
a1_0.street,
p1_0.birth_date,
p1_0.first_name,
p1_0.last_name
from
persons_info.persons p1_0
left join
persons_info.addresses a1_0
Related
Using Hibernate, I have created two entities - Employee and EmployeeDetails. Since EmployeeDetails cannot exist without a corresponding entry in Employee, I figured I don't need an extra ID for EmployeeDetails, but could instead use the ID of the Employee entity. Here is how I have implemented this idea:
Employee-Entity:
#Entity
#Table(name = "employees")
#Data
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "employee_id")
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
#OneToOne(cascade = CascadeType.ALL)
EmployeeDetails employeeDetails;
}
Employee-Details-Entity:
#Entity
#Table(name = "employee_details")
#Data
public class EmployeeDetails {
#Id
private Long id;
#Column(name = "address")
private String address;
#Column(name = "e_mail", nullable = false)
private String eMail;
#Column(name = "phone")
private String phone;
#MapsId
#OneToOne(mappedBy = "employeeDetails", cascade = CascadeType.ALL)
#JoinColumn(name = "employee_id")
private Employee employee;
}
By adding the #MapsId annotation to the employee-variable inside EmployeeDetails, I should be assigning the primary key of the Employee-entity to the Id-column of EmployeeDetails.
In a second step, I have written some data into both of my tables.
employee table in MySQL database:
employee_id first_name last_name employee_details_employee_id
1 John Smith null
2 Jennifer Adams null
The last column was somehow generated by Hibernate. I don't understand why. It appears to be some column for identification, but I don't need it.
employee_details table in MySQL database:
employee_id address e_mail phone
1 null john.smith#gmail.com null
2 null jennifer.adams#gmail.com null
I have only assigned an e-mail to the employees. Surprisingly, there is no employee-entry in this database table. I don't really need it anyways, but I was expecting it. So yeah, I think I am doing something terribly wrong and would really appreciate some help.
Change mappedBy side, here useful links
https://vladmihalcea.com/change-one-to-one-primary-key-column-jpa-hibernate/
https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/
https://javabydeveloper.com/one-one-bidirectional-association/
#Entity
#Table(name = "employees")
#Data
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "employee_id")
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
#OneToOne(mappedBy = "employee", cascade = CascadeType.ALL)
EmployeeDetails employeeDetails;
}
Entity
#Table(name = "employee_details")
#Data
public class EmployeeDetails {
#Id
private Long id;
#Column(name = "address")
private String address;
#Column(name = "e_mail", nullable = false)
private String eMail;
#Column(name = "phone")
private String phone;
#MapsId
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "employee_id")
private Employee employee;
}
#MapId is not a popular solution in work with Hibernate.
Maybe in your case, #Embeddable will be a better option?
If I understand correctly, EmployeeDetails cannot exist without correlated Employee. So, EmployeeDetails could be a field in Employee as an embeddable field:
#Entity
#Table(name = "employees")
#Data
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "employee_id")
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
#Embedded
EmployeeDetails employeeDetails;
}
Then EmployeeDetails doesn't need ID and relation with the employee:
#Embeddable
public class EmployeeDetails {
#Column(name = "address")
private String address;
#Column(name = "e_mail", nullable = false)
private String eMail;
#Column(name = "phone")
private String phone;
}
As you can see, now in the database it's only one table employees, but in our hibernate model, we have two separated objects. Probably you don't need EmployeeDetails without Employee entity, so there is more efficient construction.
If you really need a separated table for EmployeeDetails with relation to Employee I recommend creating standard one-to-one mapping instead of #MapId construction.
I want to understand how to work with related entities in Hibernate.
There are two related entities:
#Entity
#Table(name = "usr")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String username;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private String email;
private boolean active;
#Enumerated(EnumType.STRING)
private Role role;
#OneToMany(fetch = FetchType.LAZY,
mappedBy = "responsibleUser", cascade = CascadeType.ALL)
private List<GrowBox> growBoxes;
//def-constructor , getters, setters
}
and
#Entity
#Table(name = "growBoxes")
public class GrowBox {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#Column(nullable = false)
private Integer length;
#Column(nullable = false)
private Integer width;
#Column(nullable = false)
private Integer height;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "responsibleGrowBox", cascade = CascadeType.ALL)
private List<Plant> plants;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "activeGrowBox", cascade = CascadeType.ALL)
private List<Sensor> sensors;
#ManyToOne
#JoinColumn(name = "user_id")
private User responsibleUser;
//def-constructor , getters, setters
}
I have registered mapping using annotations. Hope it is correct. And I want to find box by user Id, but don't know how HQL query should be written. 'Cause there is no "user_id" field in my Box Class. Instead there is "User responsibleUser" field. And smth like this won't work(should not)
#Autowired
SessionFactory sessionFactory;
#Override
public List<GrowBox> findByUser(Long userId) {
Session session = sessionFactory.openSession();
String hqlQuery = "from GrowBox where user_id =: userId";
Query query = session.createQuery(hqlQuery);
List growBoxes = query.getResultList();
session.flush();
session.close();
return growBoxes;
}
A HQL query would be
String hqlQuery = "from GrowBox gb where gb.responsibleUser.id =: userId";
I have a problem with the relationship oneToMany. I created tables in SQLite DB, this is my tables:
I created the two models CategoryModel and ProductModel.
ProductModel is:
#Entity
#Table(name = "Product_Category")
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class ProductModel {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long product_id;
private Long category_id;
private String name;
private String description;
private int numberOfProduct;
private String image;
private int price;
#JoinColumn(name = "country_id", nullable = false)
private CategoryModel category;
//geter's and seter's
}
My CategoryModel:
#Entity
#Table(name = "Category")
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class CategoryModel {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String category_name;
private String category_description;
private String image_path;
#OneToMany( mappedBy = "category")
private Set<ProductModel> category;
//Geter's and Seter's
My Repository:
public interface CategoryRepository extends JpaRepository<CategoryModel, Long> {
#Query("SELECT * "
+ "FROM Product_Category d INNER JOIN d.categoryModel e")
List<ProductModel> fetchEmpDeptDataInnerJoin();
}
I do not understand where I make a mistake. I have this error:
Could not determine type for:
com.dar.darkozmetika.models.CategoryModel, at table: product_category,
for columns: [org.hibernate.mapping.Column(category)]
1) Add #ManyToOne annotation:
#ManyToOne
#JoinColumn(name = "country_id", nullable = false)
private CategoryModel category;
2) Remember that you are using JPQL, not SQL (unless you sent native="true"):
#Query("SELECT p "
+ "FROM ProductModel p INNER JOIN p.category c")
I have the following entities:
Person.java
#Table(name = persons)
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "UserID", nullable = false)
private Long userId;
#Column(name = "Employeenumber", nullable = false) private String employeeNumber;
#Column(name = "Firstname", nullable = false) private String firstName;
#Column(name = "Lastname", nullable = false) private String lastName;
public User() { }
public User(String employeeNumber, String firstName, String lastName) {
super();
this.employeeNumber = employeeNumber;
this.firstName = firstName;
this.lastName = lastName;
}
/*
getter and setters
...
*/
}
Personhistory.java
#Entity
#Table(name = personhistory)
public class Personhistory {
#Id
#Column(name = "UserID", nullable = false)
private Long userId;
#Column(name = "Fromdate", nullable = false) private Date fromDate;
#Column(name = "Todate", nullable = false) private Date toDate;
#Column(name = "TeamID", nullable = false) private Integer teamId;
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "UnikId", nullable = false)
private Integer unikId;
public Userhistory() {
}
public Userhistory(Long userId, Date fromDate, Date toDate, int teamId) {
super();
this.userId = userId;
this.fromDate = fromDate;
this.toDate = toDate;
this.teamId = teamId;
}
/*
Getters and setters
...
*/
}
Team.java
#Entity
#Table(name = "team")
public class Team {
#Id
#Column(name = "TeamID")
#GeneratedValue(strategy = GenerationType.AUTO)
private int teamId;
#Column(name = "TeamNumber") private String teamNumber;
public Team() {}
public Team(String teamNumber) {
super();
this.teamNumber = teamNumber;
}
/*
Getters and setters
...
*/
}
I want to make a API call like this:
localhost:8080/users/{employee}
And get back an object containing the person (His emp-number, firstname and lastname), when he was at the team and what team that is.
If I were to write this query in MSSQL, it would look like this:
select * from persons p
join personhistory ph on ph.UserID = p.UserID
and ph.Fromdate <= cast(getdate() as date)
and ph.Todate >= cast(getdate() as date)
join team t on t.TeamID = ph.TeamID
where u.Employeenumber = '999'
I have searched around for different solutions like HQL, JPQL, Criteria and so on, but I'm unable to make it work.
Any help would be much appreciated.
AFAIK Hibernate 5.1 provides more generic joins but with prior versions you'd either have to use a cross join and add the conditions in the where-clause or provide a real relation between the entities and join on that relation (using the "with" keyword for additional join conditions).
Example (note that I left out many annotations for simplicity):
class Person {
#OneToMany( mappedBy = "user" )
Collection<Personhistory> history;
...
}
class Personhistory {
#ManyToOne
Person user;
#ManyToOne
Team team;
...
}
Then the query could become
select p, ph, t from Person p
join p.history ph with ph.fromdate <= :date and ph.toDate >= :date
join ph.team t
where p.employeeNumber = :number
I have two tables(entities):
#Entity
#Table(name = "users")
#NamedQuery(name = "User.getAll", query = "SELECT c from User c")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#NotNull
#Column(name = "LOGIN")
private String login;
#Column(name = "PASSWORD", length = 64)
private String password;
#Column(name = "SALT", length = 80)
private String salt;
#ManyToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private Set<Role> roles;
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<Permission> permissions;
And
#Entity
#Table(name = "roles")
#NamedQuery(name = "Role.getAll", query = "SELECT c from Role c")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "ROLE_NAME", length = 100)
private String roleName;
#ManyToMany(mappedBy = "roles")
private Set<User> users;
And method for select Users:
#Override
public List<User> getUsersList() {
Criteria criteria = getSession().createCriteria(User.class);
return (List<User>)criteria.list();
}
I have 2 users. First user has 2 roles and second user has 1 role.
But this method return me 3 users. User who has 2 role is dublicate.
Itried criteria.createCriteria("", JoinType.NONE);
but it not helped.
You need to use criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY), check:
criteria-distinct-root-entity-vs-projections-distinct