JPA criteria query for company and its employee count - java

I am using JPA repositories. I have a Company table with one-to-many relationship with Employee table. I want to get list of companies with employee count who's last name is x. I am trying to use multiselect but the count is not getting populated. Here is what I am trying to do.
public class CompanyEntity {
#Id
private UUID id;
#Column
private String name;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "company", orphanRemoval = true)
private List<EmployeeEntity> employees;
#Transient
private Integer employeeCount;
}
...
#Autowired
private CompanyRepository repo;
...
public Page<CompanyEntity> findAllCompanies(Pageable pageable, String lastname) {
Specification<CompanyEntity> specification = (root, criteriaQuery, criteriaBuilder) -> {
Join employees = root.join("employees");
criteriaQuery.groupBy(root);
criteriaQuery.multiselect(root, criteriaBuilder.count(employees).alias("employeeCount"));
Predicate predicate = criteriaBuilder.equal(employees.get("lastname"), lastname);
return predicate;
};
Page<EmployeeEntity> page = repo.findAll(specification, pageable);
return page;
}

Where is the count value supposed to go?
A Specification just configures the where part of a query.
If you want to control the full query including the select list do a custom method implementation and use the EntityManager directly.

I ended up writing a query on repository method.
#Query("select c, count(e.id) from company c left join employee e on c.id = e.company.id where e.lastname = :lastname group by c.id")
Page<Object[]> findAll(#Param("lastname") String lastname, Pageable pageable);
Here Object[0] contains CompanyEntity and Object[1] contains count as Long.

Related

Hibernate makes extra query to full fill entity

I want to extract entity, that contains only a specific part of data from DB. But Hibernate creates extra query, that fully fill my entity.
EXAMPLE
Pseudo Query: Select * From ShopUnitDB WHERE prices.date BETWEEN 2022-05-02 AND 2022-05-30;
Pseudo Expected: ShopUnit{1, Name, prices=[{2000, 2022-05-25}, {4000, 2022-05-29}]}
Result: All data from ShopUnitPrice table, related to ShopUnitDB.
Console log:
Hibernate: select shopunitdb0_.id as id1_0_, shopunitdb0_.name as name2_0_, shopunitdb0_.parent_id as parent_i3_0_, shopunitdb0_.type as type4_0_ from shop_unit shopunitdb0_ inner join shop_unit_price prices1_ on shopunitdb0_.id=prices1_.unit_id where shopunitdb0_.id=? and (prices1_.date between ? and ?)
Hibernate: select prices0_.unit_id as unit_id4_1_0_, prices0_.id as id1_1_0_, prices0_.id as id1_1_1_, prices0_.date as date2_1_1_, prices0_.price as price3_1_1_, prices0_.unit_id as unit_id4_1_1_ from shop_unit_price prices0_ where prices0_.unit_id=?
Look, first query extracts correct entity - that contains only a part based on WHERE ... BETWEEN statement. Next it creates new query to extract all data of sub entity of ShopUnitDB.
I use Spring Data JPA Specification. Also tryed to use #Query in JpaRepository. No luck.
Entity 1:
#Entity
#Table(name = "shop_unit")
public class ShopUnitDB {
#Id
private UUID id;
#Column(name = "name")
private String name;
#Column(name = "parent_id")
#Nullable
private UUID parentId;
#Enumerated(EnumType.STRING)
private ShopUnitType type;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "unit_id", referencedColumnName = "id")
private List<ShopUnitPrice> prices;
#Transient
private Set<ShopUnitDB> children;
...
public List<ShopUnit> convertToShopUnitStatistic() {
return prices.stream()
.map(price -> new ShopUnit(id, name, price.getDate(),
parentId, type, price.getPrice()))
.collect(Collectors.toList());
}
...
}
Entity 2:
#Entity
#Table(name = "shop_unit_price")
public class ShopUnitPrice {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "unit_id")
private UUID unitId;
#Column(name = "date")
private LocalDateTime date;
#Column(name = "price")
private Integer price;
...
}
My Service, that call JpaRepository using Spring Data JPA Specification:
#Service
public class ShopUnitService {
private ShopUnitRepository jpa;
#Autowired
public ShopUnitService(ShopUnitRepository jpa) {
this.jpa = jpa;
}
public List<ShopUnit> getShopUnitStatistic(UUID uuid, LocalDateTime start, LocalDateTime end)
{
unitDB = jpa.findAll(where(idLike(uuid).and(joinPrices(start, end)))).get(0);
return unitDB.convertToShopUnitStatistic();
}
private Specification<ShopUnitDB> idLike(UUID id) {
return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("id"), id);
}
private Specification<ShopUnitDB> joinPrices(LocalDateTime start, LocalDateTime end) {
return (root, query, criteriaBuilder) -> {
return criteriaBuilder.between(root.join("prices").get("date"), start, end);
};
}
}
Spring JpaRepository:
#Repository
public interface ShopUnitRepository extends JpaRepository<ShopUnitDB, UUID>, JpaSpecificationExecutor<ShopUnitDB> {}
UPDATE
spring-boot-starter-parent version: 2.6.8
application.properties
server.port=80
spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/goods_warehouse
spring.datasource.username=username
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=none
spring.jpa.open-in-view=false
spring.jpa.show-sql=true
I see you are using fetch = FetchType.EAGER for OneToMany association (which is not recommended) in ShopUnitDB entity and criteria APIs to fetch the data. If you use JPQL/Criteria APIs to fetch data with fetchType=EAGER, it will fire 2 queries to retrieve data. Please refer this link for more details.
To fix your issue, I think you should either remove fetchType from OneToMany association in ShopUnitDB or make it fetchType = FetchType.LAZY
According to #Daniel Wosch's comment, i understood the main problem. To solve it, i used to use JPARepository method this #Query like this:
#Repository
public interface ShopUnitRepository extends JpaRepository<ShopUnitDB, UUID>, JpaSpecificationExecutor<ShopUnitDB> {
#Query("SELECT unit " +
"FROM ShopUnitDB unit JOIN FETCH unit.prices price " +
"WHERE unit.id = ?1 AND price.date BETWEEN ?2 AND ?3")
ShopUnitDB findByIdAndDate(UUID uuid, LocalDateTime dateStart, LocalDateTime dateEnd);
}
Note that i used JOIN FETCH *ShopUnit collection field* and filtering that data set.

