Hibernate and mssql inner join rowcount - java

I am struggling with Hibernate Criteria. My aim is to create the following request using Hibernate Criteria :
select
count(*) as y0_
from
PInterface this_
inner join
Product product2_
on this_.product_id=product2_.id
where
this_.product_interface_type_id=?
Here is my code:
#Entity #Table(name = "PInterface")
public class PInterface {
#Id
#GeneratedValue
#Column(name = "id", nullable = false, insertable = false, updatable = false)
private int id;
#Column(name = "product_id")
private int productId;
#Column(name = "product_interface_type_id")
private int type;
#ManyToOne(optional=false)
#JoinColumn(name = "product_id", referencedColumnName = "id", insertable=false, updatable=false)
private Product product;
}
#Entity #Table(name = "Product")
public class Product {
#Id
#GeneratedValue
#Column(name = "id", nullable = false, insertable = false, updatable = false)
private int id;
private String name;
}
//Criteria is :
Object criteria = sessionFactory.getCurrentSession()
.createCriteria(PInterface.class)
.add(Restrictions.eq("type", 1))
.setProjection(Projections.rowCount())
.uniqueResult()
;
However, the results ...
select
count(*) as y0_
from
PInterface this_
where
this_.product_interface_type_id=?
Where Inner join?
Thank you for help!

How about this:
Object criteria = sessionFactory.getCurrentSession()
.createCriteria(PInterface.class)
.createCriteria("product")
.createAlias("product", "p")
.add( Restrictions.eqProperty("p.id", "productId") )
.add(Restrictions.eq("type", 1))
.setProjection(Projections.rowCount())
.uniqueResult();

Related

Passing Queries from Postgres to JPA

I'm newbie
I'm trying pass this Postgres query to JPA/JPQL
SELECT
DISTINCT(srv.code) AS Serv_Cod,
srv.id AS Serv_id,
srv.description AS Serv_Desc
FROM db.Category AS cat
join db.Classification AS cla ON cat.id = cla.cat_id
join db.Service AS srv ON srv.id = cla.srv_id
WHERE cat.id = 10
ORDER BY srv.id;
Now I want to write the same Query, I have the Entities with the same name Table.
Classification
#Entity
#Table(name = "Classification", schema = "db")
#Audited
public class Classification implements Identifiable<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "srv_id", nullable = true)
private Service service;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "cat_id", nullable = true)
private Category category;
....
}
Service
#Entity
#Table(name = "Service", schema = "db")
#Audited
public class Service implements Identifiable<Long> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "code", nullable = false)
private String code;
#Column(name = "description", nullable = false)
private String description;
....
}
I was reading, but I'm very confused...
I don't know how to write the ON for the JOIN, and establish the DISTINCT for a Column/Field.
Long myID = 25L;
this.em.createQuery("SELECT NEW SomeDto(srv.id, srv.code, srv.description)"
+ " FROM Classification cla"
+ "JOIN cla·cat_id cat"
+ "JOIN cla·srv_id srv"
+ "WHERE cat.id = :id"
,BaseObjectDto.class).setParameter("id", myID).getResultList();
Thank you for you valuable Help.
The query is very simple. When you have ToOne relationships you can navigate to the related entity. There is no need to JOIN ON.
Even with ToMany there is no need for the ON because this is already defined in the mapping.
So the query will look like:
SELECT NEW SomeDto(cla.service.id, cla.service.code, cla.service.description)
FROM Classification cla
WHERE category.id = :id

Hibernate two ManyToOne relations on one Table, the first gets Eager and the second LAZY loaded

