Inner join using JpaRepository without writing the query - java

I have certain entities (which I've linked through Hibernate), that I'd like to query from my database; without having to explicitly write out the query.
MyParent.java
#Entity
#Table(name="myparent")
public class MyParent {
#Id
#SequenceGenerator(name = "myparent_id_seq", sequenceName = "myparent_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "myparent_id_seq")
private Long id;
#OneToOne(mappedBy = "myParent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private MyChild myChild;
public void linkChild(MyChild myChild) {
this.myChild = myChild;
myChild.setParent(this);
}
// rest of code
}
MyChild.java
#Entity
#Table(name="myChild")
public class MyChild {
#Id
#SequenceGenerator(name = "mychild_id_seq", sequenceName = "mychild_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mychild_id_seq")
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "myparent_id")
private MyParent myParent;
// rest of code
}
Now, I would like to do:
select * from myparent p
inner join mychild c on p.id = c.myparent_id;
-- basically I want to get all instances of MyParent where MyChild is not null
What would return me an instance of MyParent that I can then parse myself using my getters and setters. All I've found online are to explicitly write out the query.
Is there any way to do this?

You can use jpql:
#Query("select mp from MyParent mp where mp.myChild is not null")
Or you can use native query
#Query(value = "select p.* from myparent p inner join mychild c on p.id = c.myparent_id", nativeQuery = true)

You can do it like this:
findByMyChildIsNotNull()
See the docs, there are more keywords that you may find useful.

Related

Building the CriteriaQuery from complex SQL query

I'm new with Criteria API and struggling with building the CriteriaQuery from this simple SQL:
SELECT *
FROM user_acc u
WHERE (SELECT count(b) FROM bonus b WHERE b.employee_id = u.id) > 8;
Could anybody help me with it? I'm confused...
You can list all wanted non-aggregated columns within SELECT and GROUP BY list containing a HAVING clause :
SELECT b.br, b.employee_id
FROM user_acc u
JOIN bonus b ON b.employee_id = u.id
GROUP BY b.br, b.employee_id
HAVING count(b.br) > 8;
You need entities to use Criteria API
#Entity
#Table(name = "user_acc")
public class UserAcc{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
// getters setters
}
#Entity
#Table(name = "employee")
public class Employee{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
// getters setters
}
#Entity
#Table(name = "bonus")
public class Bonus{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne
#JoinColumn(name = "employee_id")
Employee employee;
// getters setters
}
then you can use this
public List<UserAcc> getUserAccs(EntityManager em) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<UserAcc> query = builder.createQuery(UserAcc.class);
Root<UserAcc> user = query.from(UserAcc.class);
Subquery<Bonus> subquery = query.subquery(Bonus.class);
Root<Bonus> bonus = subquery.from(Bonus.class);
Path<Long> employeeId = bonus.get("employee").get("id");
Predicate subqueryPredicate = builder.equal(user.get("id"), employeeId);
Expression<Long> bonusCount = builder.count(bonus);
subquery.select(bonusCount)
.where(subqueryPredicate)
.groupBy(employeeId)
.having(builder.greaterThan(bonusCount, 8L);
Predicate predicate = builder.exists(subquery);
query.select(user).where(predicate);
return em.createQuery(query).getResultList();
}
Final query is a bit different but result should be as expected.
I wrote this code using text editor so it needs to be tested.

How to query for all entries a WHERE "b MEMBER OF a.list"