Convert postgresql join and group by query to JPA criteria API

I am having trouble to converting the following postgresql query (with a join and a group by) to JPA criteria API for a Spring Boot, JPA, Hibernate application:
select u.id, u.full_name, count(*) project_applications_count from users u
join project_applications pa on pa.created_by = u.id
group by u.id, u.full_name
having count(*) >= 1 and count(*) <= 5
The tables look like this:
create table project_applications (
id serial primary key,
...
city_id integer not null references cities (id),
created_by integer not null references users (id)
);
create table users (
id serial primary key,
...
full_name varchar(100) not null
);
And the entities look like this:
#Entity
#Table(name = "project_applications")
public class ProjectApplication {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "created_by")
private User createdBy;
...
}
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "full_name")
private String fullName;
...
}
I tried searching online for a solution but every exemple I found was using either a join or group by, but not both.
Using #akortex's idea with projections, I think something like this should work:
public class UserSummary {
private Long id;
private String fullName;
private Long count;
public UserSummary() {
}
public UserSummary(Long id, String fullName, Long count) {
this.id = id;
this.fullName = fullName;
this.count = count;
}
... (getters and setters)
}
public List<UserSummary> getSummaries(Integer minProjectAppsCount, Integer maxProjectAppsCount) {
CriteriaBuilder cb = _entityManager.getCriteriaBuilder();
CriteriaQuery<UserSummary> query = cb.createQuery(UserSummary.class);
Root<ProjectApplication> projectApp = query.from(ProjectApplication.class);
Join<ProjectApplication, User> userJoin = projectApp.join("createdBy", JoinType.INNER);
query.multiselect(userJoin.get("id"), userJoin.get("fullName"), cb.count(projectApp))
.groupBy(userJoin.get("id"), userJoin.get("fullName"));
List<Predicate> predicates = new ArrayList<>();
if (minProjectAppsCount != null ) {
Predicate p = cb.ge(cb.count(projectApp), minProjectAppsCount);
predicates.add(p);
}
if (maxProjectAppsCount != null ) {
Predicate p = cb.le(cb.count(projectApp), maxProjectAppsCount);
predicates.add(p);
}
query.having(predicates.toArray(new Predicate[0]));
return _entityManager.createQuery(query).getResultList();
}
You could potentially look into projections in order to achieve what you want.
For example consider the following projection and repository:
#Data
#AllArgsConstructor
public class ProjectApplicationSummary {
private Long id;
private String fullName;
private Long count;
}
And:
#Repository
public interface ProjectApplicationRepository extends JpaRepository<ProjectApplication, Long> {
#Query(
"""
SELECT new com.example.springdemo.entities.ProjectApplicationSummary(u.id, u.fullName, count(pa))
FROM User u, ProjectApplication pa
GROUP BY u.id, u.fullName
"""
)
List<ProjectApplicationSummary> getSummaries();
}
You will most likely need to tweak the query a bit (which revolves experimenting with JPQL) but other than that, the basic idea is there.
I'm not sure in my solution, but it should be similar. I took an idea from here. Maybe it helps you to resolve your problem.
public static Specification<User> getUsers() {
return Specification.where((root, query, criteriaBuilder) -> {
CriteriaQuery<User> criteriaQuery = criteriaBuilder.createQuery(User.class);
Subquery<Long> subQuery = criteriaQuery.subquery(Long.class);
Root<ProjectApplication> subRoot = subQuery.from(ProjectApplication.class);
subQuery
.select(criteriaBuilder.count(subRoot))
.where(criteriaBuilder.equal(root.get("id"), subRoot.get("createdBy").get("id")));
query
.multiselect(criteriaBuilder.construct(root.get("id"), root.get("fullName")))
.groupBy(root.get("id"), root.get("fullName"))
.having(criteriaBuilder.and(
criteriaBuilder.greaterThanOrEqualTo(subQuery.getSelection(), 1L),
criteriaBuilder.lessThanOrEqualTo(subQuery.getSelection(), 5L)));
return query.getRestriction();
});
}

