I have the following problem. I want to execute this query in my spring boot project. I tried to do this with the query annotation in the JPA repository interface. But it says "unexpected SELECT" at the inner join. When I execute this query directly on my mySQL database, it will work.
Do anyone have a solution for this case?
This is my query:
SELECT t1.*
FROM az_manager t1
INNER JOIN
(
SELECT maID, MAX(datum) AS max_date
FROM az_manager
WHERE maID IN (7243, 1)
GROUP BY maID
) t2
ON t1.maID = t2.maID AND t1.datum = t2.max_date
WHERE
t1.maID IN (7243, 1);
This is my class:
#Entity
#Table(name = "az_manager")
#IdClass(TnsWorkingHoursManagerId.class)
#Getter
#Setter
public class TnsWorkingHoursManager extends TnsObject{
#Id
#Column(name = "datum")
private long date;
#Id
#Column(name = "maid")
private int employeeId;
#Column(name = "typid")
private int typeId;
#Column(name = "bemerkung")
private String comment;
#Column(name = "host")
private String host;
#Column(name = "modus")
private byte mode;
public TnsWorkingHoursManager() {
}
}
Here is my try with the JPA repository:
#Query(value = "SELECT azm1 FROM az_manager azm1 INNER JOIN (SELECT maID, MAX(datum) AS max_date FROM az_manager WHERE maID IN(:userIds) GROUP BY maID) azm2 ON azm1.maID = azm2.maID AND azm1.datum = azm2.max_date WHERE azm1.maID IN (:userIds)")
List<TnsWorkingHoursManager> getLastEntries(#Param("userIds") ArrayList<Integer> userIds);
At the second select it says "'SELECT' unexpected"
For anyone else that might stumble upon this question:
If you don't add the nativeQuery = true parameter to the #Query annotation in a Spring Repository, the query will be considered as written in JPQL.
From the JPQL docs:
Subqueries may be used in the WHERE or HAVING clause.
Based on the quote above, the JPQL (Java Persistence Query Language) does not support subqueries in the FROM clause and that is why OP had to make the query native in order for it to work.
I have found a solution.
I forgot to add ", nativeQuery = true" at the end of the line, but in the bracket. Now it works.
Related
I've been trying to get a query inside a join table for a many to many relation working. The query was meant to count how many users follow a specific game. The entity itself is very simple, looks like this:
#Entity
#Table(name = "followed_users_games", uniqueConstraints = {
#UniqueConstraint(columnNames = "followed_id")
})
public class FollowedEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "followed_id", unique = true, nullable = false)
private Integer followedId;
#ManyToOne
#JoinColumn(name = "game_id")
private GameEntity games;
#ManyToOne
#JoinColumn(name = "user_id")
private UserEntity users;
#Column(name = "notify")
#NonNull private Boolean notify;
}
And the query I've been trying to get running looks like so
#Query("select f.gameId, count(f) as usercount from FollowedEntity f group by f.games.gameId order by usercount desc")
List<GameEntity> findMostFollowed(Pageable pageable);
I have tested the query on my database itself, and it seems to be working fine. However my application returns an error as such:
org.postgresql.util.PSQLException: ERROR: column "gameentity1_.game_id" must appear in the GROUP BY clause or be used in an aggregate function
Any help would be appreciated.
It looks like you have to use join in your query like
#Query(value = "SELECT g.gameId, COUNT(g) as usercount FROM FollowedEntity f JOIN f.games g GROUP By g.gameId ORDER BY usercount DESC")
List<GameEntity> findMostFollowed(Pageable pageable);
You try to map a pair (game_id, count) to the whole GameEntity that has a different structure, that's why sql query that is generated is not what you expect.
What can probably help you is mapping your query result to DTO.
Every time when you have an issue like this I would recommend to have a look at SQL query that JPA is generating. See, e.g., https://www.baeldung.com/sql-logging-spring-boot
My application under Spring Boot v1.5.7
I have 3 entities (schematically):
#Entity
public class Word {
#Id
#GeneratedValue
private Integer id
...
}
#Entity
public class UserWordList {
#Id
#GeneratedValue
private Integer id
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
#ManyToOne
#JoinColumn(name = "word_id")
private Word word;
}
#Entity
public class UserAnotherWordList {
#Id
#GeneratedValue
private Integer id
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
#ManyToOne
#JoinColumn(name = "word_id")
private Word word;
}
And now I need to select all Words for User, but exclude Words placed in user's lists
Native SQL for user_id=1 is
select *
from Word w
left join UserWordList uwl
on w.id = uwl.word_id and uwl.user_id = 1
left join UserAnotherWordList uawl
on w.id = uawl.word_id and uawl.user_id = 1
where uwl.word_id is NULL
and uawl.word_id is NULL
What is a best way to do it? Ideally I would like to use Spring Data features or HQL, but I don't understand how...
UPD
I solve my problem with native query:
#Entity
#NamedNativeQuery(
name = "User.getWordsToProcess",
resultClass = Word.class,
query = "<...native query to select Words...>"
)
public class User {...}
...
public interface UserRepository extends CrudRepository<User, Integer> {
List<Word> getWordsToProcess(Integer userId);
}
Fastest answer is Criteria api (but that is deprecated in hibernate 5.2 and above.)
So you can use Hql :
getSession().createQuery(" select * from UserWordList u left join fetch u.word
left join fetch u.user").list()
And you can use union or create another query to fetch UserAnotherWordList.
Also you can set any restrictions in Hql like below:
Query query = getSession().createQuery(" select * from UserWordList u left join fetch u.word left join fetch u.user us where us.user = :sample").list();
query.setParameter("sample",value);
query.list();
I have following model:
#Entity
#Table(name = "SAMPLE_TABLE")
#Audited
public class SampleModel implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#Column(name = "NAME", nullable = false)
#NotEmpty
private String name;
#Column(name = "SHORT_NAME", nullable = true)
private String shortName;
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "MENTOR_ID")
private User mentor;
//other fields here
//omitted getters/setters
}
Now I would like to query only columns: id, name, shortName and mentor which referes to User entity (not complete entity, because it has many other properties and I would like to have best performance).
When I write query:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<SampleModel> query = builder.createQuery(SampleModel.class);
Root<SampleModel> root = query.from(SampleModel.class);
query.select(root).distinct(true);
root.fetch(SampleModel_.mentor, JoinType.LEFT);
query.multiselect(root.get(SampleModel_.id), root.get(SampleModel_.name), root.get(SampleModel_.shortName), root.get(SampleModel_.mentor));
query.orderBy(builder.asc(root.get(SampleModel_.name)));
TypedQuery<SampleModel> allQuery = em.createQuery(query);
return allQuery.getResultList();
I have following exception:
Caused by: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=generatedAlias1,role=com.sample.SampleModel.model.SampleModel.mentor,tableName=USER_,tableAlias=user1_,origin=SampleModel SampleModel0_,columns={SampleModel0_.MENTOR_ID ,className=com.sample.credential.model.User}}]
at org.hibernate.hql.internal.ast.tree.SelectClause.initializeExplicitSelectClause(SelectClause.java:214)
at org.hibernate.hql.internal.ast.HqlSqlWalker.useSelectClause(HqlSqlWalker.java:991)
at org.hibernate.hql.internal.ast.HqlSqlWalker.processQuery(HqlSqlWalker.java:759)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:675)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:311)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:259)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:262)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:190)
... 138 more
Query before exception:
SELECT DISTINCT NEW com.sample.SampleModel.model.SampleModel(generatedAlias0.id, generatedAlias0.name, generatedAlias0.shortName, generatedAlias0.mentor)
FROM com.sample.SampleModel.model.SampleModel AS generatedAlias0
LEFT JOIN FETCH generatedAlias0.mentor AS generatedAlias1
ORDER BY generatedAlias0.name ASC
I know that I can replace fetch with join but then I will have N+1 problem. Also I do not have back reference from User to SampleModel and I do not want to have..
I ran into this same issue, and found that I was able to work around it by using:
CriteriaQuery<Tuple> crit = builder.createTupleQuery();
instead of
CriteriaQuery<X> crit = builder.createQuery(X.class);
A little extra work has to be done to produce the end result, e.g. in your case:
return allQuery.getResultList().stream()
map(tuple -> {
return new SampleModel(tuple.get(0, ...), ...));
})
.collect(toList());
It's been a long time since the question was asked. But I wish some other guys would benefit from my solution:
The trick is to use subquery.
Let's assume you have Applicant in your Application entity (one-to-one):
#Entity
public class Application {
private long id;
private Date date;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "some_id")
private Applicant applicant;
// Other fields
public Application() {}
public Application(long id, Date date, Applicant applicant) {
// Setters
}
}
//...............
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Application> cbQuery = cb.createQuery(Application.class);
Root<Application> root = cbQuery.from(Application.class);
Subquery<Applicant> subquery = cbQuery.subquery(Applicant.class);
Root subRoot = subquery.from(Applicant.class);
subquery.select(subRoot).where(cb.equal(root.get("applicant"), subRoot));
cbQuery.multiselect(root.get("id"), root.get("date"), subquery.getSelection());
This code will generate a select statement for Application, and select statements for Applicant per each Application.
Note that you have to define an appropriate constructor corresponding to your multiselect.
I got the same problem using EclipseLink as the JPA provider : I just wanted to return the id of a mapped entity («User» in Gazeciarz's example).
This can be achieved quite simply by replacing (in the query.multiselect clause)
root.get(SampleModel_.mentor)
with something like
root.get(SampleModel_.mentor).get(User_.id)
Then, instead of returning all the fields of User, the request will only return the its id.
I also used a tuple query but, in my case, it was because my query was returning fileds from more than one entity.
I have an Entity shown as below, there are two tables mapped by annotation #SecondaryTable. The PrimaryKey's names of two tables are both 'resource_id'. When I execute HQL
delete AssetProjectProposalInfoEntity e where e.resourceId=:rid
Column ambiguously defined error occured.
import ...
#Entity
#Table(name = "ASSET_PROJECT_INFO", schema = "CORE_WAC")
#SecondaryTable(name="ASSET_PROJECT_PROPOSAL_INFO", schema="CORE_WAC")
public class AssetProjectProposalInfoEntity
{
#Id
#Column(name = "RESOURCE_ID")
#GeneratedValue(generator="UUIdentifier")
#GenericGenerator(name="UUIdentifier", strategy = "uuid")
private String resourceId;
#Column(name = "PROJECT_NAME")
private String projectName;
#Column(table="ASSET_PROJECT_PROPOSAL_INFO", name="PROJECT_TYPE")
private String projectType;
#Column(table="ASSET_PROJECT_PROPOSAL_INFO", name="APPLY_UNIT_ID")
private String applyUnitId;
...
}
The console log as below.It shows that the where clause in the SQL is RESOURCE_ID=? , but it should be assetproje0_.RESOURCE_ID=? I think. Can any body help? Thanks:)
Hibernate: insert into HT_ASSET_PROJECT_INFO select assetproje0_.RESOURCE_ID as
RESOURCE_ID from CORE_WAC.ASSET_PROJECT_INFO assetproje0_ left outer join
CORE_WAC.ASSET_PROJECT_PROPOSAL_INFO assetproje0_1_ on
assetproje0_.RESOURCE_ID=assetproje0_1_.RESOURCE_ID where RESOURCE_ID=?
Hibernate: delete from HT_ASSET_PROJECT_INFO
Use two natives queries
String deleteTable2 =
"DELETE CORE_WAC.ASSET_PROJECT_PROPOSAL_INFO act where RESOURCE_ID IN (:resourceIdsList)";
Query qDeleteTable2 = getEntityManager().createNativeQuery(deleteTable2);
qDeleteTable2.setParameter("resourceIdsList", resourceIdsList);
qDeleteTable2.executeUpdate();
String deleteTable1 =
"DELETE CORE_WAC.ASSET_PROJECT_INFO act where RESOURCE_ID IN (:resourceIdsList)";
Query qDeleteTable1 = getEntityManager().createNativeQuery(deleteTable1);
qDeleteTable1.setParameter("resourceIdsList", resourceIdsList);
qDeleteTable1.executeUpdate();
In my WAS application, I have a requirement to define a variable(String) in an Entity class that maps to a table.
So the fields that are related to the table have annotation as #Column(name="column_name")
I have a requirement to add a new field/variable to this Entity class that is not a column in table. If I declare it as a normal field, JPA converts this field also in the SQLs. This is causing SQL -206 error(as expected).
How to do I declare this field? Is there an annotation that I can use to say its a custom variable and not related to any of the columns in the table defined by this Entity?
example:
#Entity
#Table(name = "TABLE_1")
#NamedQueries(
{
#NamedQuery(name = "abc:findTableContent", query = "SELECT u FROM TABLE_1 u WHERE u.Id = :iD"),
})
public class TableEntity1 implements Serializable
{
#Id
#Column(name = "TABLE1_ID")
private Long iD;
#Column(name = "DESC")
private String desc;
private String error;
}
So if I run the namedquery, it gets executed as "SELECT t0.iD, t0.desc, t0.error FROM TABLE_1 u WHERE u.iD=?"
How do I solve this? Thanks for your help!
I found the answer. I could mark the field or variable as #javax.persistence.Transient