How to Use Multiple Join on Hibernate? - java

I have these following Classes:
class Person(){
#OneToMany(mappedBy="person")
private List<PersonRoles> roles;
}
class PersonRoles(){
#ManyToOne
#JoinColumn(name = "person_id", nullable = false)
private Person person;
#ManyToOne
#JoinColumn(name = "request_id")
private Request request;
}
class Request(){
#OneToMany(mappedBy="request")
private List<PersonRoles> roles;
}
Now I am going to fetch all person based on a given request id and his roles by using hibernate and inner join but my log is telling me that my table doesn't exist. This is my query so far:
sql = "SELECT p.* FROM person AS p INNER JOIN p.roles ON p.roles.personId = p.id
INNER JOIN request AS r ON p.roles.requestId = r.id AND p.roles.role like :item
AND r.id = :id";
query = session.createSQLQuery(sql);
query.addEntity(Person.class);
query.setParameter("item", "Members");
query.setParameter("id", id);
person = (Person) query.uniqueResult();
and this is what i received on the log:
Table 'p.roles' doesn't exist
Did i forget some hibernate annotation? or My query has something wrong?

Brief reason
your syntax of SQL is wrong
Detailed explanation
here is the syntax of inner join example
SELECT column_name(s)
FROM table1
INNER JOIN table2
ON table1.column_name = table2.column_name;
for multiple inner join
SELECT *
FROM table1
INNER JOIN table2
ON table1.primaryKey=table2.table1Id
INNER JOIN table3
ON table1.primaryKey=table3.table1Id
but you have used INNER JOIN p.roles there should be a table name after the INNER JOIN, not a column name.
that's why you got an error, moreover, use HQL instead of SQL in hibernate it is a good practice.
happy coding!

Related

Hibernate failing to parse native query containing STRAIGHT_JOIN keyword

I have a native sql query used inside #Formula annotation in a hibernate entity, but Hibernate is failing to parse it, and throws a syntax error exception.
The entity/query is something similar to the following:
#Table(name="table2")
public class Table2{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Formula("(SELECT MAX(table1.col1) from table1 STRAIGHT_JOIN table2 t ON"
+ "(table1.col2 = t.col2) INNER JOIN table3 AS t3 ON (t3.col1 = t.col1)
WHERE t3.col2 = 1)")
private Integer code;
#Column
private Type type;
....
//getters and setters
....
}
On looking at the mysql query logs, following is the actual query being executed:
select table2x0_.id, table2x0_.type, (SELECT MAX(table1.col1) from table1
STRAIGHT_JOIN table2x0_.table2 table2x0_.t ON (table1.col2 = t.col2) INNER JOIN
table3 AS t3 ON (t3.col1 = t.col1) WHERE t3.col2 = 1) as formula1_ from table2
table2x0_ where table2x0_.type in (0,1,2);
It appears that hibernate is having issues with the STRAIGHT_JOIN keyword. Instead of STRAIGHT_JOIN table2 t, the query is being written as STRAIGHT_JOIN table2x0_.table2 table2x0_.t, i.e., it is prefixing the outer query's table alias.
If I replace STRAIGHT_JOIN with INNER JOIN, the query works fine. But I need to use STRAIGHT_JOIN for my use case.
Can someone please help with how to resolve it?

JPQL include elementCollection map in select statement