Is there a way to map 1 column of a query to a collection using Spring JPA/Hibernate [duplicate]

I am using Spring JPA to perform all database operations. However I don't know how to select specific columns from a table in Spring JPA?
For example:
SELECT projectId, projectName FROM projects
You can use projections from Spring Data JPA (doc). In your case, create interface:
interface ProjectIdAndName{
String getId();
String getName();
}
and add following method to your repository
List<ProjectIdAndName> findAll();
I don't like the syntax particularly (it looks a little bit hacky...) but this is the most elegant solution I was able to find (it uses a custom JPQL query in the JPA repository class):
#Query("select new com.foo.bar.entity.Document(d.docId, d.filename) from Document d where d.filterCol = ?1")
List<Document> findDocumentsForListing(String filterValue);
Then of course, you just have to provide a constructor for Document that accepts docId & filename as constructor args.
You can set nativeQuery = true in the #Query annotation from a Repository class like this:
public static final String FIND_PROJECTS = "SELECT projectId, projectName FROM projects";
#Query(value = FIND_PROJECTS, nativeQuery = true)
public List<Object[]> findProjects();
Note that you will have to do the mapping yourself though. It's probably easier to just use the regular mapped lookup like this unless you really only need those two values:
public List<Project> findAll()
It's probably worth looking at the Spring data docs as well.
In my situation, I only need the json result, and this works for me:
public interface SchoolRepository extends JpaRepository<School,Integer> {
#Query("select s.id, s.name from School s")
List<Object> getSchoolIdAndName();
}
in Controller:
#Autowired
private SchoolRepository schoolRepository;
#ResponseBody
#RequestMapping("getschoolidandname.do")
public List<Object> getSchool() {
List<Object> schools = schoolRepository.getSchoolIdAndName();
return schools;
}
With the newer Spring versions One can do as follows:
If not using native query this can done as below:
public interface ProjectMini {
String getProjectId();
String getProjectName();
}
public interface ProjectRepository extends JpaRepository<Project, String> {
#Query("SELECT p FROM Project p")
List<ProjectMini> findAllProjectsMini();
}
Using native query the same can be done as below:
public interface ProjectRepository extends JpaRepository<Project, String> {
#Query(value = "SELECT projectId, projectName FROM project", nativeQuery = true)
List<ProjectMini> findAllProjectsMini();
}
For detail check the docs
In my case i created a separate entity class without the fields that are not required (only with the fields that are required).
Map the entity to the same table.
Now when all the columns are required i use the old entity, when only some columns are required, i use the lite entity.
e.g.
#Entity
#Table(name = "user")
Class User{
#Column(name = "id", unique=true, nullable=false)
int id;
#Column(name = "name", nullable=false)
String name;
#Column(name = "address", nullable=false)
Address address;
}
You can create something like :
#Entity
#Table(name = "user")
Class UserLite{
#Column(name = "id", unique=true, nullable=false)
int id;
#Column(name = "name", nullable=false)
String name;
}
This works when you know the columns to fetch (and this is not going to change).
won't work if you need to dynamically decide the columns.
In my opinion this is great solution:
interface PersonRepository extends Repository<Person, UUID> {
<T> Collection<T> findByLastname(String lastname, Class<T> type);
}
and using it like so
void someMethod(PersonRepository people) {
Collection<Person> aggregates =
people.findByLastname("Matthews", Person.class);
Collection<NamesOnly> aggregates =
people.findByLastname("Matthews", NamesOnly.class);
}
I guess the easy way may be is using QueryDSL, that comes with the Spring-Data.
Using to your question the answer can be
JPAQuery query = new JPAQuery(entityManager);
List<Tuple> result = query.from(projects).list(project.projectId, project.projectName);
for (Tuple row : result) {
System.out.println("project ID " + row.get(project.projectId));
System.out.println("project Name " + row.get(project.projectName));
}}
The entity manager can be Autowired and you always will work with object and clases without use *QL language.
As you can see in the link the last choice seems, almost for me, more elegant, that is, using DTO for store the result. Apply to your example that will be:
JPAQuery query = new JPAQuery(entityManager);
QProject project = QProject.project;
List<ProjectDTO> dtos = query.from(project).list(new QProjectDTO(project.projectId, project.projectName));
Defining ProjectDTO as:
class ProjectDTO {
private long id;
private String name;
#QueryProjection
public ProjectDTO(long projectId, String projectName){
this.id = projectId;
this.name = projectName;
}
public String getProjectId(){ ... }
public String getProjectName(){....}
}
Using Spring Data JPA there is a provision to select specific columns from database
---- In DAOImpl ----
#Override
#Transactional
public List<Employee> getAllEmployee() throws Exception {
LOGGER.info("Inside getAllEmployee");
List<Employee> empList = empRepo.getNameAndCityOnly();
return empList;
}
---- In Repo ----
public interface EmployeeRepository extends CrudRepository<Employee,Integer> {
#Query("select e.name, e.city from Employee e" )
List<Employee> getNameAndCityOnly();
}
It worked 100% in my case.
Thanks.
You can use JPQL:
TypedQuery <Object[]> query = em.createQuery(
"SELECT p.projectId, p.projectName FROM projects AS p", Object[].class);
List<Object[]> results = query.getResultList();
or you can use native sql query.
Query query = em.createNativeQuery("sql statement");
List<Object[]> results = query.getResultList();
You can apply the below code in your repository interface class.
entityname means your database table name like projects.
And List means Project is Entity class in your Projects.
#Query(value="select p from #{#entityName} p where p.id=:projectId and p.projectName=:projectName")
List<Project> findAll(#Param("projectId") int projectId, #Param("projectName") String projectName);
It is possible to specify null as field value in native sql.
#Query(value = "select p.id, p.uid, p.title, null as documentation, p.ptype " +
" from projects p " +
"where p.uid = (:uid)" +
" and p.ptype = 'P'", nativeQuery = true)
Project findInfoByUid(#Param("uid") String uid);
You can use the answer suggested by #jombie, and:
place the interface in a separate file, outside the entity class;
use native query or not (the choice depended on your needs);
don't override findAll() method for this purpose but use name of your choice;
remember to return a List parametrized with your new interface (e.g. List<SmallProject>).
Using Native Query:
Query query = entityManager.createNativeQuery("SELECT projectId, projectName FROM projects");
List result = query.getResultList();
public static final String FIND_PROJECTS = "select ac_year_id,ac_year from tbl_au_academic_year where ac_year_id=?1";
#Query(value = FIND_PROJECTS, nativeQuery = true)
public List<Object[]> findByAcYearId(Integer ac_year_id);
this works for me
You can update your JPARepository as below.
#Query("select u.status from UserLogin u where u.userId = ?1 or u.email = ?1 or u.mobile = ?1")
public UserStatus findByUserIdOrEmailOrMobile(String loginId);
Where UserStatus is a Enum
public enum UserStatus
{
New,
Active,
Deactived,
Suspended,
Locked
}
Use:
#Query("SELECT e FROM #{#entityName} e where e.userId=:uid")
List<ClienteEnderecoEntity> findInfoByUid(#Param("uid") UUID uid);
{
"Comments":"Why not using JDBCTemplate",
"Url":"https://www.baeldung.com/spring-jdbc-jdbctemplate"
}

JPA Criteria multiselect with fetch

I have following model:
#Entity
#Table(name = "SAMPLE_TABLE")
#Audited
public class SampleModel implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#Column(name = "NAME", nullable = false)
#NotEmpty
private String name;
#Column(name = "SHORT_NAME", nullable = true)
private String shortName;
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "MENTOR_ID")
private User mentor;
//other fields here
//omitted getters/setters
}
Now I would like to query only columns: id, name, shortName and mentor which referes to User entity (not complete entity, because it has many other properties and I would like to have best performance).
When I write query:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<SampleModel> query = builder.createQuery(SampleModel.class);
Root<SampleModel> root = query.from(SampleModel.class);
query.select(root).distinct(true);
root.fetch(SampleModel_.mentor, JoinType.LEFT);
query.multiselect(root.get(SampleModel_.id), root.get(SampleModel_.name), root.get(SampleModel_.shortName), root.get(SampleModel_.mentor));
query.orderBy(builder.asc(root.get(SampleModel_.name)));
TypedQuery<SampleModel> allQuery = em.createQuery(query);
return allQuery.getResultList();
I have following exception:
Caused by: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=generatedAlias1,role=com.sample.SampleModel.model.SampleModel.mentor,tableName=USER_,tableAlias=user1_,origin=SampleModel SampleModel0_,columns={SampleModel0_.MENTOR_ID ,className=com.sample.credential.model.User}}]
at org.hibernate.hql.internal.ast.tree.SelectClause.initializeExplicitSelectClause(SelectClause.java:214)
at org.hibernate.hql.internal.ast.HqlSqlWalker.useSelectClause(HqlSqlWalker.java:991)
at org.hibernate.hql.internal.ast.HqlSqlWalker.processQuery(HqlSqlWalker.java:759)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:675)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:311)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:259)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:262)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:190)
... 138 more
Query before exception:
SELECT DISTINCT NEW com.sample.SampleModel.model.SampleModel(generatedAlias0.id, generatedAlias0.name, generatedAlias0.shortName, generatedAlias0.mentor)
FROM com.sample.SampleModel.model.SampleModel AS generatedAlias0
LEFT JOIN FETCH generatedAlias0.mentor AS generatedAlias1
ORDER BY generatedAlias0.name ASC
I know that I can replace fetch with join but then I will have N+1 problem. Also I do not have back reference from User to SampleModel and I do not want to have..
I ran into this same issue, and found that I was able to work around it by using:
CriteriaQuery<Tuple> crit = builder.createTupleQuery();
instead of
CriteriaQuery<X> crit = builder.createQuery(X.class);
A little extra work has to be done to produce the end result, e.g. in your case:
return allQuery.getResultList().stream()
map(tuple -> {
return new SampleModel(tuple.get(0, ...), ...));
})
.collect(toList());
It's been a long time since the question was asked. But I wish some other guys would benefit from my solution:
The trick is to use subquery.
Let's assume you have Applicant in your Application entity (one-to-one):
#Entity
public class Application {
private long id;
private Date date;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "some_id")
private Applicant applicant;
// Other fields
public Application() {}
public Application(long id, Date date, Applicant applicant) {
// Setters
}
}
//...............
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Application> cbQuery = cb.createQuery(Application.class);
Root<Application> root = cbQuery.from(Application.class);
Subquery<Applicant> subquery = cbQuery.subquery(Applicant.class);
Root subRoot = subquery.from(Applicant.class);
subquery.select(subRoot).where(cb.equal(root.get("applicant"), subRoot));
cbQuery.multiselect(root.get("id"), root.get("date"), subquery.getSelection());
This code will generate a select statement for Application, and select statements for Applicant per each Application.
Note that you have to define an appropriate constructor corresponding to your multiselect.
I got the same problem using EclipseLink as the JPA provider : I just wanted to return the id of a mapped entity («User» in Gazeciarz's example).
This can be achieved quite simply by replacing (in the query.multiselect clause)
root.get(SampleModel_.mentor)
with something like
root.get(SampleModel_.mentor).get(User_.id)
Then, instead of returning all the fields of User, the request will only return the its id.
I also used a tuple query but, in my case, it was because my query was returning fileds from more than one entity.

#OneToMany which is in fact OneToOne / #Formula with Parameters

I have a Table
Products ( ProductId, Name ) and
ProductPrices (ProductId, Market, Price)
ProductPrices has a compositeKey (ProductId, Market). For a given Market, a Product has 0..1 Prices in that Market.
First approach #Formula
The Market is known at runtime, and can possibly be changed per request.
In an first attempt to model the ProductEntity I took an #Formula annotation, like so:
#Entity
#Table(...)
public class Product {
#Id
private int ProductId;
private String name;
#Formula("(SELECT TOP 1 Price FROM ProductPrices p WHERE p.ProductId = ProductId AND p.Market='Berlin')")
private double price;
}
But obviously, the market is then hard-compiled as annotations need to be static final Strings. [ so no #Formula("..." + getCurMarket() ) ].
Second approach, #OneToMany
Take a separate entity class for the prices, and reference them in the product entity as:
#OneToMany(mappedBy = "product")
private List<Price> price;
In a getPrice(), I could always return the first entry (there will never be more...) or nothing if the list is empty.
I then want to create a Predicate/Specification to use from within the ProductService. Example:
public static Specification<Product> marketEquals(final String market) {
return new Specification<Product>() {
#Override
public Predicate toPredicate(Root<Product> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
CriteriaQuery<String> q = cb.createQuery(String.class);
Root<Price> price = q.from(Price.class);
return price.get("Market").in("Berlin");
}
};
}
However, that only results in a (and I tried writing "market", "Market", ...)
org.hibernate.hql.internal.ast.QuerySyntaxException: Invalid path: 'generatedAlias1.market' [select generatedAlias0 from ...backend.entities.Product as generatedAlias0 where generatedAlias1.market in (:param0)]
Third approach, Hibernate/JPA Filter
This time, I write in the product entity
#OneToMany(mappedBy = "product")
#Filters( {
#Filter(name="marketFilter", condition="Market = :market")
} )
private List<Price> price;
Again, I want to fill this filter in the ProductService, but I cannot gelt hold of the CurrentSession. I tried the Spring-way, adding an #Autowired private SessionFactory sessionFactory; and configuring it through
Filter filter = sessionFactory.getCurrentSession().enableFilter("marketFilter");
filter.setParameter("market", "Berlin" );
but I cannot get hold of the right context, as org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.hibernate.SessionFactory
Who could advise on how to model the database schema as entities, or could point working solutions to approach 2 and 3 ? Thanks!
Second approach is actually correct.Try it changing your entities and creteria query.
Products table:
#Entity
#Table(...)
public class Product {
#Id
private int ProductId;
private String name;
#OneToMany(mappedBy = "products")
private List<ProductPrices> ProductPrices;
}
ProductPrices table:
#Entity
#Table(...)
public class ProductPrices {
#Id
private int ProductPriceId;
private String market;
private double price;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "product_id") //foreign key reference
private Product products
}
ProductService:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Product> qry = cb.createQuery(Product.class);
Root<Product> root = qry.from(Product.class);
Join<Product, ProductPrices> price = root.join("ProductPrices");
List<Predicate> conditions = new ArrayList<>();
conditions.add(cb.equal(price.get("products"), "Berlin"));
TypedQuery<Product> typedQuery = em.createQuery(qry
.select(root)
.where(conditions.toArray(new Predicate[] {}))
.orderBy(cb.asc(root.get("Berlin")))
.distinct(true)
);
return typedQuery;

Categories