I have got the following Entities, an item which can has up to two categories, a primary and a secondary.
Both categories are mapped ManyToOne to the category table using a JoinColumnsOrFormulas.
The first one gets fetched EAGER as expected, but the second one does not occur in the SQL statement and gets lazy loaded.
This lazy loading results in a classical n+1 problem.
This is my item entity with the both category entities which should gets joined:
#Entity
#Table(name = "item", schema = "public", catalog = "stackoverflow_question")
#DynamicUpdate
public class Item extends StackOverflowQuestionEntity {
#Id
#Column(name = "id")
protected Long id;
#Column(name = "site")
private String site;
#ManyToOne
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(formula = #JoinFormula(value = "site", referencedColumnName = "site")),
#JoinColumnOrFormula(formula = #JoinFormula(value = "primary_category_id", referencedColumnName = "category_id"))
})
private Category primaryCategory;
#Column(name = "primary_category_id")
private Long primaryCategoryId;
#ManyToOne
#JoinColumnsOrFormulas({
#JoinColumnOrFormula(formula = #JoinFormula(value = "site", referencedColumnName = "site")),
#JoinColumnOrFormula(formula = #JoinFormula(value = "secondary_category_id", referencedColumnName = "category_id"))
})
private Category secondaryCategory;
#Column(name = "secondary_category_id")
private Long secondaryCategoryId;
}
This is the category entity:
#Entity
#Table(name = "category", schema = "public", catalog = "stackoverflow_question")
public class Category extends StackOverflowQuestionEntity {
#Column(name = "category_id")
private Long categoryId;
#Column(name = "name")
private String name;
#Column(name = "site")
private String site;
}
The resulting query contains only the primary category:
SELECT this_.id AS id1_9_9_,
this_.inserted AS inserted2_9_9_,
this_.updated AS updated3_9_9_,
this_.primary_category_id AS formula174_9_,
this_.secondary_category_id AS formula176_9_,
category2_.id AS id1_0_0_,
category2_.inserted AS inserted2_0_0_,
category2_.updated AS updated3_0_0_,
category2_.name AS name7_0_0_
FROM public.item this_
LEFT OUTER JOIN public.category category2_ ON this_.site=category2_.site
AND this_.primary_category_id=category2_.category_id
WHERE True;
Hence the secondary category get joined lazy:
SELECT category0_.id AS id1_0_0_,
category0_.inserted AS inserted2_0_0_,
category0_.updated AS updated3_0_0_,
category0_.name AS name4_0_0_,
category0_.site AS site5_0_0_
FROM public.category category0_
WHERE category0_.site=?
AND category0_.category_id=?;
Why is Hibernate joining the secondary category lazy, the annotations seems the be the same.
The hibernate version I am using is 5.0.10.Final.
This is how the base entity looks like:
#MappedSuperclass
abstract public class StackOverflowQuestionEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, insertable = true, updatable = false, nullable = false)
protected Long id;
#Type(type="LocalDateTime")
#Column(name = "created", nullable = false, insertable = true, updatable = false)
protected LocalDateTime created;
#Type(type="LocalDateTime")
#Column(name = "refreshed", nullable = false, insertable = true, updatable = true)
protected LocalDateTime refreshed;
#PreUpdate
protected void onUpdate() {
refreshed = now();
}
#PrePersist
protected void onCreate() {
created = refreshed = now();
}
}
Here is an example "query", as said I am using hibernate criteria as well as HQL, the problem occurs with both methods.
session
.createCriteria(Item.class)
.add(eq("id", id))
.uniqueResult();
With standard JPA annotations it would look like this (updated):
#ManyToOne
#JoinColumns({
#JoinColumn(name="site", referencedColumnName="site", insertable = false, updatable = false),
#JoinColumn(name="primary_category_id", referencedColumnName="category_id", insertable = false, updatable = false)
})
private Category primaryCategory;
#ManyToOne
#JoinColumns({
#JoinColumn(name="site", referencedColumnName="site", insertable = false, updatable = false),
#JoinColumn(name="secondary_category_id", referencedColumnName="category_id", insertable = false, updatable = false)
})
private Category secondaryCategory;
UPDATE: I found that the second select statement is generated only when you use join by a composite key: Hibernate tries to resolve associations for {site=site, id=null} using TwoPhaseLoad. But if you write
#ManyToOne
#JoinColumn(name="secondary_category_id")
private Category secondaryCategory;
and secondary_category_id is null then the only one select statement will be generated, and the secondaryCategory value will be null. Maybe it will help you somehow. For example, you could add a constraint on site field while building your criteria:
Category c = (Category) session.createCriteria(Category.class)
.add(Restrictions.eq("id", 1L)) // for example
// here you define additional restriction on site field
.createAlias("secondaryCategory", "sc", JoinType.LEFT_OUTER_JOIN, Restrictions.sqlRestriction("this_.site = {alias}.site"))
.uniqueResult();
I did a quick test using your classes, and the following query code (using JPA criteria queries rather than native Hibernate)
CriteriaQuery<Item> cq = em.getCriteriaBuilder().createQuery(Item.class);
EntityGraph<Item> entityGraph = em.createEntityGraph(Item.class);
entityGraph.addSubgraph("primaryCategory", Category.class);
entityGraph.addSubgraph("secondaryCategory", Category.class);
List<Item> items = em.createQuery(cq.select(cq.from(Item.class)))
.setHint("javax.persistence.loadgraph", entityGraph)
.getResultList();
results in the following SQL being generated (formatted for readability):
select item0_.id as id1_1_0_,
category1_.id as id1_0_1_,
category2_.id as id1_0_2_,
item0_.site as site4_1_0_,
item0_.primary_category_id as primary_2_1_0_,
item0_.secondary_category_id as secondar3_1_0_,
category1_.category_id as category2_0_1_,
category1_.name as name3_0_1_,
category1_.site as site4_0_1_,
category2_.category_id as category2_0_2_,
category2_.name as name3_0_2_,
category2_.site as site4_0_2_
from item item0_
left outer join category category1_
on item0_.site=category1_.site
and item0_.secondary_category_id=category1_.category_id
left outer join category category2_
on item0_.site=category2_.site
and item0_.primary_category_id=category2_.category_id
As you can see, both category tables are being joined in the same SELECT
Try the following solution:
#Entity
#Table(name = "item", schema = "public", catalog = "stackoverflow_question")
#DynamicUpdate
public class Item {
#ManyToOne
#JoinColumn(name="site")
private Category primaryCategory;
#ManyToOne
#JoinColumn(name="site")
private Category secondaryCategory;
}
#Entity
#Table(name = "category", schema = "public", catalog = "stackoverflow_question")
public class Category {
#OneToMany(targetEntity=Item.class, mappedBy="primaryCategory", cascade=CascadeType.ALL)
private List<Item> primaryCategoryList;
#OneToMany(targetEntity=Item.class, mappedBy="secondaryCategory", cascade=CascadeType.ALL)
private List<Item> secondaryCategoryList;
}

