Get single element from JPA nested OneToMany relationship - java

I have those Entities, It is not from real code, but it's not important I think.
#Entity
public class Country{
private String name;
//other fields with getters and setters
#OneToMany
private List<City> cites;
}
#Entity
public class City {
private String name;
//other fields with getters and setters
#OneToMany
private List<Employee> emps;//list size could be millions
}
#Entity
public class Employee {
private String name;
//other fields with getters and setters
#OneToMany
private List<Cars> cars;// list size could be 1000
}
#Entity
public class Car {
private String name;
#ManyToOne
private Employee emp;
}
I would like to get single Car entity but with other data as well(Country, City, Employee) like this
1 Country in which 1 City in which 1 Empoyee in which 1 Car(which Id I put on select)
So when I do jpa joins from Country like
select c from Country c
inner join c.cites ci
inner join ci.emps em
inner join ci.cars car where car.id = ?
I get ALL data in Country(with all cities).
What should I do to get 1 Country,1 City , 1 Employee, 1 Car.
If this is not possible to do with one jpa query then please suggest other way.
All relationships are bidirectional and lazy.
I tried this way.
1)select car by id. then get Id of employee from car.
2)select Employee by id - but at this point Employee data are nulls -
I think it is because ManyToOne from Car to Employee is lazy. What do you think ?

Just select the additional entities you want:
select c, ci, em, car from Country c
inner join c.cites ci
inner join ci.emps em
inner join ci.cars car where car.id = ?
Or, since your associations are bidirectional, select the car: it will have a ManyToOne to its employee, which will have a ManyToOne with its city, which will have a ManyToOne with its country:
select car from Car car where car.id = ?
or simply
em.find(Car.class, carId);
and now you can do
car.getEmployee().getCity().getCountry()

If all of the relationships are bidirectional then I would suggest starting from Car and then fetching up the hierarchy.
select c from Car car
inner join fetch car.emp emp
inner join fetch emp.city city
inner join fetch city.country country
where car.id = ?
Just remember to add that fetch in all of the joins that you missed.

Related

JPA direct join to entities with common primary key

Assume I have dozen of one-to-one relation using common id carId and I can't touch schema or merge them into one.
#Entity
public class Car {
#Id
private Integer id;
#OneToOne(mappedBy="car")
private Registration regist;
#OneToOne(mappedBy="car")
private Detail detail;
#OneToOne(mappedBy="car")
private License license;
}
#Entity
public class Registration {
#OneToOne
private Car car;
private String governor;
}
#Entity
public class Detail {
#OneToOne
private Car car;
private Date manufacturingDate;
}
#Entity
public class License {
#OneToOne
private Car car;
private String licenseNumber;
}
// and more ...
When I create a query begin with License to get data from Detail and Registration, the generated sql looks like
select
car_detail.manufacturing_date,
car_registration.governor
from
car_license
left outer join
car
on car_license.car_id=car.id
left outer join
car_detail
on car.id=car_detail.car_id
left outer join
car
on car_license.car_id=car.id
left outer join
car_registration
on car.id=car_registration.car_id
where
and car_license.licenseNumber=?
It's reasonable because JPA doesn't understand column car_id is common across entities. However, the result is hard to debug and error prone on complicated join.
How can I define those entities so that JPA knowns they don't need additional join on table car and can be joined directly, or just join to table car only once?

How do I join multiple table in hibernate and call it to a web page?

