ManyToMany join Not IN - many-to-many

I have these entities:
In my TestType entity:
#ManyToMany(mappedBy = "testTypes")
private Set<Test> tests = new HashSet<Test>();
In my Test entity
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name="Test_TestType", joinColumns = { #JoinColumn(name = "test_id", referencedColumnName = "test_id") }, inverseJoinColumns = { #JoinColumn(name = "testtype_id", referencedColumnName = "testtype_id") })
#OrderBy("shortName ASC")
private Set<TestType> testTypes = new HashSet<TestType>();
I need to write a query to return all TestTypes not associated to a given Test.
I have this query:
#Query("select new org.company.IdValue(d.id, d.name) from TestType d where d.id NOT IN (select t.id from Test_TestType t JOIN t.tests test where test.id != :id)")
But, getting the error: Test_TestType is not mapped.
Ideas?
thanks

JPA queries are done on entities, not on tables. The join table Test_TestType is not an entity, so it cannot be explicitely accessed in a JPA query.
You could try a query like this one (not 100% sure if it works). There should be an additional join on TestType in the subselect, instead of just joining Test_TestType to Test, but this is a normal behavior for JPA queries).
#Query("select new org.company.IdValue(d.id, d.name) from TestType d "
+ "where d.id NOT IN ("
+ "select distinct t.id from TestType t JOIN t.tests test where test.id != :id)")

Related

How to select from a table using HQL that are unique by one parameter?

I need to select all unique rows from Feed table by reference_id (ref_id)
I wrote a request, but the request does not return unique values, but gives all the rows include duplicate rows with the same ref_id.
So select doesn't work in right way
What is expected:
#Query("SELECT o FROM FeedEntity o WHERE o.referenceId IN (SELECT DISTINCT ol.referenceId FROM FeedEntity ol) AND o.id < :lastId")
List<FeedEntity> getByIdBefore(#Param("lastId") Long lastId, Pageable pageable);
This is the class that mapped for request:
public class FeedEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private UserEntity user;
#Enumerated
#Column(name = "type")
private FeedType type;
#Column(name = "ref_id")
private Long referenceId;
#Column(name = "create_time")
private Timestamp createTime;
public FeedEntity(UserEntity user, FeedType type, Long referenceId) {
this.user = user;
this.type = type;
this.referenceId = referenceId;
this.createTime = DateTimeUtil.getCurrentTimestamp();
}
}
Native query is working
SELECT * FROM feed
WHERE id IN (
SELECT DISTINCT MIN(Id)
FROM feed fe
GROUP BY fe.ref_id
)
I hope you do realise you posted both the question and the answer
This
SELECT o FROM FeedEntity o
WHERE o.referenceId IN
(SELECT DISTINCT ol.referenceId
FROM FeedEntity ol)
AND o.id < :lastId
is not the same as
SELECT * FROM feed
WHERE id IN (
SELECT DISTINCT MIN(Id)
FROM feed fe
GROUP BY fe.ref_id
)
Why would you expect the same results.
You already have the query, just use it. The only difference I suggest based on the screenshot you provided is to swap out MIN for MAX as you wanted id 654 and 655 in your result while the MIN version would return 622 and 655.
#Query("SELECT o FROM FeedEntity o WHERE o.id IN (SELECT MAX(ol.id) FROM FeedEntity ol GROUP BY ReferenceId) AND o.id < :lastId")
List<FeedEntity> getByIdBefore(#Param("lastId") Long lastId, Pageable pageable);

Can somebody help to translate this MySql query to JPQL or HQL?

Don't forget to check the image below
Objects:
1) Question
2) Tags
The relationship is ManyToMany
public class Question {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "question_has_tag",
joinColumns = #JoinColumn(name = "question_id"),
inverseJoinColumns = #JoinColumn(name = "tag_id"))
private List<Tag> tags;
public class Tag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Column
private String name;
#ManyToMany(mappedBy = "tags", fetch = FetchType.LAZY)
#ContainedIn
private List<Question> questions;
The MySql query is following:
select t.id, t.name, count(question_has_tag.tag_id) as i from tag as t join question_has_tag
on id = question_has_tag.tag_id group by id order by i desc;
Need to translate to JPQL or HQL
The main goal is to get list of the most frequency tags like:enter image description here
Try:
Query q = entityManager.createQuery("select t.id, t.name, count(t.id) from Tag t join t.questions group by t.id,t.name");
List<Object[]> res = q.getResultList();
for(Object[] row:res) {
System.out.println(row[0]+" "+row[1]+" "+row[2]);
}
The given JPA query is translated to SQL (by Hibernate) as:
select tag0_.id as col_0_0_, tag0_.name as col_1_0_, count(tag0_.id) as col_2_0_ from Tag tag0_ inner join question_has_tag questions1_ on tag0_.id=questions1_.tag_id inner join Question question2_ on questions1_.question_id=question2_.id group by tag0_.id , tag0_.name

Inner join on two tables which are not mapped using HQL

I have table A and table b having no relationship. But table A, table B mapped with entity, and there's no relationship between the entities.
Assume tables are mapped as below.
#Entity
#Table(name = "tableA")
public class TableA
#Entity
#Table(name = "tableB")
public class TableB
table A
ida,
ref_no,
schema,
type
table B
idb,
accountno,
sechema,
type
I need to get the "accountno" from tableB for the given "ref_no" which having same "sechema" and "type". I can get the "accountno" using the below native SQL query in my repository class which implements CrudRepository.
#Query(value = "SELECT b.accountno FROM DB.tableA as a INNER JOIN DB.tableB as b ON b.sechema = a.sechema AND b.type = a.type WHERE a.ref_no= ?1", nativeQuery = true)
Integer findByRefNo(String refNo);
Could someone help me to overcome this problem, because using native query, I need to change if DB name different.
I have tried with below but it gave me errors.
#Query(value = select b.accountno from TableA a join TableB b where b.sechema = a.sechema and b.type = a.type and (:refNo is null or a.refNo = :refNo)")
Integer findByRefNo(#Param("refNo") String refNo);
If you are using Hibernate 5.1+ the query will be almost the same in HQL:
#Query("select b.accountno from TableA a join TableB b on b.sechema = a.sechema and b.type = a.type where a.ref_no= ?1")
Integer findByRefNo(String refNo);
More info: 1, 2

Spring JPA Many To Many Relationship Slow Loading

I have two models - Product and Category. There is a relation between them - m:n. How I defined the relation at the Product side:
#JoinTable(
name = "products_categories",
joinColumns = #JoinColumn(
name = "product_id",
referencedColumnName = "id"
),
inverseJoinColumns = #JoinColumn(
name = "category_id",
referencedColumnName = "id"
)
)
#ManyToMany(fetch = FetchType.EAGER)
private Set<Category> categories;
When I try to load only one record (findById) it sends only one SQL Query and it's what I want to achieve. But when I request to load more than one it sends different SQL Query for every single product. How can I fix that problem? Should I write my own SQL Query (using EntityManager)?
Using EntityManager you can use LEFT JOIN FETCH to overcome the SELECT N+1 issue.
#Autowired EntityManager entityManager;
#Test
public void findWithEntityManager() {
Query q = entityManager.createQuery("SELECT p FROM ProductEntity p LEFT JOIN FETCH p.categories");
List<ProductEntity> prods = q.getResultList();
prods.forEach(p -> {
System.out.println(p.getCategories().size());
});
}
//Hibernate: select productent0_.id as id1_4_0_, ... from product productent0_ left outer join product_category categories1_ on productent0_.id=categories1_.ProductEntity_id left outer join category categoryen2_ on categories1_.categories_id=categoryen2_.id
===EDIT===
Strangely enough, using the same query with #Query returning Page<T> results in exception:
#Query("SELECT p FROM ProductEntity p LEFT JOIN FETCH p.categories c")
Page<ProductEntity> findAllLeftJoin(Pageable pageable);
// Caused by: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list ...
However using a List<T> it works fine but then I need to handle pagination manually.
#Query("SELECT p FROM ProductEntity p LEFT JOIN FETCH p.categories c")
List<ProductEntity> findAllLeftJoin();
// Hibernate: productent0_.id as id1_4_0_,... from product productent0_ left outer join product_category categories1_ on productent0_.id=categories1_.ProductEntity_id left outer join category categoryen2_ on categories1_.categories_id=categoryen2_.id