I have an #Entity class Company with several attributes, referencing a companies Table in my db. One of them represents a Map companyProperties where the companies table is extended by a company_properties table, and the properties are saved in key-value format.
#Entity
#Table(name = "companies")
public class Company extends AbstractEntity {
private static final String TABLE_NAME = "companies";
#Id
#GeneratedValue(generator = TABLE_NAME + SEQUENCE_SUFFIX)
#SequenceGenerator(name = TABLE_NAME + SEQUENCE_SUFFIX, sequenceName = TABLE_NAME + SEQUENCE_SUFFIX, allocationSize = SEQUENCE_ALLOCATION_SIZE)
private Long id;
//some attributes
#ElementCollection
#CollectionTable(name = "company_properties", joinColumns = #JoinColumn(name = "companyid"))
#MapKeyColumn(name = "propname")
#Column(name = "propvalue")
private Map<String, String> companyProperties;
//getters and setters
}
The entity manager is able to perform properly find clauses
Company company = entityManager.find(Company.class, companyId);
However, I am not able to perform JPQL Queries in this entity and retrieve the Map accordingly. Since the object is big, I just need to select some of the attributes in my entity class. I also do not want to filter by companyProperties but to retrieve all of them coming with the proper assigned companyid Foreign Key. What I have tried to do is the following:
TypedQuery<Company> query = entityManager.createQuery("SELECT c.id, c.name, c.companyProperties " +
"FROM Company as c where c.id = :id", Company.class);
query.setParameter("id", companyId);
Company result = query.getSingleResult();
The error I get is:
java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Problem compiling [SELECT c.id, c.name, c.companyProperties FROM Company as c where c.id = :id]. [21, 40] The state field path 'c.companyProperties' cannot be resolved to a collection type.
org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1616)
org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1636)
com.sun.enterprise.container.common.impl.EntityManagerWrapper.createQuery(EntityManagerWrapper.java:476)
Trying to do it with joins (the furthest point I got was with
Query query = entityManager.createQuery("SELECT c.id, c.name, p " +
"FROM Company c LEFT JOIN c.companyProperties p where c.id = :id");
does not give me either the correct results (it only returns the value of the property and not a list of them with key-value).
How can I define the right query to do this?
Your JPA syntax looks off to me. In your first query you were selecting individual fields in the Company entity. But this isn't how JPA works; when you query you get back the entire object, with which you can access any field you want. I propose the following code instead:
TypedQuery<Company> query = entityManager.createQuery("from Company as c where c.id = :id", Company.class);
query.setParameter("id", companyId);
Company result = query.getSingleResult();
Similarly, for the second join query I suggest the following code:
Query query = entityManager.createQuery("SELECT c" +
"FROM Company c LEFT JOIN c.companyProperties p WHERE c.id = :id");
query.setParameter("id", companyId);
List<Company> companies = query.getResultList();
The reason why only select a Company and not a property entity is that properties would appear as a collection inside the Company class. Assuming a one to many exists between companies and properties, you could access the propeties from each Company entity.
You are expecting to get a complete Company object when doing select only on particular fields, which is not possible. If you really want to save some memory (which in most cases would not be that much of a success) and select only some field, then you should expect a List<Object[]>:
List<Object[]> results = entityManager.createQuery("SELECT c.id, c.name, p " +
"FROM Company c LEFT JOIN c.companyProperties p where c.id = :id")
.setParameter("id", companyId)
.getResultList();
Here the results will contain a single array of the selected fields. You can use getSingleResult, but be aware that it will throw an exception if no results were found.

JPQL query that returns entities involving a many to many relationship

I need a JPQL query that returns:
All Tweets that I posted
All Tweets that are posted by users whom I follow (this is the many to many part)
I tried something like:
SELECT t
FROM Tweet t
WHERE t.author.id = :userId
OR t.author.id IN (
SELECT u.followedUsers
FROM User u
WHERE u.id = :userId
)
Only to find out that the subquery has wrong syntax. Also I cannot call the relation table User_User, like I would in SQL, because JPA doens't recognize it
User
#Entity
public class User {
#Id
#GeneratedValue
private long id
#ManyToMany(mappedBy = "followedUsers")
private Set<User> followers;
#ManyToMany
private Set<User> followedUsers;
}
Tweet
#Entity
public class Tweet {
#Id
#GeneratedValue
private long id;
#ManyToOne
private User author;
}
I have the habit of making JPQL queries more similar to SQL queries. So, my suggestion:
SELECT t FROM Tweet t
JOIN t.author a
WHERE a.id = :idUser
OR a.id IN (SELECT followedUser.id
FROM User u
JOIN u.followedUsers followedUser
WHERE u.id = :idUser)
given this query
OR t.author.id IN (
SELECT u.followedUsers
FROM User u
WHERE u.id = :userId
)
you are trying to find an Integer (t.author.id) in a list of User objects !
I think this should work :
SELECT t
FROM Tweet t
WHERE t.author.id = :userId
OR t.author MEMBER OF (
SELECT u.followedUsers
FROM User u
WHERE u.id = :userId
)
Edit
... MEMBER OF (subquery)... isn't allowed, but merging the subquery into its parent resolves this.
SELECT DISTINCT(t)
FROM Tweet t, User u
WHERE t.author.id = :userId
OR (t.author MEMBER OF u.followedUsers AND u.id = :userId)

NamedNativeQuery in Hibernate generates many select statements? How get referenced entities in a batch-way?

I thought I understood hibernate's fetching strategies, but it seems I was wrong.
So, I have an namedNativeQuery:
#NamedNativeQueries({
#NamedNativeQuery(
name = "getTest",
resultClass = ArticleOnDate.class,
query = "SELECT `a`.`id` AS `article_id`, `a`.`name` AS `name`, `b`.`price` AS `price` FROM article a LEFT JOIN price b ON (a.id = b.article_id) WHERE a.date <= :date"
)
})
#Entity()
#Immutable
public class ArtikelOnDate implements Serializable {
#Id
#OneToOne
#JoinColumn(name = "article_id")
private Article article;
...
}
Then I call it:
Query query = session.getNamedQuery("getTest").setDate("date", date);
List<ArticleOnDate> list = (List<ArticleOnDate>) query.list();
The query returns thousand of entities... Well, ok, but after that query hibernate queries thousand other queries:
Hibernate:
select
article0_.id as id1_0_0_,
article0_.bereich as name2_0_0_,
price1_.price as price1_14_1_
from
article artikel0_
where
artikel0_.id=?
Ok, that's logic, because the #OneToOne relation is fetched eagerly. I don't want to fetch it lazy, so I want a batch fetching strategy.
I tried to annotate the Article property but it didn't work:
#Id
#OneToOne
#JoinColumn(name = "article_id")
#BatchSize(size=100)
private Article article;
So what can I do to fetch the relation in a batch?

