I have a query where i am joining two entities java (tables ) Command and User. I create sql hql query with left join and i get an org.hibernate.hql.ast.QuerySyntaxException: unexpected token error
I think that i have a problem with my left join syntax and also my second select.
My entities
#Entity
#Table(name = "Users")
public class User {
private int idUser;
private String login;
private String password;
private List<Command> commands = new ArrayList<>();
public User() { }
public User( String login, String password ) {
super();
this.setLogin( login );
this.setPassword( password );
}
#Id
#Column(name = "USER_ID", unique = true, nullable = false, scale = 0)
public int getIdUser() {
return idUser;
}
#Column(name = "USER_LOGIN", nullable = false)
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
#Column(name = "USER_PASSWORD", nullable = false)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "utilisateur")
public List<Command> getCommands() {
return commands;
}
public String toString() {
return this.idUser + ": " + this.login + "/" + this.password
;
}
}
#Entity
#Table(name="Commands")
public class Command {
private int idCommand;
private User utilisateur;
private Date commandDate;
public Command() {}
public Command( User user, Date commandDate ) {
this.setUser( user );
this.setCommandDate( commandDate );
}
#Id
#Column(name = "CMD_Id", unique = true, nullable = false, scale = 0)
public int getIdCommand() {
return idCommand;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "USER_ID")
public User getUtilisateur() {
return user;
}
public void setUtilisateur(User user) {
this.user = user;
}
#Column(name = "CMD_DATE")
#Temporal(TemporalType.TIMESTAMP)
public Date getCommandDate() {
return commandDate;
}
public void setCommandDate(Date commandDate) {
this.commandDate = commandDate;
}
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append( "Commande de >> " ).append( this.user )
.append( " - " ).append( this.commandDate ).append( "\n" );
return builder.toString();
}
}
And my hql query
select us.login,us.password
from User us left join Command cmd on us.idUser = cmd.utilisateur.idUser
AND us.idUser not in (SELECT utilisateur.idUser from Command )
ORDER BY us.idUser desc
I don't know what is the problem with my hql query. I Use the entity java in my hql query. AnyOne can i tell me how can i write correctly my hql query with a good syntax hql.
If you need the users login and password of users without commands the following might help
select us.login,us.password
from User us
where us.idUser not in (select c.user.idUser from Command c)
order by us.idUser desc
Related
I have two tables in my oracle database
my table data base
And two entities user and command
#Entity
#Table(name = "Users")
public class User {
private int idUser;
private String login;
private String password;
private List<Command> commands = new ArrayList<>();
public User() { }
public User( String login, String password ) {
super();
this.setLogin( login );
this.setPassword( password );
}
#Id
#Column(name = "USER_ID", unique = true, nullable = false, scale = 0)
public int getIdUser() {
return idUser;
}
#Column(name = "USER_LOGIN", nullable = false)
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
#Column(name = "USER_PASSWORD", nullable = false)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "utilisateur")
public List<Command> getCommands() {
return commands;
}
public String toString() {
return this.idUser + ": " + this.login + "/" + this.password
;
}
}
#Entity
#Table(name="Commands")
public class Command {
private int idCommand;
private User utilisateur;
private Date commandDate;
public Command() {}
public Command( User user, Date commandDate ) {
this.setUser( user );
this.setCommandDate( commandDate );
}
#Id
#Column(name = "CMD_Id", unique = true, nullable = false, scale = 0)
public int getIdCommand() {
return idCommand;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "USER_ID")
public User getUtilisateur() {
return user;
}
public void setUtilisateur(User user) {
this.user = user;
}
#Column(name = "CMD_DATE")
#Temporal(TemporalType.TIMESTAMP)
public Date getCommandDate() {
return commandDate;
}
public void setCommandDate(Date commandDate) {
this.commandDate = commandDate;
}
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append( "Commande de >> " ).append( this.user )
.append( " - " ).append( this.commandDate ).append( "\n" );
return builder.toString();
}
}
and my service java class
#Service
#Transactional
public class MyServiceImpl implements MyService {
private static final long serialVersionUID = 8393594103219622298L;
private static final String MY_ADVANCED_REQUEST
="select us.USER_ID, cd.CMD_DATE, us.USER_PASSWORD " +
"from Users us , Commands cd ON us.USER_ID = cd.USER_ID " +
"where us.USER_ID = cd.USER_ID " +
"AND us.USER_ID = 5";
public List<Object> findUsersCmd() {
String query1 = MY_ADVANCED_REQUEST;
SQLQuery queryObj = sessionFactory.getCurrentSession().createSQLQuery(query1);
List<Object> lstObj =queryObj.list();
return lstObj;
}
}
I get an empty list in this line List lstObj =queryObj.list(); .
I use a request SQLQuery native not jpa.
I want to get 1 object for us.USER_ID = 5(even if I don't have an us.USER_ID = 5 in table commands), 3 objects for us.USER_ID=1 (because 3 rows in Commands table); 1 object for us.USER_ID=6 (because 1 rows in Commands table)
How can i solve my problem to return a valeur for my us.USER_ID = 5 ?
Left outer join returns all the rows from first table and only matched rows from second table.(though the user_id=5 is not present in commands table it can be retrieved)
private static final String MY_ADVANCED_REQUEST
="select us.USER_ID, cd.CMD_DATE, us.USER_PASSWORD " +
"from Users us LEFT JOIN Commands cd ON us.USER_ID = cd.USER_ID ";
I am using a native query in JPA repository inside #Query annotation
#Query(value = " select * from message where id in(select if(coalesce(a.maxId,0)>coalesce(b.maxId,0), a.maxId, b.maxId) maxId from (select from_number, to_number, " +
" max(id) maxId,direction,type from message where direction='INCOMING' group by from_number, to_number having sum(type= :type)) as a " +
" left join ( select from_number, to_number, max(id) maxId, direction, type from message where direction = 'OUTGOING' and schedule_id is null " +
" group by from_number, to_number) as b on a.from_number=b.to_number and a.to_number=b.from_number) order by generated_time desc ", nativeQuery = true)
Page<Message> getLatestMessageFromThread(#Param("type") String type, Pageable page);
The problem is when i execute this query i am getting syntax error. When i checked the logs it is showing that the query to take count of total messages in query is like below
select count(where) from message where id in(select if(coalesce(a.maxId,0)>coalesce(b.maxId,0),
a.maxId, b.maxId) maxId from (select from_number, to_number, max(id) maxId,direction,type from
message where direction='INCOMING' group by from_number, to_number having sum(type= 'REVIEW'))
as a left join ( select from_number, to_number, max(id) maxId, direction, type from message
where direction = 'OUTGOING' and schedule_id is null group by from_number, to_number) as b
on a.from_number=b.to_number and a.to_number=b.from_number)
The count(*) is replaced by count(where)
The entity class for message table is here.
#Entity
#Table(name = "message")
public class Message implements Comparable<Message> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "to_number")
private String toNumber;
#Column(name = "from_number")
private String fromNumber;
#Column(name = "message")
private String message;
#Enumerated(EnumType.STRING)
#Column(name = "direction")
private MessageDirection direction;
#Enumerated(EnumType.STRING)
#Column(name = "status")
private MessageStatus status;
#Enumerated(EnumType.STRING)
#Column(name = "type")
private KeywordType type;
#Column(name = "keyword_matched")
private String keywordMatched;
#Column(name = "generated_time")
private Timestamp generatedTime;
#Column(name = "scheduled_time")
private Timestamp scheduledTime;
#Column(name = "details")
private String details;
#Enumerated(EnumType.STRING)
#Column(name = "read_status")
private MessageReadStatus readStatus;
#Column(name = "delivery_code")
private String deliveryCode;
#Column(name = "delivery_description")
private String deliveryDescription;
#Column(name = "data", columnDefinition = "json")
private String messageData;
#ManyToOne
#JoinColumn(name = "schedule_id")
private Schedule schedule;
public Message() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getToNumber() {
return toNumber;
}
public void setToNumber(String toNumber) {
this.toNumber = toNumber;
}
public String getFromNumber() {
return fromNumber;
}
public void setFromNumber(String fromNumber) {
this.fromNumber = fromNumber;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public MessageDirection getDirection() {
return direction;
}
public void setDirection(MessageDirection direction) {
this.direction = direction;
}
public MessageStatus getStatus() {
return status;
}
public void setStatus(MessageStatus status) {
this.status = status;
}
public KeywordType getType() {
return type;
}
public void setType(KeywordType type) {
this.type = type;
}
public Timestamp getGeneratedTime() {
return generatedTime;
}
public void setGeneratedTime(Timestamp generatedTime) {
this.generatedTime = generatedTime;
}
public Timestamp getScheduledTime() {
return scheduledTime;
}
public void setScheduledTime(Timestamp scheduledTime) {
this.scheduledTime = scheduledTime;
}
public Schedule getSchedule() {
return schedule;
}
public void setSchedule(Schedule schedule) {
this.schedule = schedule;
}
public String getKeywordMatched() {
return keywordMatched;
}
public void setKeywordMatched(String keywordMatched) {
this.keywordMatched = keywordMatched;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
public MessageReadStatus getReadStatus() {
return readStatus;
}
public void setReadStatus(MessageReadStatus readStatus) {
this.readStatus = readStatus;
}
public String getDeliveryCode() {
return deliveryCode;
}
public void setDeliveryCode(String deliveryCode) {
this.deliveryCode = deliveryCode;
}
public String getMessageData() {
return messageData;
}
public void setMessageData(String messageData) {
this.messageData = messageData;
}
public String getDeliveryDescription() {
return deliveryDescription;
}
public void setDeliveryDescription(String deliveryDescription) {
this.deliveryDescription = deliveryDescription;
}
}
Why it is getting replaced. How can i write the query in correct way ?
The solution is to use countQuery annotation parameter :
public interface Repository extends JpaRepository<Entity, Long> {
String QUERY = "FROM entity " +
" WHERE name ILIKE :name ";
#Query(
value = "SELECT * " + QUERY,
countQuery = "SELECT count(*) " + QUERY,
nativeQuery = true
)
Page<KDeploymentView> findBy(
#Param("name") String name,
Pageable p
);
I am working on a web application using:
Spring Boot
PostgreSQL
JPA and Hibernate.
I have a table called role and another page. Among them there is a many-to-many table with ID's. What I'm trying to get is the list of pages that correspond to a role, so I'm trying to retrieve the list of pages from the id of the role and executing a query to bring the list. The problem is that I have an error in the ResultSet because it tells me that it does not find a column with the name page_id.
Example:
I have executed the query separately and this brings me results correctly.
select p.url from role_page rp, page p where rp.role_id = 6 and rp.page_id = p.page_id;
Output:
Output of the query
Then I made a call to the function to get the list and check it to make sure I get results.
public void rolesAndPages(){
List<Page> pages = pageRepository.findPagePerRole(6);
for (Page page: pages
) {
System.out.println(page.getUrl());
}
}
And throws the error described above:
org.postgresql.util.PSQLException: The column name page_id was not found in this ResultSet.
My Repository:
#Repository("pageRepository")
public interface PageRepository extends JpaRepository<Page, Long> {
#Query(value = "select p.url from role_page rp, page p where rp.role_id = ?1 and rp.page_id = p.page_id", nativeQuery = true)
List<Page> findPagePerRole(Integer id);
}
Role.java
#Entity
#Table(name = "app_role")
public class Role {
#Id
#SequenceGenerator(name="pk_sequence",sequenceName="messagesounds_id_seq", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="pk_sequence")
#Column(name="role_id")
private int id;
#Column(name="authority")
#NotEmpty(message = "*Please provide a name")
private String authority;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "role_page", joinColumns = #JoinColumn(name = "role_id"), inverseJoinColumns = #JoinColumn(name = "page_id"))
private Set<Page> pages;
public void setPages(Set<Page> pages) {
this.pages = pages;
}
public Set<Page> getPages() {
return pages;
}
public Role() {}
public Role(String authority) {
this.authority = authority;
}
public Role(String authority, Set<Page> pages){
this.authority = authority;
this.pages = pages;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
}
Page.java
#Entity
#Table(name = "page")
public class Page {
#Id
#SequenceGenerator(name = "pk_sequence", sequenceName = "messagesounds_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "pk_sequence")
#Column(name = "page_id")
private int id;
#Column(name = "name_page")
#NotEmpty(message = "*Please provide a name")
private String name;
#Column(name = "url")
#NotEmpty(message = "*Please provide an url")
private String url;
#Column(name = "description")
#NotEmpty(message = "*Please provide a description")
private String description;
#ManyToMany(mappedBy = "pages")
private Set<Role> roles;
public Page() {
}
public Page(String name_page) {
this.name = name_page;
}
public Page(String name_page, Set<Role> roles) {
this.name = name_page;
this.roles = roles;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
The error is quite explicite, you need to select page_id column to make it work, not only p.url.
Try this to retrieve every column of Page :
#Query(value = "select p.* from role_page rp, page p where rp.role_id = ?1 and rp.page_id = p.page_id", nativeQuery = true)
Or, this to retrieve every columns of tables Page and Role_Page :
#Query(value = "from role_page rp, page p where rp.role_id = ?1 and rp.page_id = p.page_id", nativeQuery = true)
The both queries should work.
Did you tried :
#Query(value = "select p.url from role_page rp, page p where rp.id = ?1 and rp.id = p.id")
I have the following 2 entities:
#Entity
public class User implements java.io.Serializable {
private Integer iduser;
private String email;
private String password;
private Byte enabled;
private Set<Token> tokens = new HashSet<>(0);
public User() {
}
public User(String email, String password, Byte enabled/*, Set groupRights*/, Set tokens) {
this.email = email;
this.password = password;
this.enabled = enabled;
this.tokens = tokens;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "iduser", unique = true, nullable = false)
public Integer getIduser() {
return this.iduser;
}
public void setIduser(Integer iduser) {
this.iduser = iduser;
}
#Column(name = "email", unique = true, length = 45)
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
#Column(name = "password", length = 60)
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
#Column(name = "enabled")
public Byte getEnabled() {
return this.enabled;
}
public void setEnabled(Byte enabled) {
this.enabled = enabled;
}
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
public Set<Token> getTokens() {
return this.tokens;
}
public void setTokens(Set<Token> tokens) {
this.tokens = tokens;
}
}
#Entity
public class Token implements java.io.Serializable {
private String idtoken;
private User user;
private Date tokenTtl;
private String ipLock;
public Token() {
}
public Token(String idtoken) {
this.idtoken = idtoken;
}
public Token(String idtoken, User user, Date tokenTtl, String ipLock) {
this.idtoken = idtoken;
this.user = user;
this.tokenTtl = tokenTtl;
this.ipLock = ipLock;
}
#Id
#Column(name = "idtoken", unique = true, nullable = false, length = 36)
public String getIdtoken() {
return this.idtoken;
}
public void setIdtoken(String idtoken) {
this.idtoken = idtoken;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user = user;
}
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "token_ttl", length = 19)
public Date getTokenTtl() {
return this.tokenTtl;
}
public void setTokenTtl(Date tokenTtl) {
this.tokenTtl = tokenTtl;
}
#Column(name = "ip_lock", length = 45)
public String getIpLock() {
return this.ipLock;
}
public void setIpLock(String ipLock) {
this.ipLock = ipLock;
}
}
the problem is that when I select a user using this JPA-QL: select u from User u, I get a null Set related to Tokens, even though there are associated tokens for this user.
This problem arises only when I get the JPA context (EntityManager) in a Spring 4 context. If I'm doing a test, creating the EntityManager directly (using this: Persistence.createEntityManagerFactory("unit-name");), this issue is not present.
Can anybody tell me what's the cause of my issue?
You need to use in this way
private Set<Token> tokens = new HashSet<Token>(0);
Create the Getter Setter accordingley
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
public Set<Token> getTokens() {
return this.tokens;
}
public void setTokens(Set<Token>tokens) {
this.tokens = tokens;
}
The problem is related to a bug from OpenJPA.
When in OpenJPA you create a bidirectional #OneToMany relationship with FetchType.EAGER fetch type, the bug comes up by not being able to create this relation. The bug is fixed in some versions. Seems to affect versions 2.1.0, 2.2.2, 2.3.0 and 2.4.0.
References:
https://issues.apache.org/jira/browse/OPENJPA-2505
Spring Data JPA OneToMany and ManyToOne give me JpaSystemException
I am using Eclipselink v2.3.3
I am getting this error
Error compiling the query [Credential.updateExistingCredentialNoPW: UPDATE Credential c SET c.active = :active, c.employee.address.streetAddress = :stadd, c.employee.address.city = :city, c.employee.address.province = :prov, c.employee.address.zip = :zip WHERE c.id = :id], line 1, column 46: invalid navigation expression [c.employee], cannot navigate association field [employee] in the SET clause target.
when I try to run my code.
Here are the affected named queries that prevents running my code
#NamedQuery(name = "Credential.updateExistingCredentialNoPW",
query = "UPDATE Credential c "
+ "SET c.active = :active, "
+ "c.employee.address.streetAddress = :stadd, "
+ "c.employee.address.city = :city, "
+ "c.employee.address.province = :prov, "
+ "c.employee.address.zip = :zip "
+ "WHERE c.id = :id"),
#NamedQuery(name = "Credential.updateExistingCredential",
query = "UPDATE Credential c "
+ "SET c.password = :pw, "
+ "c.active = :active, "
+ "c.employee.address.streetAddress = :stadd, "
+ "c.employee.address.city = :city, "
+ "c.employee.address.province = :prov, "
+ "c.employee.address.zip = :zip "
+ "WHERE c.id = :id"),
#NamedQuery(name = "Credential.updateSalary",
query = "UPDATE Credential c "
+ "SET c.employee.salary.value = :val WHERE c.id = :id")
Here is the class where I put all the named queries specified above
public class Credential implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "CRED_GEN", strategy = GenerationType.IDENTITY)
#Column(name = "CREDENTIAL_ID")
private long id;
// unique & not nullabe username
#Column(nullable = false, unique = true)
private String username;
#Column(nullable = false, name = "USER_KEY")
private String password;
#Column(nullable = false)
private boolean active;
#Column(nullable = false)
private int userLevel;
#OneToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private Employee employee;
public Credential() {
}
public Credential(String username, String password, boolean active,
int userLevel, Employee employee) throws NoSuchAlgorithmException {
this.username = username;
this.setPassword(password);
this.active = active;
this.userLevel = userLevel;
this.employee = employee;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public int getUserLevel() {
return userLevel;
}
public void setUserLevel(int userLevel) {
this.userLevel = userLevel;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public String getPassword() {
return password;
}
public void setPassword(String password) throws NoSuchAlgorithmException {
this.password = Cryptography.getHash(password);
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
Am I doing it wrong?
It is not possible to update multiple tables with single sql query.
See https://forums.oracle.com/forums/thread.jspa?threadID=2223393.
So it's also not possible with Eclipselink.
You'll have to split this it two queries, or better use setters.
You can't. Either use a SQL query, or use Java code:
Credential c = em.find(Credential.class, credentialId);
c.setActive(active);
c.getEmployee().getAddress().setStreet(street);
...