I have an entity, let's say A, which has a field that is a list of another entity, let's say B.
This is A:
#Entity
#Table(name = "a")
public class A implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#ManyToMany
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JoinTable(name = "a_b",
joinColumns = #JoinColumn(name="as_id", referencedColumnName="id"),
inverseJoinColumns = #JoinColumn(name="bs_id", referencedColumnName="id"))
private Set<B> bs = new HashSet<>();
}
This is B:
#Entity
#Table(name = "b")
public class B implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#NotNull
#Column(name = "name", nullable = false)
private String name;
#Column(name = "description")
private String description;
}
Now, what I'm trying to achieve is to be able to query for all the entries a of A, having an entity b of B in a.bs
Tha actual query is show below:
#Repository
public interface ARepository extends JpaRepository<A, Long> {
#Query("SELECT DISTINCT a FROM A a LEFT JOIN FETCH a.bs bs WHERE :b MEMBER OF bs")
List<A> findAllByB(#Param("b") B b);
}
Wasn't it supposed to work like this?
However the error I get is the one below, I put it on pastebin due to exceeding the size limits of the post:
Log
I think you don't need join here.
Try this query:
#Query("SELECT DISTINCT a FROM A a WHERE :b MEMBER OF a.bs")
You're using MEMBER OF operator but match it with another table, not the collection itself so it leads to sql syntax error.
By default PostgreSQL create a public schema, if you don't specify schema for you entity, it will be not found when hibernate checking against public schema.
#Table(name = "b", schema = "schemaname")

Spring Data - Specification Join with hierarchy

I have problems with a specification, in which I am trying to do a left Join between two classes, that are children of two abstract classes.
I have a Hierarchy like this:
#Entity
#Table(name = "product")
public abstract class Product {
#Id
#SequenceGenerator(name = "product_seq", sequenceName = "product_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "product_seq")
#Column(updatable = false)
private Long id;
private String name;
private String brand;
}
and
#Entity
#Table(name = "product")
public class Fridge extends Product {
#Id
#SequenceGenerator(name = "product_seq", sequenceName = "product_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "product_seq")
#Column(updatable = false)
private Long id;
private String otherField;
...
}
In addition, I have other classes like this:
#Entity
#Table(name = "offer")
public abstract class Offer {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="PRODUCT_ID")
private Product product;
...
}
and
#Entity
#JsonDeserialize(using = FridgeOfferDeserializer.class)
#Table(name = "offer")
public class FridgeOffer extends Offer {
#Id
#SequenceGenerator(name = "offer_gen", sequenceName = "offer_seq", allocationSize = 1)
#GeneratedValue(strategy= GenerationType.IDENTITY, generator = "offer_gen")
private Long id;
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="PRODUCT_ID")
private Fridge product;
...
}
In my specification, I have a code like this:
#Override
public Predicate toPredicate(Root<FridgeOffer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Join<FridgeOffer, Fridge> join = root.join("product", JoinType.LEFT);
Path<String> expression = join.get(searchCriteria.getKey());
...
}
but every time that I perform a request filtering for a child's field, I have the following error:
Unable to locate Attribute with the the given name [otherField] on this ManagedType [com.fishingoffers.main.abstracts.Product]; nested exception is java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [otherField] on this ManagedType [com.fishingoffers.main.abstracts.Product]
I tried with other_field (the name of the column) but I have the same problem. The most extrange part of the message, is that hibernate seems to be doing the lookup in the Product class, instead of Fridge, that is the one that I choose in the specification. If I try with columns defined in Product, everything is OK. What am I doing wrong?

Hibernate query to Sql query