Hibernate does change the result set structure (puts Objects[]) into the result set when changing DESC to ASC on a many to many sorted column

I have the following two different HQL statements.
My Data Structure looks like this:
User
#Entity (name = "User")
public class User
{
#Id
#GeneratedValue
#Column (name = "id")
private int id;
#Column (name = "user_name")
private String username;
#Column (name = "password")
private String password;
#Column (name = "enabled")
private boolean enabled;
#ManyToMany (targetEntity = Role.class, cascade =
{
CascadeType.ALL
})
#JoinTable (name = "user_role", joinColumns =
{
#JoinColumn (name = "user_id")
}, inverseJoinColumns =
{
#JoinColumn (name = "role_id")
})
private Set<Role> roles;
/* getters and setters)
}
To cut it short the only difference between the two queries is that one is ASC the other is DESC
#NamedQuery (name = "user.getUsersOrderByRoleAsc",
query = "FROM User as u left outer join u.roles roles WHERE u.username like :username ORDER BY roles.name ASC"),
#NamedQuery (name = "user.getUsersOrderByRoleDesc",
query = "FROM User as u left outer join u.roles roles WHERE u.username like :username ORDER BY roles.name DESC"),
The query for ASC returns: A list of Users -> As I would expect.
The query of DESC returns: An List of Object[], and in each object the [0] is the User, while the [1] is just another null object.
That does not make any sense to me. How can simply changing ASC to DESC change the structure of the result set ?
I am using Hibernate 4.3.6.Final.
The fastest way to determin, what went wrong is to set the show_sql flag to true in you hibernate configuration file. This will log every rendered query.
See Hibernate show real SQL
Probably there is some Hibernate bug you bumped into, but because you are join fetching a one to many children collections, it's safer to use distinct as well:
#NamedQuery (name = "user.getUsersOrderByRoleAsc",
query = "select distinct u FROM User as u left outer join u.roles roles WHERE u.username like :username ORDER BY roles.name ASC"),
#NamedQuery (name = "user.getUsersOrderByRoleDesc",
query = "select distinct u FROM User as u left outer join u.roles roles WHERE u.username like :username ORDER BY roles.name DESC")

Categories

Resources