How can I select only specific fields from the following class hierarchy?
#Entity
public class MyEntity {
#Id private Long id;
#ManyToOne
#JoinColumn(name="fk_person_id", foreignKey = #ForeignKey(name="fk_person"))
private Person person; //unidirectional
private String fieldA, fieldB, ..field M;
//many more fields and some clobs
}
#Entity
public class Person {
#Id private Long id;
private String firstname;
private String lastname;
}
interface MyEntityRepository extends CrudRepository<MyEntity, Long> {
List<MyEntity> findByIdAndPersonFirstnameAndPersonLastname(long id, String firstname, String lastname);
}
This works perfectly, just the performance is very poor as MyEntity and also Person have some fields and associations that I would like to prevent to be fetched in this specific case (eg clob/texts).
Question: how can I write this query to find the result set, and just fetch the fields that are absolutely required (let's assume id, fieldA, fieldB from MyEntity?
Use lazy initialization for the non necessary fields:
FetchType.LAZY = Doesn’t load the relationships unless explicitly “asked for” via getter
FetchType.EAGER = Loads ALL relationships
For example, in Person:
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="fk_person_id", foreignKey = #ForeignKey(name="fk_person"))
private Person person; //unidirectional
Mark the unwanted fields as lazy (beware of this documented warning though), or create a dedicated class with the properties you want, and use projections in your query:
select new com.foo.bar.MyEntityWithFirstNameAndLastName(m.id, person.firstname, person.lastname)
from MyEntity m
join m.person person
where ...
If Person is an often-used entity, and contains large blobs that should rarely be fetched, you should consider storing the blobs in a separate entity, and use a OneToOne lazy association.
Related
I have a one to one relationship like this:
#Entity
public class Modification {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="modification_id")
Long id;
#Column(name="second_line", length=1000)
String firstLine;
#OneToOne
#JoinColumn(name="tm_satus")
StatusOrder tmStatus;
// Constructors getters and setters
}
#Entity
public class StatusOrder {
#Id
#Column(name="status")
int status;
#Column(name="status_order")
int order;
// Constructors getters and setters
}
So every Modification has a StatusOrder.
Now I want to perform a query where I select from Modification table ordered by order field in StatusOrder.
Is there a way to have a method in my repository like:
Page<Modification> findAllOrderByStatusOrder(Pageable pageable);
Try these,
#Query("SELECT m FROM Modiciation m ORDER BY m.tmStatus.order DESC")
Page<Modification> findAllOrderByStatusOrder(Pageable pageable);
or (I don't know if this'll work.)
Page<Modification> findAllOrderBytmStatus_orderDesc(Pageable pageable);
I am trying to learn it and I am facing some conceptual issues.
Let's imagine to have the entities of Employee, Project and Department where:
Employee - Department is a ManyToOne relationship
Employee - Project is a ManyToMany relationship
Right now I have written this:
Employee.java
#Entity
public class Employee {
#Id #GeneratedValue
private Long id;
private String name;
#ManyToOne
#JoinColumn(name="department_id")
private String department;
#ManyToMany
#JoinTable(name="project_employee",
joinColumns=#JoinColumn(name="project_id", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="employee_id", referencedColumnName="id"))
private ArrayList<Project> projects;
//constructors, getters, setters...
}
Project.java
#Entity
public class Project {
#Id #GeneratedValue
private Long id;
private String client;
#ManyToMany(mappedBy="project")
private ArrayList<Employee> employees;
//constructors, getters, setters...
}
Department.java
#Entity
public class Department {
#Id #GeneratedValue
private Long id;
private String name;
#OneToMany(mappedBy="department")
private ArrayList<Employee> employees;
//constructors, getters, setters...
}
EmployeeService.java
#Service
public class EmployeeService {
#Autowired
private EmployeeRepository employeeRep;
#Autowired
private ProjectRepository projectRep;
#Autowired
private DepartmentRepository deptRep;
public List<Project> getProjectsByEmployee(Long emplId){
}
}
The first solution that came in my mind to implement that methods was:
Get the specific employee object through the employeeRep and then loop over its list project retrieving ids and using them to retrieve project info with projectRep.
But in this case it seems I am not exploiting the table relation I have between Project and Employee, so what's the point on having it then?
But in that case it seems to me that I am not exploiting the table
relation I have between Project and Employee, so what's the point on
having it then?
In fact you exploit that ManyToMany table when you access to the Project relationship from an Employee entity (JPA performs a query for you at that time) but you don't exploit it efficiently. I will explain.
The first solution that came in my mind to implement that methods was:
Get the specific employee object through the employeeRep and then loop
over its list project retrieving ids and using them to retrieve
project info with projectRep.
That is bad idea because you don't want to perform a lot of queries while a single one rather simple could achieve the same thing.
Similarly defining a repository by entity is not mandatory :
#Autowired
private EmployeeRepository employeeRep;
#Autowired
private ProjectRepository projectRep;
#Autowired
private DepartmentRepository deptRep;
if according to your requirements, your queries rely on a pivot table (for example the employee table), just declare :
#Autowired
private EmployeeRepository employeeRep;
And perform the join from employee to project to get the list of projects for a specific employee.
In JPQL you could use fetch join on projects to get projects associated to the matching employee such as :
public List<Project> getProjectsByEmployee(Long empId){
String jpql = "SELECT emp FROM Employee e" +
"INNER JOIN FETCH e.projects" +
"WHERE emp.id=:empId";
TypedQuery<Employee> query = em.createQuery(jpql, Employee.class)
.setParameter("empId", empId);
Employee emp = query.getSingleResult();
return emp.getProjects();
}
Have a "full Entity" class:
#Entity(name = "vacancy_dec_to_words")
public class VacancyDescriptionToWords {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#JoinColumn(name = "vacancy_description_id")
#ManyToOne(cascade = CascadeType.ALL)
private VacancyDescription vacancyDescription;
#JoinColumn(name = "words_id")
#ManyToOne
private Words words;
#Column(name = "qty")
private int qty;
#Column(name = "create_date")
private Date date;
//...getters and setters
In some methods I need use only 2 column from this database: word_id and qty
I try the follow way:
Projections
https://docs.spring.io/spring-data/jpa/docs/2.1.2.RELEASE/reference/html/#projections
public interface QtyWords {
Long getWords();
Integer getQty();
}
JpaReposytory:
*Query, that I use tested and it workable, I use him in JpaRepository:
#Repository
public interface SmallVDTWRepository extends JpaRepository<VacancyDescriptionToWords, Long> {
#Query(nativeQuery = true,
value = "SELECT sum(qty), words_id FROM vacancy_desc_to_words WHERE vacancy_description_id IN (" +
"SELECT id FROM vacancy_description WHERE vacancy_id IN (" +
"SELECT id FROM vacancy WHERE explorer_id = :exp))" +
"GROUP BY words_id")
List<QtyWords> getDistinctWordsByExplorer(#Param("exp") long exp);
}
But I get some interesting result when I get list of entities:
List<QtyWords> list = vdtwService.getByExplorerId(72);
I am not get any exceptions, but I have the list with are unknowns objects. This objects contains my data, which I need(qty and words_id), but I cannot get them from him.
Can I use this method (Projection) to implement this task and, in general, how to correctly implement the 'Light Entity' in this case?
Spring provides two mechanisms that can be used to limit data to be fetched.
Projections
Projections can help you to reduce data, retrieved from database, by setting what exactly attributes you want to fetch.
Example:
#Entity
class Person {
#Id UUID id;
String firstname, lastname;
#OneToOne
Address address;
}
#Entity
static class Address {
#Id UUID id;
String zipCode, city, street;
}
interface NamesOnly {
String getFirstname();
String getLastname();
}
#Repository
interface PersonRepository extends Repository<Person, UUID> {
Collection<NamesOnly> findByLastname(String lastname);
}
Entity graph
Annotation EntityGraph can help you to reduce amount of queries to database, by setting what exactly related entities you need to fetch.
Example:
#Entity
#NamedEntityGraph(name = "GroupInfo.detail", attributeNodes = #NamedAttributeNode("members"))
public class GroupInfo {
#Id UUID id;
#ManyToMany //default fetch mode is lazy.
List<GroupMember> members = new ArrayList<GroupMember>();
}
#Repository
public interface GroupRepository extends CrudRepository<GroupInfo, String> {
#EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD)
GroupInfo getByGroupName(String name); //Despite of GroupInfo.members has FetchType = LAZY, it will be fetched because of using EntityGraph
}
There are two types of EntityGraph:
EntityGraphType.LOAD - is used to specify an entity graph, attributes that are specified by attribute nodes of the entity graph are treated as FetchType.EAGER and attributes that are not specified are treated according to their specified or default FetchType.
EntityGraphType.FETCH - is used to specify an entity graph, attributes that are specified by attribute nodes of the entity graph are treated as FetchType.EAGER and attributes that are not specified are treated as FetchType.LAZY.
PS: Also remember that you can set lazy fetch type: #ManyToOne(fetch = FetchType.LAZY) and JPA will not fetching child entities when parent is being fetched.
Writing the case it will be more simple to explain.
I am using Seam 2.3.1 v Hibernate JPA 2.0 and in our project. I have a base Person Entity Class.
#Entity
#Name("person")
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "person")
public class Person extends BaseEntity {
private String name;
private String surName;
private String email;
private String phone;
// getter & setters
}
And I have 3 more Entity extends from Person as #Inheritance(strategy = InheritanceType.JOINED): Personel, Musteri, DisPaydas
#Entity
#Name("personel")
public class Personel extends Person {
private String appellation;
// getter & setters
}
I want to List personels, musteris and dispaydas in my bean however when I set them in my Group Entity, I want to save them as Person.
In fact in DB there is no difference between person_id and personel_id, they are same. However when I listing it, they are List<Personel>
In summary:
I want to get List<Person> from List<Personel>
or Person from Personel object.
You're going to have to define an #Id and #Column in the Person class for the person_id.
However, since the column has a different name in the child entity, you'll need to change it using an #AttributeOverride to point to the personel_id #Column.
It couldn't hurt to use a #DiscriminatorColumn in the parent and #DiscriminatorValue in the children, but I don't think Hibernate requires those.
I have a Company entity that I fetch with a JPQL query with Hibernate. The entity has a many-to-many association with a Keyword entity. Since the join table has an additional column is_active, this table has been mapped to a CompanyKeyword entity. So the association is like this:
Company <-- CompanyKeyword --> Keyword
Now, the association from the Company entity is lazy, and it is not initialized by my JPQL query, as I want to avoid creating a cartesian product performance problem. That is why I want to initialize the association after running the JPQL query, e.g. like this:
#Service
class CompanyServiceImpl implements CompanyService {
#Autowired
private CompanyRepository companyRepository;
#Transactional
public Company findOne(int companyId) {
Company company = this.companyRepository.findOneWithSomeCustomQuery(companyId);
Hibernate.initialize(company.companyKeywords());
return company;
}
}
For a "normal" many-to-many association, this would work great, as all of the associated entities would be fetched in a single query. However, since I have an entity between Company and Keyword, Hibernate will only initialize the first part of the association, i.e. from Company to CompanyKeyword, and not from CompanyKeyword to Keyword. I hope that makes sense. I am looking for a way to initialize this association all the way without having to do something like this:
Company company = this.companyRepository.findOneWithSomeCustomQuery(companyId);
Hibernate.initialize(company.getCompanyKeywords());
for (CompanyKeyword ck : company.getCompanyKeywords()) {
Hibernate.initialize(ck.getKeyword());
}
The above code is neither clean, nor good in terms of performance. If possible, I would like to stick to my current approach of using a JPQL query to fetch my Company entity and then initializing certain associations afterwards; it would take quite a bit of refactoring to change this in my project. Should I just "manually" fetch the association with a second JPQL query, or is there a better way of doing it that I haven't thought of?
Below are my mappings. Thanks in advance!
Company
#Entity
#Table(name = "company")
public class Company implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private int id;
#Size(max = 20)
#OneToMany(fetch = FetchType.LAZY, mappedBy = "company")
private Set<CompanyKeyword> companyKeywords = new HashSet<>();
// Getters and setters
}
CompanyKeyword
#Entity
#Table(name = "company_service")
#IdClass(CompanyServicePK.class)
public class CompanyKeyword implements Serializable {
#Id
#ManyToOne(fetch = FetchType.LAZY, targetEntity = Company.class)
#JoinColumn(name = "company_id")
private Company company;
#Id
#ManyToOne(fetch = FetchType.LAZY, targetEntity = Keyword.class)
#JoinColumn(name = "keyword_id")
private Keyword keyword;
#Column(nullable = true)
private boolean isActive;
// Getters and setters
}
CompanyKeywordPK
public class CompanyServicePK implements Serializable {
private Company company;
private Service service;
public CompanyServicePK() { }
public CompanyServicePK(Company company, Service service) {
this.company = company;
this.service = service;
}
// Getters and setters
// hashCode()
// equals()
}
Keyword
#Entity
#Table(name = "keyword")
public class Keyword {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private int id;
// Fields and getters/setters
}
You'll indeed need to execute an additional JPQL query, fetching the company with its companyKeyWords and with the keyword of each CompanyKeyWord.
You could also doing it by simply looping and initializing every entity, and still avoid executing too many queries, by enabling batch fetching.