My entity of product looks like below:
#Entity
#Table(name = "order")
public class OrderEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "order_id")
private Long id;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(
name = "order_products",
joinColumns = #JoinColumn(name = "order_id", referencedColumnName = "order_id"),
inverseJoinColumns = #JoinColumn(name = "product_id", referencedColumnName = "id")
)
private Set<ProductEntity> products = new HashSet<>();
}
ProductEntity:
#Entity
#Table(name = "product")
public class ProductEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(unique = true)
private String name;
#ManyToMany(mappedBy = "products")
private Set<OrderEntity> orders = new HashSet<>();
}
I want to get all orders where product name is equal to wanted value. And I write sql query to get result from database, but I cannot write hibernate query for Spring Data JPA.
My query for postgreSQL looks like this:
SELECT o.order_id, op.product_id, p.name
FROM public.order o
INNER JOIN public.order_products op
ON p.order_id = op.product_id
INNER JOIN public.product p
ON op.product_id = p.id
WHERE p.name = 'Foo';
And this query return me an id of order, product_id and name of product. And this works. But I didn't know how to write this question as spring query using #Query.
I need a metohod in my repository:
#Repository
public interface OrderRepository extends JpaRepository<OrderEntity, Long> {
#Query("") <- place for my query in Hibernate sql
List<OrderEntity> findAllByProductName(#Param("name") String name);
}
try this: (it returns full OrderEntity objects )
#Query("select o from OrderEntity o join o.products prod where prod.name = :name")
List<OrderEntity> findAllByProductName(#Param("name") String name);
if you need fetch eager all data for products use : ....OrderEntity o join o.products... in query instead of OrderEntity o join o.products
This is a projection consisting of columns from many entties, so you would have to go for the Result Class strategy.
Basically, you create a POJO class with expected result fields an an equivalent constructor:
public class ResultClass{
private Integer orderId;
private Integer productId;
private String name;
public ResultClass(Integer orderId, Integer productId, String name){
// set the fields
}
}
Then you alter the query a bit:
SELECT new com.mypkg.ResultClass(o.order_id, op.product_id, p.name)
FROM public.order o
INNER JOIN public.order_products op
ON p.order_id = op.product_id
INNER JOIN public.product p
ON op.product_id = p.id
WHERE p.name = 'Foo';
And change the return type on the interface method:
#Repository
public interface OrderRepository extends JpaRepository<OrderEntity, Long> {
#Query("...")
List<ResultClass> findAllByProductName(#Param("name") String name);
}

SQL Query Too Complex To Express In JPA Criteria API?

I have an SQL query that gets me exactly the data I need. The problem is that we are trying to express all queries in JPA Criteria API to maintain portability, and I can't figure out how to map this particular query.
The problem is that the JPA Criteria API Subquery class lacks the multiselect() method that CriteriaQuery class has. As you can see in the SQL query, I have computed fields in the sub-query which don't exist in the entity. Thus, I have no way to retrieve these fields.
I would be quite appreciative if anyone knows a solution or could offer guidance, or even if someone could validate that what I am trying to achieve in JPA Criteria API is not possible.
The SQL:
SELECT w.NAME AS 'wave_name',
Count(*) AS 'num_lines',
Sum(qty_ordered) AS 'num_units',
Count(DISTINCT unit_of_work_id) AS 'num_units_of_work',
Sum(completed_units) AS 'completed_units',
( Sum(completed_units) + Sum(qty_scratched) ) / Sum(qty_ordered) AS 'perc_completed_units'
FROM (SELECT t.id,
t.wave_id,
t.quantity_requested AS 'qty_ordered',
t.quantity_scratched AS 'qty_scratched',
t.unit_of_work_id AS 'unit_of_work_id',
Ifnull(m.quantity, 0) AS 'qty_picked',
CASE
WHEN Ifnull(m.quantity, 0) > quantity_requested THEN
quantity_requested
ELSE Ifnull(m.quantity, 0)
END AS 'completed_units'
FROM task t
LEFT OUTER JOIN (SELECT move.task_id,
Sum(quantity) AS 'quantity'
FROM move
GROUP BY task_id) m
ON m.task_id = t.id) s
JOIN wave w
ON w.id = s.wave_id
GROUP BY w.name;
The entities:
#Entity
#Table(name = "task")
public class Task {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private Long id;
#ManyToOne (cascade = CascadeType.ALL)
#JoinColumn (name = "wave_id", nullable = false)
private Wave wave;
#ManyToOne (cascade = CascadeType.ALL)
#JoinColumn (name = "unit_of_work_id", nullable = false)
private UnitOfWork unitOfWork;
#OneToMany (cascade = CascadeType.ALL, mappedBy = "task")
private Set<Move> moves = new HashSet<Move>();
#Column (name = "quantity_requested")
private Long quantityRequested;
#Column (name = "quantity_scratched")
private Long quantityScratched;
}
#Entity
#Table(name = "wave")
public class Wave {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "wave", cascade = CascadeType.ALL)
private Set<Task> tasks = new HashSet<Task>();
}
#Entity
#Table(name = "unit_of_work")
public class UnitOfWork {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
#Column (name = "id")
private Long id;
#OneToMany(mappedBy = "unitOfWork", cascade = CascadeType.ALL)
private Set<Task> tasks = new HashSet<Task>();
}
#Entity
#Table(name = "move")
public class Move {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private Long id;
#ManyToOne (cascade = CascadeType.ALL)
#JoinColumn (name = "task_id", nullable = false)
private Task task;
#Column (name = "quantity")
private Long quantity;
}
I would say use named parameters or native query approach for this.
For example:
Named parameters:
public interface Repo extends JpaRepository<AEntity, String> {
#Query("select a from AEntity a where a.BEntity.name = :name")
public aMethod( #Param("name") String name)
}
OR
Native query approach:
public interface Repo extends JpaRepository<AEntity, String> {
#Query(value = "select * from Tablename t where t.name = :name", nativeQuery=true)
public aMethod(#Param("name") String name)
}
Check this link if you are using spring jpa
http://docs.spring.io/spring-data/data-jpa/docs/1.4.x/reference/htmlsingle/#jpa.named-parameters

Categories

Resources