I have three table (I won't use my real table but pseudo tables)
Car table
Vin_ID ColorCode MakeId Model
123 qwer Audi A7
456 asdf BMW M5
789 qwer Audi S7
369 cvbn Chevy C9
... ... ... ...
Paint table
Color_Code Color MakeId
qwer Black Audi
asdf Red BMW
qwer Black Audi
cvbn White Chevy
... ... ...
Manufacture table
MakeId
Audi
BMW
Chevy
...
Car table has VIN_ID as PK, ColorCode as FK to Reference table Paint.Colorcode Pk, and MakeId as FK to Reference table Manufacture.Make.
This is the pojo classes
#Entity
#Table(name="car")
public class Car {
#Id
#JoinColumn(name="vin_id")
private Paint paint;
#OneToOne
#Column(name="colorcode")
private String colorCode;
#OneToOne
#JoinColumn(name="makeid")
private Manufacture manufacture;
#Column(name="model")
private String model;
...constructor and getters and setter...
Paint Class
#Entity
#Table(name="paint")
public class Paint {
#Id
#Column(name="colorcode")
private String colorCode;
#Column(name="color")
private String color;
#Column(name="makeid")
private String makeId;
#OneToOne(mappedBy="colorcode")
private Car car;
....Constructor and setters and getter...
Manufacture table
#Entity
#Table(name="manufacture")
public class Manufacture {
#Id
#Column(name="Makeid")
private String MakeId;
#OneToOne(mappedBy="makeid"
private Car car;
....Constructor and setters and getter...
How do i create a join query of the 3 tables in my DAO class (such as example below) and get the results onto a jsp page. If I want to display the vinId in my jsp page is pretty forward ${car.vinId}, But how do display the CAR.COLORCODE, and CAR.MAKEID values from POJO class to my jsp. They are using objects to define the variable?
SELECT C. VINID, P.COLOR, M.MAKEID, C.MODEL
FROM CAR C
JOIN PAINT P ON P.COLORCODE = C.COLORCODE
JOIN MANUFACTURE M ON M.MAKEID = C.MAKEID
I would like to display C. VINID, P.COLOR, M.MAKEID, C.MODEL values on a jsp. Please help.
There are a couple of issues with your entity classes.
First, unless you are going to be explicitly querying for child entities and not the full Car entity, you don't need a bi-directional join (i.e. no Car entity in Paint/Manufacture etc). Second, you have an invalid mapping between Car and Paint. You should do something like this:
Car:
#Entity
#Table(name="car")
public class Car {
#Id
#Column(name = "vin_id")
private int vinId;
#OneToOne
#JoinColumn(name="colorcode")
private Paint paint;
#OneToOne
#JoinColumn(name="makeid")
private Manufacture manufacture;
#Column(name="model")
private String model;
...constructor and getters and setter...
From which you can query the Car entity:
Query q = em.createQuery("select c from Car where c.vinId = :id");
q.setParameter("id", 1234567890123456);
Car c = q.uniqueValue();
And you can access the values you wish via the Car entity in JSTL:
${car.vinId}
${car.paint.colorCode}
${car.manufacture.Makeid}
Finally, you really shouldn't use string values as primary and foreign keys. That can be a major performance bottleneck, especially when joining tables on said keys. It's a much better practice to use integer values.

JPA mappedBy don't persisting entity with relationship

For example, I have the following entities:
Room entity:
#Entity
public class Room extends Base {
#OneToMany(mappedBy = "room")
private List<Person> persons;
//getters and setters
}
And the Person entity:
#Entity
public class Person extends Base {
#ManyToOne
private Room room;
private String name;
//getters and setters
}
If I add person to room's persons list and then persist, the room is added, but person is not. I thought, enough is to add mappedBy parameter to OneToMany annotation, but.. what should I do else?
You need to set CascadeType=PERSIST or ALL on #OneToMany. And Also set room variable in Person class to populate foreign key in Person table.
Do the opposite, set a Room as the room field of a Person instance:
Room room = ...;
person.setRoom(room);
// persist the person
With the mappedBy, you specify that which entity owns the relationship. Updating the relationship between those two entities is the responsibility of the owning side.
In your example, the Person owns the relationship, so it's his responsibility to update the foreign key, not Room's, so when you add a Person to list of persons in Room and update the Room, Room wouldn't update the foreign key for you.

How to filter by attributes of attributes of an Entity class in Hibernate

I'm using Hibernate in Java to map classes to DB tables. I have a Person table, each entry has many Pets, each of which has many Toys.
I'd like to be able to filter these in my DAO based on attributes of the toys; for example, find all people with pets that have red toys, as a List<Person>. How can I filter on this?
Person class:
#Entity
public class Person {
...
#OneToMany(mappedBy = "person")
private List<Pet> pets;
...
}
Pet class:
#Entity
public class Pet {
...
#OneToMany(mappedBy = "pet")
private List<Toy> toys;
...
}
Toy class:
#Entity
public class Toy {
...
private String colour;
...
}
I am not sure what you mean 'filter' in this case, but you can always use HQL.
For example:
select p from Person p
inner join p.pets as pets
inner join pets.toys as toys
where p.pets.size() > 0
and toys.color = 'red'
Maybe where condition 'p.pets.size() > 0' is redunant here, because of inner join.

Criteria api restriction with InheritanceType.JOINED

I'm stuck with the following problem - i have a MappedSuperClass entity with InheritanceType.JOINED, and some entities, extending that superclass. And when i try to query this entity with criteria api, i'm getting strange result set, which contains only one of sub entities.
The code as follows:
Base parent class:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class Parent
{
#Id
private long id;
/** getters and setters */
}
Mapped superclass, which contains shared attribute:
#MappedSuperclass
public class Attributes extends Parent
{
#Column
private String name;
/** getters and setters */
}
First subentity:
#Entity
#Table(name = "classA")
public class SubClassA extends Attributes
{
#Column
private String lastName;
/** getters and setters */
}
Second subentity:
#Entity
#Table(name = "classB")
public class SubClassB extends Attributes
{
#Column
private String secondName;
/** getters and setters */
}
And the criteria:
Criteria criteria = session.createCriteria(Parent.class);
Criterion restrictions = Restrictions.ilike("name", "n%");
Junction conditionGroup = Restrictions.conjunction();
conditionGroup.add(restrictions);
criteria.add(conditionGroup);
As a result, i've got the following sql:
select
this_.id as id1_0_0_,
this_1_.name as name1_1_0_,
this_1_.lastName as lastName2_1_0_,
this_2_.name as name1_2_0_,
this_2_.secondName as secondNa2_2_0_,
case
when this_1_.id is not null then 1
when this_2_.id is not null then 2
when this_.id is not null then 0
end as clazz_0_
from Parent this_
left outer join classA this_1_ on this_.id=this_1_.id
left outer join classB this_2_ on this_.id=this_2_.id
where
(lower(this_1_.name) like ?)
As i can see, the selection is nearly i've expected, except the "where" clause, which includes only this_1_.name, which is alias for classA, but not classB. Both tables (classA and classB) are filled with data, but, the result set contains data only from classA table.
Other observation shows, that, if i change InheritanceType to TABLE_PER_CLASS, the result would be correct, because it uses union:
select
this_.id as id1_0_0_,
this_.name as name1_1_0_,
this_.lastName as lastName2_1_0_,
this_.name as name1_2_0_,
this_.secondName as secondNa2_2_0_,
this_.clazz_ as clazz_0_
from
(
select
id,
cast(null as varchar(100)) as name,
cast(null as varchar(100)) as lastName,
cast(null as varchar(100)) as secondName,
0 as clazz_
from Parent
union all
select
id,
name,
lastName,
cast(null as varchar(100)) as secondName,
1 as clazz_
from classA
union all select
id,
name,
cast(null as varchar(100)) as lastName,
secondName,
2 as clazz_ from classB
) this_ where (lower(this_.name) like ?)
So, to make a long story short, the there is a question - is there a way to make restriction queries for InheritanceType.JOINED, which would cover ALL subclasses? Something like:
where
( (lower(this_1_.name) like ?) or
(lower(this_2_.name) like ?)
)
Thanks in advance for your replies!

Categories