eclipselink with join and pagination

Currently i am stuck in generating the query via eclipselink for the following relation schema.
Manager can have zero or more employees.(Manager can exist without any employee)
Employee will belong to exactly one manager.(Employee is always mapped to a manager)
public class Manager {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private String id;
#OneToMany(mappedBy = "manager")
private List<Employee> employee;
private String department;
}
public class Employee {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private String id;
#ManyToOne
#JoinColumn(name="managerId")
private Manager manager;
private String status;
}
How can i form the left outer join query in eclipselink containing where conditions on either Manager or Employee or both?
eg:
SELECT m.id FROM Manager m LEFT OUTER JOIN Employee e ON m.id = e.managerId
WHERE e.id is null or m.department like 'Dev%' AND e.status = 'ACTIVE'
GROUP BY m.id
ORDER BY m.id
LIMIT 10
JPQL query is almost the same. You are just not working with tables but with entities:
SELECT m FROM Manager m
LEFT JOIN m.employee e
ON condition is already defined in mapping annotation so it's skipped. You can also use 'LEFT JOIN FETCH' to fetch objects trough lazy relationships.
also check https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#LEFT_JOIN
You can specify JPQL query as named query in your enity:
#Entity
...
#NamedQueries({
#NamedQuery(
name="findAllEmployeesOrderById",
query="SELECT e FROM Employee e order by e.id"
),
#NamedQuery(
name="findAllEmployeesJoinAddressPhones",
query="SELECT e FROM Employee e",
hints={
#QueryHint(name=QueryHints.FETCH, value="e.address"),
#QueryHint(name=QueryHints.FETCH,
value="e.phoneNumbers")
}
)
})
...
public class MyEntity { ...