JPQL join condition in linked Entity class

I'm using the following SQL to join employees, user_projects and project_master
SELECT DISTINCT usr.user_number,
emp.emp_name
FROM employees emp
LEFT JOIN user_projects usr
ON (emp.user_number = usr.user_number)
JOIN project_master mast
ON (usr.project_id = mast.project_id)
WHERE mast.active = 'Y'
AND emp.user_number = 'SMITH'
In Employee entity, I have the following JPQL defined as namedQuery
#NamedQuery(name = "Employee.findProjects", query = " select DISTINCT u.userNumber,e.empName" +
" from Employee e LEFT JOIN e.userProjectsList u where e.userNumber='SMITH' ")
Not sure how to link UserProjects and ProjectMaster to create a where condition
mast.active = 'Y' in Employee entity's findProjects namedQuery
How to join UserProjects and ProjectMaster in Employee Entity class?
Entities
Entity Employee
#Table(name = "EMPLOYEES")
public class Employee implements Serializable {
#Id
#Column(name = "USER_NUMBER", nullable = false)
private String userNumber;
#Column(name = "EMP_NAME")
private String empName;
#OneToMany(mappedBy = "employee")
private List<UserProjects> userProjectsList;
Entity UserProjects
#Table(name = "USER_PROJECTS")
public class UserProjects implements Serializable {
#Id
#Column(name="PROJECT_ID", nullable = false, insertable = false,
updatable = false)
private String projectId;
#Id
#Column(name="USER_NUMBER", nullable = false, insertable = false, updatable = false)
private String userNumber;
#ManyToOne
#JoinColumn(name = "PROJECT_ID", referencedColumnName = "PROJECT_ID")
private ProjectMaster projectMaster;
Entity ProjectMaster
#Table(name = "PROJECTMASTER")
public class ProjectMaster implements Serializable {
#Id
#Column(name="PROJECT_ID", nullable = false)
private String projectId;
#Column(name="PROJECT_DESCRIPTION")
private String projectDesc;
#Column(name="ACTIVE")
private String active;
select DISTINCT u.userNumber,e.empName from Employee e LEFT JOIN
e.userProjectsList u JOIN u.projectMaster pm
where e.userNumber='SMITH' AND pm.active='Y'
Isn't this work?

Hibernate query causing lots extra unnecassery queries

I am developing a auction site. The problem lies in 3 entities i use:
Product (has zero or many ProductBid)
ProductBid (has zero or one ProductBidRejection)
ProductBidRejection
I use a hibernate query to get the bids:
select pb from ProductBid pb left join pb.rejection pbr where pbr is null and pb.product = :product order by pb.amount desc
This generates this query (via console):
select
productbid0_.id as id4_,
productbid0_.amount as amount4_,
productbid0_.bid_by as bid4_4_,
productbid0_.date as date4_,
productbid0_.product_id as product5_4_
from
product_bids productbid0_
left outer join
product_bid_rejections productbid1_
on productbid0_.id=productbid1_.product_bid_id
where
(
productbid1_.id is null
)
and productbid0_.product_id=?
But for each bid it gets it also generates:
select
productbid0_.id as id3_1_,
productbid0_.date_rejected as date2_3_1_,
productbid0_.product_bid_id as product4_3_1_,
productbid0_.reason as reason3_1_,
productbid0_.rejected_by as rejected5_3_1_,
productbid1_.id as id4_0_,
productbid1_.amount as amount4_0_,
productbid1_.bid_by as bid4_4_0_,
productbid1_.date as date4_0_,
productbid1_.product_id as product5_4_0_
from
product_bid_rejections productbid0_
inner join
product_bids productbid1_
on productbid0_.product_bid_id=productbid1_.id
where
productbid0_.product_bid_id=?
These are my entities:
ProductBid
#Entity
#Table(name = "product_bids")
public class ProductBid
{
#Column(name = "id", nullable = false)
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#JoinColumn(name = "product_id", nullable = false)
#Index(name="product")
#ManyToOne(fetch = FetchType.LAZY)
private Product product;
#Column(name = "amount", nullable = false)
private BigDecimal amount;
#JoinColumn(name = "bid_by", nullable = false)
#Index(name="bidBy")
#ManyToOne(fetch = FetchType.LAZY)
#Fetch(FetchMode.JOIN)
private User bidBy;
#Column(name = "date", nullable = false)
#Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
private DateTime date;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "productBid")
private ProductBidRejection rejection;
}
ProductBidRejection
#Entity
#Table(name = "product_bid_rejections")
public class ProductBidRejection
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private long id;
#Column(name = "reason", nullable = false, columnDefinition = "TEXT")
private String reason;
#Column(name = "date_rejected", nullable = false)
#Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
private DateTime dateRejected;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "rejected_by", nullable = false)
private User rejectedBy;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "product_bid_id", nullable = false)
#Fetch(FetchMode.JOIN)
private ProductBid productBid;
}
Its because you have #Fetch(FetchMode.JOIN) on ProductBid.
So for each of the ProductBidRejections you retrieve, it also loads a ProductBid.
UPDATE
Try this query. It will get distinct pb and eagerly fetch the PBR
select distinct pb from ProductBid pb left join fetch pb.rejection pbr where pbr is null and pb.product = :product order by pb.amount desc
Use Criteria instead of HQL your problem will be solve
session.createCriteria(ProductBid.class).add(Restrictions.eq("product",yourproduct)).list();
and in ProductBid Entity Class use annotation to join EAGER ly to ProductBidRejection

Java Persistence Query Language JOIN

I have two tables :
A(bigint id, ...)
B(bigint id, varchar name, bigint id_A)
and now I want get all rows from A which exists in B (and those rows in B have name eg Andy)
Plase help me create dynamic query
class A
#Entity
#Table(name = "A", schema = "mySchema")
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public class A{
#Id
private Long id;
}
class B
#Entity
#Table(name = "B",
schema = "mySchema",
uniqueConstraints = { #UniqueConstraint(columnNames = {
"some_id", "id_A" }) })
public class B{
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "Seq")
#SequenceGenerator(name = "Seq", sequenceName = "mySchema.mySeq")
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_A", nullable = false)
private A a;
#Column(name = "id_A", updatable = false, insertable = false)
private Long IdA;
}
There are several weird parts. e.g. this:
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_A", nullable = false)
private A a;
#Column(name = "id_A", updatable = false, insertable = false)
private Long IdA;
With the #JoinColumn annotation you are telling the JPA provider that it should use the specified column for internal mapping, but with the IdA field, you are trying to manage the column yourself. Which is it going to be?

Categories